diff --git a/app/Http/Controllers/CategoryController.php b/app/Http/Controllers/CategoryController.php index 696145827b..1a5895b2a3 100644 --- a/app/Http/Controllers/CategoryController.php +++ b/app/Http/Controllers/CategoryController.php @@ -162,7 +162,6 @@ class CategoryController extends Controller // list of ranges for list of periods: // oldest transaction in category: - //$start = $repository->getFirstActivityDate($category); $start = $repository->firstUseDate($category, new Collection); $range = Preferences::get('viewRange', '1M')->data; $start = Navigation::startOfPeriod($start, $range); diff --git a/app/Http/Controllers/Chart/BudgetController.php b/app/Http/Controllers/Chart/BudgetController.php index 162ca52394..ce5132628b 100644 --- a/app/Http/Controllers/Chart/BudgetController.php +++ b/app/Http/Controllers/Chart/BudgetController.php @@ -150,7 +150,7 @@ class BudgetController extends Controller $cache->addProperty('budget'); $cache->addProperty('all'); if ($cache->has()) { - return Response::json($cache->get()); + //return Response::json($cache->get()); } $budgets = $repository->getActiveBudgets(); $repetitions = $repository->getAllBudgetLimitRepetitions($start, $end); @@ -203,6 +203,8 @@ class BudgetController extends Controller $data = $this->generator->frontpage($allEntries); $cache->store($data); + return ' ' . json_encode($data); + return Response::json($data); } diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index 15bd8c0405..5e432520fe 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -9,7 +9,6 @@ use FireflyIII\Generator\Chart\Category\CategoryChartGeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface as CRI; -use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\CacheProperties; use Illuminate\Support\Collection; use Log; @@ -148,10 +147,10 @@ class CategoryController extends Controller } /** - * @param Carbon $start - * @param Carbon $end - * @param Collection $accounts - * @param Collection $categories + * @param Carbon $start + * @param Carbon $end + * @param Collection $accounts + * @param Collection $categories * * @return \Illuminate\Http\JsonResponse */ @@ -241,8 +240,8 @@ class CategoryController extends Controller // return Response::json($cache->get()); } - /** @var CategoryRepositoryInterface $repository */ - $repository = app(CategoryRepositoryInterface::class); + /** @var CRI $repository */ + $repository = app(CRI::class); $categoryCollection = new Collection([$category]); // loop over period, add by users range: $current = clone $start; diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 9e17c3c244..1d7997e4ba 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -15,7 +15,6 @@ use FireflyIII\User; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Query\JoinClause; use Illuminate\Support\Collection; -use Log; /** * Class BudgetRepository @@ -78,7 +77,7 @@ class BudgetRepository implements BudgetRepositoryInterface { $oldest = Carbon::create()->startOfYear(); $journal = $budget->transactionjournals()->orderBy('date', 'ASC')->first(); - if ($journal) { + if (!is_null($journal)) { $oldest = $journal->date < $oldest ? $journal->date : $oldest; } @@ -86,8 +85,9 @@ class BudgetRepository implements BudgetRepositoryInterface ->transactions() ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.id') ->orderBy('transaction_journals.date', 'ASC')->first(['transactions.*', 'transaction_journals.date']); - if ($transaction) { - $oldest = $transaction->date < $oldest ? $transaction->date : $oldest; + if (!is_null($transaction)) { + $carbon = new Carbon($transaction->date); + $oldest = $carbon < $oldest ? $carbon : $oldest; } return $oldest; @@ -187,8 +187,8 @@ class BudgetRepository implements BudgetRepositoryInterface // first get all journals for all budget(s): $journalQuery = $this->user->transactionjournals() ->expanded() - ->before($end) ->sortCorrectly() + ->before($end) ->after($start) ->leftJoin( 'transactions as source', @@ -204,6 +204,7 @@ class BudgetRepository implements BudgetRepositoryInterface } // get them: $journals = $journalQuery->get(TransactionJournal::queryFields()); + //Log::debug('journalsInPeriod journal count is ' . $journals->count()); // then get transactions themselves. @@ -302,15 +303,80 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function spentInPeriod(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) : string { - $set = $this->journalsInPeriod($budgets, $accounts, $start, $end); - //Log::debug('spentInPeriod set count is ' . $set->count()); - $sum = '0'; - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $sum = bcadd($sum, TransactionJournal::amount($journal)); + // first collect actual transaction journals (fairly easy) + $query = $this->user + ->transactionjournals() + ->distinct() + ->leftJoin( + 'transactions as t', function (JoinClause $join) { + $join->on('t.transaction_journal_id', '=', 'transaction_journals.id')->where('amount', '<', 0); + } + ); + + if ($end >= $start) { + $query->before($end)->after($start); + } + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $query->whereIn('t.account_id', $accountIds); + } + if ($budgets->count() > 0) { + $budgetIds = $budgets->pluck('id')->toArray(); + $query->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $query->whereIn('budget_transaction_journal.budget_id', $budgetIds); } - Log::debug('spentInPeriod between ' . $start->format('Y-m-d') . ' and ' . $end->format('Y-m-d') . ' is ' . $sum); + // that should do it: + $first = strval($query->sum('t.amount')); + + // then collection transactions (harder) + $query = $this->user->transactions() + ->where('transactions.amount', '<', 0) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')); + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $query->whereIn('transactions.account_id', $accountIds); + } + if ($budgets->count() > 0) { + $budgetIds = $budgets->pluck('id')->toArray(); + $query->leftJoin('budget_transaction', 'budget_transaction.transaction_id', '=', 'transactions.id'); + $query->whereIn('budget_transaction.budget_id', $budgetIds); + } + $second = strval($query->sum('transactions.amount')); + + return bcadd($first, $second); + } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string + { + $query = $this->user->transactionjournals() + ->distinct() + ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin( + 'transactions as t', function (JoinClause $join) { + $join->on('t.transaction_journal_id', '=', 'transaction_journals.id')->where('amount', '<', 0); + } + ) + ->leftJoin('budget_transaction', 't.id', '=', 'budget_transaction.transaction_id') + ->whereNull('budget_transaction_journal.id') + ->whereNull('budget_transaction.id') + ->before($end) + ->after($start); + + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + + $query->whereIn('t.account_id', $accountIds); + } + $sum = strval($query->sum('t.amount')); return $sum; } @@ -402,25 +468,4 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } - /** - * @param Collection $accounts - * @param Carbon $start - * @param Carbon $end - * - * @return string - */ - public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end): string - { - $set = $this->journalsInPeriodWithoutBudget($accounts, $start, $end); - //Log::debug('spentInPeriod set count is ' . $set->count()); - $sum = '0'; - /** @var TransactionJournal $journal */ - foreach ($set as $journal) { - $sum = bcadd($sum, TransactionJournal::amount($journal)); - } - - Log::debug('spentInPeriodWithoutBudget between ' . $start->format('Y-m-d') . ' and ' . $end->format('Y-m-d') . ' is ' . $sum); - - return $sum; - } } diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 7c1eff2d91..c02a8ad1a1 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -8,8 +8,10 @@ use FireflyIII\Models\Category; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\User; +use Illuminate\Database\Query\JoinClause; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; +use Log; /** * Class CategoryRepository @@ -53,12 +55,24 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { - $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $journals = $this->journalsInPeriod($categories, $accounts, $types, $start, $end); - $sum = '0'; - foreach ($journals as $journal) { - $sum = bcadd(TransactionJournal::amount($journal), $sum); - } + $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; + $sum = bcmul($this->sumInPeriod($categories, $accounts, $types, $start, $end), '-1'); + + return $sum; + + } + + /** + * @param Collection $accounts + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) :string + { + $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; + $sum = $this->sumInPeriodWithoutCategory($accounts, $types, $start, $end); return $sum; } @@ -80,7 +94,6 @@ class CategoryRepository implements CategoryRepositoryInterface return $category; } - /** * @param Category $category * @param Collection $accounts @@ -118,10 +131,13 @@ class CategoryRepository implements CategoryRepositoryInterface $firstTransactionQuery->whereIn('transactions.account_id', $ids); } - $firstTransaction = $firstJournalQuery->first(['transaction_journals.*']); + $firstTransaction = $firstTransactionQuery->first(['transaction_journals.*']); - if (!is_null($firstTransaction) && !is_null($first) && $firstTransaction->date < $first) { - $first = $firstTransaction->date; + if (!is_null($firstTransaction) && ((!is_null($first) && $firstTransaction->date < $first) || is_null($first))) { + $first = new Carbon($firstTransaction->date); + } + if (is_null($first)) { + return new Carbon('1900-01-01'); } return $first; @@ -162,25 +178,30 @@ class CategoryRepository implements CategoryRepositoryInterface $first = $query->get(TransactionJournal::queryFields()); // then collection transactions (harder) - $query = $this->user->transactions(); - $query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); - $query->where('category_transaction.category_id', $category->id); + $query = $this->user->transactionjournals()->distinct() + ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id') + ->where('category_transaction.category_id', $category->id); $second = $query->get(['transaction_journals.*']); - $complete = $complete->merge($first); $complete = $complete->merge($second); // sort: + /** @var Collection $complete */ $complete = $complete->sortByDesc( - function (TransactionJournal $journal) { - return $journal->date->format('Ymd'); + function ($model) { + $date = new Carbon($model->date); + + return intval($date->format('U')); } ); - // create paginator - $offset = ($page - 1) * $pageSize; - $subSet = $complete->slice($offset, $pageSize); + $offset = ($page - 1) * $pageSize; + Log::debug('Page is ' . $page); + Log::debug('Offset is ' . $offset); + Log::debug('pagesize is ' . $pageSize); + $subSet = $complete->slice($offset, $pageSize)->all(); $paginator = new LengthAwarePaginator($subSet, $complete->count(), $pageSize, $page); return $paginator; @@ -322,6 +343,7 @@ class CategoryRepository implements CategoryRepositoryInterface /** @var TransactionJournal $first */ $lastJournalQuery = $category->transactionjournals()->orderBy('date', 'DESC'); + Log::debug('lastUseDate ' . $category->name . ' (' . $category->id . ')'); if ($accounts->count() > 0) { // filter journals: @@ -334,6 +356,7 @@ class CategoryRepository implements CategoryRepositoryInterface if ($lastJournal) { $last = $lastJournal->date; + Log::debug('last is now ' . $last); } // check transactions: @@ -347,10 +370,15 @@ class CategoryRepository implements CategoryRepositoryInterface $lastTransactionQuery->whereIn('transactions.account_id', $ids); } - $lastTransaction = $lastJournalQuery->first(['transaction_journals.*']); + $lastTransaction = $lastTransactionQuery->first(['transaction_journals.*']); + if (!is_null($lastTransaction)) { + } + if (!is_null($lastTransaction) && ((!is_null($last) && $lastTransaction->date < $last) || is_null($last))) { + $last = new Carbon($lastTransaction->date); + } - if (!is_null($lastTransaction) && !is_null($last) && $lastTransaction->date < $last) { - $last = $lastTransaction->date; + if (is_null($last)) { + return new Carbon('1900-01-01'); } return $last; @@ -366,12 +394,8 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function spentInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string { - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $this->journalsInPeriod($categories, $accounts, $types, $start, $end); - $sum = '0'; - foreach ($journals as $journal) { - $sum = bcadd(TransactionJournal::amount($journal), $sum); - } + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $sum = $this->sumInPeriod($categories, $accounts, $types, $start, $end); return $sum; } @@ -385,12 +409,8 @@ class CategoryRepository implements CategoryRepositoryInterface */ public function spentInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) : string { - $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; - $journals = $this->journalsInPeriodWithoutCategory($accounts, $types, $start, $end); - $sum = '0'; - foreach ($journals as $journal) { - $sum = bcadd(TransactionJournal::amount($journal), $sum); - } + $types = [TransactionType::WITHDRAWAL, TransactionType::TRANSFER]; + $sum = $this->sumInPeriodWithoutCategory($accounts, $types, $start, $end); return $sum; } @@ -429,21 +449,100 @@ class CategoryRepository implements CategoryRepositoryInterface } /** + * @param Collection $categories * @param Collection $accounts + * @param array $types * @param Carbon $start * @param Carbon $end * * @return string */ - public function earnedInPeriodWithoutCategory(Collection $accounts, Carbon $start, Carbon $end) :string + private function sumInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end): string { - $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER]; - $journals = $this->journalsInPeriodWithoutCategory($accounts, $types, $start, $end); - $sum = '0'; - foreach ($journals as $journal) { - $sum = bcadd(TransactionJournal::amount($journal), $sum); + // first collect actual transaction journals (fairly easy) + $query = $this->user + ->transactionjournals() + ->distinct() + ->transactionTypes($types) + ->leftJoin( + 'transactions as t', function (JoinClause $join) { + $join->on('t.transaction_journal_id', '=', 'transaction_journals.id')->where('amount', '<', 0); + } + ); + + if ($end >= $start) { + $query->before($end)->after($start); + } + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $query->whereIn('t.account_id', $accountIds); + } + if ($categories->count() > 0) { + $categoryIds = $categories->pluck('id')->toArray(); + $query->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); + $query->whereIn('category_transaction_journal.category_id', $categoryIds); } + // that should do it: + $first = strval($query->sum('t.amount')); + + // then collection transactions (harder) + $query = $this->user->transactions() + ->where('transactions.amount', '<', 0) + ->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')) + ->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')); + if (count($types) > 0) { + $query->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id'); + $query->whereIn('transaction_types.type', $types); + } + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + $query->whereIn('transactions.account_id', $accountIds); + } + if ($categories->count() > 0) { + $categoryIds = $categories->pluck('id')->toArray(); + $query->leftJoin('category_transaction', 'category_transaction.transaction_id', '=', 'transactions.id'); + $query->whereIn('category_transaction.category_id', $categoryIds); + } + $second = strval($query->sum('transactions.amount')); + + return bcadd($first, $second); + + } + + /** + * @param Collection $accounts + * @param array $types + * @param Carbon $start + * @param Carbon $end + * + * @return string + */ + private function sumInPeriodWithoutCategory(Collection $accounts, array $types, Carbon $start, Carbon $end): string + { + $query = $this->user->transactionjournals() + ->distinct() + ->transactionTypes($types) + ->leftJoin('category_transaction_journal', 'category_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') + ->leftJoin( + 'transactions as t', function (JoinClause $join) { + $join->on('t.transaction_journal_id', '=', 'transaction_journals.id')->where('amount', '<', 0); + } + ) + ->leftJoin('category_transaction', 't.id', '=', 'category_transaction.transaction_id') + ->whereNull('category_transaction_journal.id') + ->whereNull('category_transaction.id') + ->before($end) + ->after($start); + + if ($accounts->count() > 0) { + $accountIds = $accounts->pluck('id')->toArray(); + + $query->whereIn('t.account_id', $accountIds); + } + $sum = strval($query->sum('t.amount')); + return $sum; + } } diff --git a/app/Support/Twig/Journal.php b/app/Support/Twig/Journal.php index 18fd9ab2bc..ff505f0e12 100644 --- a/app/Support/Twig/Journal.php +++ b/app/Support/Twig/Journal.php @@ -43,6 +43,7 @@ class Journal extends Twig_Extension } $array[] = '' . e($entry->name) . ''; } + $array = array_unique($array); $result = join(', ', $array); $cache->store($result); @@ -111,6 +112,7 @@ class Journal extends Twig_Extension } $array[] = '' . e($entry->name) . ''; } + $array = array_unique($array); $result = join(', ', $array); $cache->store($result);