commonBalanceInPeriod($budget, $start, $end, $accounts); } /** * @return void */ public function cleanupBudgets() { // delete limits with amount 0: BudgetLimit::where('amount', 0)->delete(); } /** * @param Budget $budget * * @return boolean */ public function destroy(Budget $budget) { $budget->delete(); return true; } /** * @param Budget $budget * * @return Carbon */ public function firstActivity(Budget $budget) { $first = $budget->transactionjournals()->orderBy('date', 'ASC')->first(); if ($first) { return $first->date; } return new Carbon; } /** * @return Collection */ public function getActiveBudgets() { /** @var Collection $set */ $set = Auth::user()->budgets()->where('active', 1)->get(); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); return $set; } /** * @param Carbon $start * @param Carbon $end * * @return Collection */ public function getAllBudgetLimitRepetitions(Carbon $start, Carbon $end) { /** @var Collection $repetitions */ return LimitRepetition:: leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') ->leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id') ->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d 00:00:00')) ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d 00:00:00')) ->where('budgets.user_id', Auth::user()->id) ->get(['limit_repetitions.*', 'budget_limits.budget_id']); } /** * Get the budgeted amounts for each budgets in each year. * * @param Collection $budgets * @param Carbon $start * @param Carbon $end * * @return Collection */ public function getBudgetedPerYear(Collection $budgets, Carbon $start, Carbon $end) { $budgetIds = $budgets->pluck('id')->toArray(); $set = Auth::user()->budgets() ->leftJoin('budget_limits', 'budgets.id', '=', 'budget_limits.budget_id') ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') ->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')) ->where('limit_repetitions.enddate', '<=', $end->format('Y-m-d')) ->groupBy('budgets.id') ->groupBy('dateFormatted') ->whereIn('budgets.id', $budgetIds) ->get( [ 'budgets.*', DB::raw('DATE_FORMAT(`limit_repetitions`.`startdate`,"%Y") as `dateFormatted`'), DB::raw('SUM(`limit_repetitions`.`amount`) as `budgeted`'), ] ); return $set; } /** * @return Collection */ public function getBudgets() { /** @var Collection $set */ $set = Auth::user()->budgets()->get(); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); return $set; } /** * Returns an array with every budget in it and the expenses for each budget * per month. * * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @return array */ public function getBudgetsAndExpensesPerMonth(Collection $accounts, Carbon $start, Carbon $end) { $ids = $accounts->pluck('id')->toArray(); /** @var Collection $set */ $set = Auth::user()->budgets() ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->leftJoin( 'transactions', function (JoinClause $join) { $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); } ) ->groupBy('budgets.id') ->groupBy('dateFormatted') ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->whereIn('transactions.account_id', $ids) ->get( [ 'budgets.*', DB::raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y-%m") AS `dateFormatted`'), DB::raw('SUM(`transactions`.`amount`) AS `sumAmount`'), ] ); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); $return = []; foreach ($set as $budget) { $id = $budget->id; if (!isset($return[$id])) { $return[$id] = [ 'budget' => $budget, 'entries' => [], ]; } // store each entry: $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; } return $return; } /** * Returns an array with every budget in it and the expenses for each budget * per year for. * * @param Collection $budgets * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) // it's a query. * * @return array */ public function getBudgetsAndExpensesPerYear(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) { $ids = $accounts->pluck('id')->toArray(); $budgetIds = $budgets->pluck('id')->toArray(); /** @var Collection $set */ $set = Auth::user()->budgets() ->leftJoin('budget_transaction_journal', 'budgets.id', '=', 'budget_transaction_journal.budget_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->leftJoin( 'transactions', function (JoinClause $join) { $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); } ) ->groupBy('budgets.id') ->groupBy('dateFormatted') ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->whereIn('transactions.account_id', $ids) ->whereIn('budgets.id', $budgetIds) ->get( [ 'budgets.*', DB::raw('DATE_FORMAT(`transaction_journals`.`date`, "%Y") AS `dateFormatted`'), DB::raw('SUM(`transactions`.`amount`) AS `sumAmount`'), ] ); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); $return = []; foreach ($set as $budget) { $id = $budget->id; if (!isset($return[$id])) { $return[$id] = [ 'budget' => $budget, 'entries' => [], ]; } // store each entry: $return[$id]['entries'][$budget->dateFormatted] = $budget->sumAmount; } return $return; } /** * Returns a list of budgets, budget limits and limit repetitions * (doubling any of them in a left join) * * @param Carbon $start * @param Carbon $end * * @return Collection */ public function getBudgetsAndLimitsInRange(Carbon $start, Carbon $end) { /** @var Collection $set */ $set = Auth::user() ->budgets() ->leftJoin('budget_limits', 'budget_limits.budget_id', '=', 'budgets.id') ->leftJoin('limit_repetitions', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id') ->where( function (Builder $query) use ($start, $end) { $query->where( function (Builder $query) use ($start, $end) { $query->where('limit_repetitions.startdate', '>=', $start->format('Y-m-d')); $query->where('limit_repetitions.startdate', '<=', $end->format('Y-m-d')); } ); $query->orWhere( function (Builder $query) { $query->whereNull('limit_repetitions.startdate'); $query->whereNull('limit_repetitions.enddate'); } ); } ) ->get(['budgets.*', 'limit_repetitions.startdate', 'limit_repetitions.enddate', 'limit_repetitions.amount']); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); return $set; } /** * @param Budget $budget * @param Carbon $start * @param Carbon $end * * @return LimitRepetition|null */ public function getCurrentRepetition(Budget $budget, Carbon $start, Carbon $end) { $data = $budget->limitrepetitions() ->where('limit_repetitions.startdate', $start->format('Y-m-d 00:00:00')) ->where('limit_repetitions.enddate', $end->format('Y-m-d 00:00:00')) ->first(['limit_repetitions.*']); return $data; } /** * Returns the expenses for this budget grouped per day, with the date * in "date" (a string, not a Carbon) and the amount in "dailyAmount". * * @param Budget $budget * @param Carbon $start * @param Carbon $end * * @return Collection */ public function getExpensesPerDay(Budget $budget, Carbon $start, Carbon $end) { $set = Auth::user()->budgets() ->leftJoin('budget_transaction_journal', 'budget_transaction_journal.budget_id', '=', 'budgets.id') ->leftJoin('transaction_journals', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->where('transaction_journals.date', '>=', $start->format('Y-m-d')) ->whereNull('transaction_journals.deleted_at') ->where('transaction_journals.date', '<=', $end->format('Y-m-d')) ->where('budgets.id', $budget->id) ->where('transactions.amount', '<', 0) ->groupBy('transaction_journals.date') ->orderBy('transaction_journals.date') ->get(['transaction_journals.date', DB::raw('SUM(`transactions`.`amount`) as `dailyAmount`')]); return $set; } /** * @param Budget $budget * * @return Carbon */ public function getFirstBudgetLimitDate(Budget $budget) { $limit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first(); if ($limit) { return $limit->startdate; } return Carbon::now()->startOfYear(); } /** * @return Collection */ public function getInactiveBudgets() { /** @var Collection $set */ $set = Auth::user()->budgets()->where('active', 0)->get(); $set = $set->sortBy( function (Budget $budget) { return strtolower($budget->name); } ); return $set; } /** * Returns all the transaction journals for a limit, possibly limited by a limit repetition. * * @param Budget $budget * @param LimitRepetition $repetition * @param int $take * * @return LengthAwarePaginator */ public function getJournals(Budget $budget, LimitRepetition $repetition = null, int $take = 50) { $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(); if (!is_null($repetition->id)) { $setQuery->after($repetition->startdate)->before($repetition->enddate); $countQuery->after($repetition->startdate)->before($repetition->enddate); } $set = $setQuery->get(['transaction_journals.*']); $count = $countQuery->count(); $paginator = new LengthAwarePaginator($set, $count, $take, $offset); return $paginator; } /** * @param Carbon $start * @param Carbon $end * * @return Collection */ public function getWithoutBudget(Carbon $start, Carbon $end) { return Auth::user() ->transactionjournals() ->transactionTypes([TransactionType::WITHDRAWAL]) ->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.*']); } /** * @param Carbon $start * @param Carbon $end * * @return string */ public function getWithoutBudgetSum(Carbon $start, Carbon $end) { $entry = 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) ->leftJoin( 'transactions', function (JoinClause $join) { $join->on('transactions.transaction_journal_id', '=', 'transaction_journals.id')->where('transactions.amount', '<', 0); } ) ->transactionTypes([TransactionType::WITHDRAWAL]) ->first([DB::raw('SUM(`transactions`.`amount`) as `journalAmount`')]); if (is_null($entry->journalAmount)) { return ''; } return $entry->journalAmount; } /** * Returns an array with the following key:value pairs: * * yyyy-mm-dd: * * That array contains: * * budgetid: * * Where yyyy-mm-dd is the date and is the money spent using WITHDRAWALS in the $budget * from the given users accounts.. * * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @return array */ public function spentAllPerDayForAccounts(Collection $accounts, Carbon $start, Carbon $end) { $ids = $accounts->pluck('id')->toArray(); /** @var Collection $query */ $query = Auth::user()->transactionJournals() ->transactionTypes([TransactionType::WITHDRAWAL]) ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->whereIn('transactions.account_id', $ids) ->where('transactions.amount', '<', 0) ->before($end) ->after($start) ->groupBy('budget_id') ->groupBy('dateFormatted') ->get( ['transaction_journals.date as dateFormatted', 'budget_transaction_journal.budget_id', DB::raw('SUM(`transactions`.`amount`) AS `sum`')] ); $return = []; foreach ($query->toArray() as $entry) { $budgetId = $entry['budget_id']; if (!isset($return[$budgetId])) { $return[$budgetId] = []; } $return[$budgetId][$entry['dateFormatted']] = $entry['sum']; } return $return; } /** * Returns a list of expenses (in the field "spent", grouped per budget per account. * * @param Collection $budgets * @param Collection $accounts * @param Carbon $start * @param Carbon $end * * @return Collection */ public function spentPerBudgetPerAccount(Collection $budgets, Collection $accounts, Carbon $start, Carbon $end) { $accountIds = $accounts->pluck('id')->toArray(); $budgetIds = $budgets->pluck('id')->toArray(); $set = Auth::user()->transactionjournals() ->leftJoin( 'transactions AS t_from', function (JoinClause $join) { $join->on('transaction_journals.id', '=', 't_from.transaction_journal_id')->where('t_from.amount', '<', 0); } ) ->leftJoin( 'transactions AS t_to', function (JoinClause $join) { $join->on('transaction_journals.id', '=', 't_to.transaction_journal_id')->where('t_to.amount', '>', 0); } ) ->leftJoin('budget_transaction_journal', 'transaction_journals.id', '=', 'budget_transaction_journal.transaction_journal_id') ->whereIn('t_from.account_id', $accountIds) ->whereNotIn('t_to.account_id', $accountIds) ->where( function (Builder $q) use ($budgetIds) { $q->whereIn('budget_transaction_journal.budget_id', $budgetIds); $q->orWhereNull('budget_transaction_journal.budget_id'); } ) ->after($start) ->before($end) ->groupBy('t_from.account_id') ->groupBy('budget_transaction_journal.budget_id') ->transactionTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) // opening balance is not an expense. ->get( [ 't_from.account_id', 'budget_transaction_journal.budget_id', DB::raw('SUM(`t_from`.`amount`) AS `spent`'), ] ); return $set; } /** * Returns an array with the following key:value pairs: * * yyyy-mm-dd: * * Where yyyy-mm-dd is the date and is the money spent using DEPOSITS in the $budget * from all the users accounts. * * @param Budget $budget * @param Carbon $start * @param Carbon $end * * @return array */ public function spentPerDay(Budget $budget, Carbon $start, Carbon $end): array { /** @var Collection $query */ $query = $budget->transactionjournals() ->transactionTypes([TransactionType::WITHDRAWAL]) ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->where('transactions.amount', '<', 0) ->before($end) ->after($start) ->groupBy('dateFormatted')->get(['transaction_journals.date as dateFormatted', DB::raw('SUM(`transactions`.`amount`) AS `sum`')]); $return = []; foreach ($query->toArray() as $entry) { $return[$entry['dateFormatted']] = $entry['sum']; } return $return; } /** * @param array $data * * @return Budget */ public function store(array $data) { $newBudget = new Budget( [ 'user_id' => $data['user'], 'name' => $data['name'], ] ); $newBudget->save(); return $newBudget; } /** * @param Budget $budget * @param array $data * * @return Budget */ public function update(Budget $budget, array $data) { // update the account: $budget->name = $data['name']; $budget->active = $data['active']; $budget->save(); return $budget; } /** * @param Budget $budget * @param Carbon $date * @param int $amount * * @return BudgetLimit */ public function updateLimitAmount(Budget $budget, Carbon $date, int $amount) { // there should be a budget limit for this startdate: /** @var BudgetLimit $limit */ $limit = $budget->budgetlimits()->where('budget_limits.startdate', $date)->first(['budget_limits.*']); if (!$limit) { // if not, create one! $limit = new BudgetLimit; $limit->budget()->associate($budget); $limit->startdate = $date; $limit->amount = $amount; $limit->repeat_freq = 'monthly'; $limit->repeats = 0; $limit->save(); // likewise, there should be a limit repetition to match the end date // (which is always the end of the month) but that is caught by an event. } else { if ($amount > 0) { $limit->amount = $amount; $limit->save(); } else { $limit->delete(); } } return $limit; } }