/**
  * @param Collection $accounts
  * @param Carbon     $start
  * @param Carbon     $end
  *
  * @return string
  */
 public function spentInPeriod(Collection $accounts, Carbon $start, Carbon $end) : string
 {
     /** @var HasMany $query */
     $query = $this->user->transactionJournals()->expanded()->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]);
     if ($end >= $start) {
         $query->before($end)->after($start);
     }
     if ($accounts->count() > 0) {
         $accountIds = $accounts->pluck('id')->toArray();
         $query->leftJoin('transactions as source', function (JoinClause $join) {
             $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0);
         });
         $query->leftJoin('transactions as destination', function (JoinClause $join) {
             $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0);
         });
         $query->whereIn('source.account_id', $accountIds);
         $query->whereNotIn('destination.account_id', $accountIds);
         $query->whereNull('source.deleted_at');
         $query->whereNull('destination.deleted_at');
         $query->distinct();
     }
     // remove group by
     $query->getQuery()->getQuery()->groups = null;
     $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray();
     $sum = $this->user->transactions()->whereIn('transaction_journal_id', $ids)->where('amount', '<', '0')->whereNull('transactions.deleted_at')->sum('amount');
     return strval($sum);
 }
 /**
  * @param Collection $accounts
  * @param Carbon     $start
  * @param Carbon     $end
  *
  * @return string
  */
 public function spentInPeriodWithoutBudget(Collection $accounts, Carbon $start, Carbon $end) : string
 {
     $types = [TransactionType::WITHDRAWAL];
     $query = $this->user->transactionJournals()->distinct()->transactionTypes($types)->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')->leftJoin('transactions as source', function (JoinClause $join) {
         $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0);
     })->leftJoin('transactions as destination', function (JoinClause $join) {
         $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0);
     })->leftJoin('budget_transaction', 'source.id', '=', 'budget_transaction.transaction_id')->whereNull('budget_transaction_journal.id')->whereNull('budget_transaction.id')->before($end)->after($start)->whereNull('source.deleted_at')->whereNull('destination.deleted_at')->where('transaction_journals.completed', 1);
     if ($accounts->count() > 0) {
         $accountIds = $accounts->pluck('id')->toArray();
         $set = join(', ', $accountIds);
         $query->whereRaw('(source.account_id in (' . $set . ') XOR destination.account_id in (' . $set . '))');
     }
     $ids = $query->get(['transaction_journals.id'])->pluck('id')->toArray();
     $sum = '0';
     if (count($ids) > 0) {
         $sum = strval($this->user->transactions()->whereIn('transaction_journal_id', $ids)->where('amount', '<', '0')->whereNull('transactions.deleted_at')->sum('amount'));
     }
     return $sum;
 }
 /**
  * @param Collection $categories
  * @param Collection $accounts
  * @param array      $types
  * @param Carbon     $start
  * @param Carbon     $end
  *
  * @return string
  */
 private function sumInPeriod(Collection $categories, Collection $accounts, array $types, Carbon $start, Carbon $end) : string
 {
     // first collect actual transaction journals (fairly easy)
     $query = $this->user->transactionJournals()->transactionTypes($types)->leftJoin('transactions as source', function (JoinClause $join) {
         $join->on('source.transaction_journal_id', '=', 'transaction_journals.id')->where('source.amount', '<', 0);
     })->leftJoin('transactions as destination', function (JoinClause $join) {
         $join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')->where('destination.amount', '>', 0);
     });
     if ($end >= $start) {
         $query->before($end)->after($start);
     }
     if ($accounts->count() > 0) {
         $accountIds = $accounts->pluck('id')->toArray();
         $set = join(', ', $accountIds);
         $query->whereRaw('(source.account_id in (' . $set . ') XOR destination.account_id in (' . $set . '))');
     }
     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('source.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);
 }