Skip to content

Commit

Permalink
fix: improve income vs expenses graph
Browse files Browse the repository at this point in the history
  • Loading branch information
jesusantguerrero committed Oct 28, 2023
1 parent af6012e commit 080ecaf
Show file tree
Hide file tree
Showing 13 changed files with 211 additions and 95 deletions.
55 changes: 49 additions & 6 deletions app/Domains/Transaction/Services/ReportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace App\Domains\Transaction\Services;

use App\Domains\Transaction\Models\Transaction;
use App\Domains\Transaction\Models\TransactionLine;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use App\Domains\Transaction\Models\Transaction;
use App\Domains\Transaction\Models\TransactionLine;

class ReportService
{
Expand Down Expand Up @@ -40,12 +40,12 @@ public function revenueReport($teamId, $methodName = 'payments')
return $results;
}

public static function generateExpensesByPeriod($teamId, $timeUnit = 'month', $timeUnitDiff = 2, $type = 'expenses')
public static function generateExpensesByPeriod($teamId, $startDate, $timeUnitDiff = 2, $timeUnit = 'month')
{
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
$startDate = Carbon::now()->subMonth($timeUnitDiff)->startOfMonth()->format('Y-m-d');
$rangeEndAt = Carbon::createFromFormat('Y-m-d', $startDate)->endOfMonth()->format('Y-m-d');
$rangeStartAt = Carbon::now()->subMonth($timeUnitDiff)->startOfMonth()->format('Y-m-d');

$results = self::getExpensesByCategoriesInPeriod($teamId, $startDate, $endDate);
$results = self::getExpensesByCategoriesInPeriod($teamId, $rangeStartAt, $rangeEndAt);
$resultGroup = $results->groupBy('date');

return $resultGroup->map(function ($monthItems) {
Expand All @@ -57,6 +57,36 @@ public static function generateExpensesByPeriod($teamId, $timeUnit = 'month', $t
}, $resultGroup)->sortBy('date');
}

public static function getIncomeVsExpenses($teamId, $timeUnitDiff = 2, $startDate = null, $timeUnit = 'month')
{
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
$startDate = Carbon::now()->subMonth($timeUnitDiff)->startOfMonth()->format('Y-m-d');

$expenses = self::getExpensesByCategoriesInPeriod($teamId, $startDate, $endDate);
$expensesGroup = $expenses->groupBy('date');



$income = self::getIncomeByPayeeInPeriod($teamId, $startDate, $endDate);
$incomeCategories = $income->groupBy('date');

$dates = $expensesGroup->keys();


return $dates->map(function ($month) use ($incomeCategories, $expensesGroup) {
$incomeData = $incomeCategories->get($month);
$expenseData = $expensesGroup->get($month);
return [
'date' => $month,
'month_date' => $month,
'income' => $incomeData?->values()->all() ?? [],
"expense" => $expenseData?->values()->all() ?? [],
'assets' => $incomeData?->sum('total_amount') ?? 0,
'debts' => $expenseData?->sum('total') ?? 0,
];
})->sortBy('date')->values()->toArray();
}

public static function generateCurrentPreviousReport($teamId, $timeUnit = 'month', $timeUnitDiff = 2, $type = 'expenses')
{
$endDate = Carbon::now()->endOfMonth()->format('Y-m-d');
Expand Down Expand Up @@ -101,6 +131,19 @@ public static function getExpensesByYear($year, $teamId)
->get();
}

public static function getIncomeByPayeeInPeriod($teamId, $startDate, $endDate)
{
return TransactionLine::byTeam($teamId)
->balance()
->inDateFrame($startDate, $endDate)
->incomePayees()
->selectRaw('date_format(transaction_lines.date, "%Y-%m-%01") as date, payees.name, payees.id')
->groupByRaw('date_format(transaction_lines.date, "%Y-%m"), payees.id')
->orderByDesc('date')
->join('transactions', 'transactions.id', 'transaction_lines.transaction_id')
->get();
}

public static function getExpensesByCategoriesInPeriod($teamId, $startDate, $endDate, $categories = null)
{
return Transaction::byTeam($teamId)
Expand Down
15 changes: 14 additions & 1 deletion app/Domains/Transaction/Traits/TransactionLineTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace App\Domains\Transaction\Traits;

use App\Domains\Budget\Data\BudgetReservedNames;
use Illuminate\Support\Facades\DB;
use Insane\Journal\Models\Core\Transaction;
use App\Domains\Budget\Data\BudgetReservedNames;

trait TransactionLineTrait
{
Expand Down Expand Up @@ -91,6 +91,19 @@ public function scopeExpenseCategories($query, array $categories = null)
return $query;
}

public function scopeIncomePayees($query, array $payees = null)
{
$query->where('categories.name', BudgetReservedNames::READY_TO_ASSIGN->value)
->join('categories', 'transaction_lines.category_id', '=', 'categories.id')
->join('payees', 'transaction_lines.payee_id', '=', 'payees.id');

if ($payees) {
$query->whereIn('transaction_lines.payee_id', $payees);
}

return $query;
}

public function scopePayees($query, array $payees)
{
return $query->whereIn('transaction_lines.payee_id', $payees)
Expand Down
26 changes: 25 additions & 1 deletion app/Http/Controllers/Finance/FinanceTrendController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class FinanceTrendController extends Controller
'payees' => 'payee',
'net-worth' => 'NetWorth',
'income-expenses' => 'IncomeExpenses',
'spending-year' => 'spendingYear',
'income-expenses-graph' => 'IncomeExpensesGraph',
'year-summary' => 'yearSummary',
];
Expand Down Expand Up @@ -143,17 +144,40 @@ public function incomeExpensesGraph()
$teamId = request()->user()->current_team_id;

return [
'data' => ReportService::generateExpensesByPeriod($teamId, 'month', 12),
'data' => ReportService::getIncomeVsExpenses($teamId, 12),
'metaData' => [
'name' => 'incomeExpensesGraph',
'title' => 'Income vs Expenses',
'props' => [
'headerTemplate' => 'grid',
"assetsLabel" => "income",
"debtsLabel" => "expense"
],
],
];
}

public function spendingYear()
{
$queryParams = request()->query();
$filters = isset($queryParams['filter']) ? $queryParams['filter'] : [];
[$startDate, $endDate] = $this->getFilterDates($filters);
$teamId = request()->user()->current_team_id;

return [
'data' => ReportService::generateExpensesByPeriod($teamId, $startDate, 12),
'metaData' => [
'name' => 'spendingYear',
'title' => 'Expenses',
'props' => [
'headerTemplate' => 'grid',
],
],
];
}



public function yearSummary()
{
// $queryParams = request()->query();
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/System/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function __invoke()
'budgetTotal' => $budget,
'transactionTotal' => $transactionsTotal,
'expenses' => ReportService::generateCurrentPreviousReport($teamId, 'month', 1),
'revenue' => ReportService::generateExpensesByPeriod($teamId),
'revenue' => ReportService::generateExpensesByPeriod($teamId, $startDate),
'onboarding' => function () use ($team) {
$onboarding = $team->onboarding();

Expand Down
10 changes: 6 additions & 4 deletions resources/js/Components/atoms/LogerButtonCircle.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<script setup lang="ts">
import LogerButtonTab from './LogerButtonTab.vue';
</script>


<template>
<LogerButtonTab
rounded
type="button"
class="relative text-body-1/80 bg-base-lvl-1 hover:bg-primary/10 transition border-base-lvl-1 hover:border-primary rounded-full"
class="relative transition bg-transparent rounded-full text-body-1/80 hover:bg-primary/10 border-base-lvl-1 hover:border-primary"
>
<slot />
</LogerButtonTab>
</template>

<script setup>
import LogerButtonTab from './LogerButtonTab.vue';
</script>
18 changes: 11 additions & 7 deletions resources/js/Components/organisms/WelcomeCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,22 @@ const cardShadow = inject("cardShadow", '')
</script>

<template>
<div
<section
class="px-5 py-3 transition border divide-y rounded-lg divide-base border-base bg-base-lvl-3"
:class="[cardShadow]"
>
<div class="items-center pb-2 md:justify-between md:flex">
<h1 class="font-bold text-body">
{{ message }} <span class="text-primary">{{ username }}</span>
</h1>
<header class="items-center pb-2 md:justify-between md:flex">
<section class="flex items-center">
<slot name="before" />

<h1 class="font-bold text-body">
{{ message }} <span class="text-primary">{{ username }}</span>
</h1>
</section>
<div class="space-x-2">
<slot name="action" />
</div>
</div>
</header>
<slot></slot>
</div>
</section>
</template>
81 changes: 41 additions & 40 deletions resources/js/Components/widgets/ChartComparison.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, reactive, ref, inject } from "vue";
import { computed, ref, inject } from "vue";
import LogerChart from "@/Components/organisms/LogerChart.vue";
Expand All @@ -8,6 +8,7 @@ import LogerButtonTab from "@/Components/atoms/LogerButtonTab.vue";
import { formatMonth } from "@/utils";
import formatMoney from "@/utils/formatMoney";
import WidgetTitleCard from "../molecules/WidgetTitleCard.vue";
const props = defineProps({
title: {
Expand Down Expand Up @@ -69,45 +70,45 @@ const state = computed(() => {
</script>

<template>
<div class="w-full comparison-card">
<div class="px-5 pb-10 rounded-lg">
<h5 class="p-4 font-bold text-left card-title">
<LogerButtonTab v-if="selectedDate" @click="selectedDate=null">
<i class="fa fa-arrow-left"></i>
</LogerButtonTab>
{{ title }}
<span v-if="selectedDate" class="capitalize text-primary">{{ formatMonth(selectedDate) }}</span>
</h5>
<div class="card-text" >
<div
:class="[headerTemplate == 'grid' ? 'md:grid md:grid-cols-4' : 'md:flex']"
class="w-full mb-2 divide-y comparison-header md:px-10 text-body-1/50 md:space-x-2 md:divide-x md:divide-y-0 divide-dashed divide-opacity-20 divide-body-1 bg-base-lvl-2">
<div
v-for="header in state.headers"
:key="header.id"
@click="selectedDate = header.id"
class="flex items-center justify-between w-full px-4 py-2 cursor-pointer comparison-header__item md:py-6 md:justify-center md:flex-col previous-period hover:text-body/80"
>
<h6 class="period-title">{{ header.label }}</h6>
<span class="mt-2 text-xs period-value">
<NumberHider />
{{ formatMoney(header.value) }}
</span>
</div>
</div>
<LogerChart
class="bg-white"
style="height:300px; width: 100%"
label="name"
type="bar"
:labels="currentSeries[0].labels"
:options="state.options"
:series="state.series"
:has-hidden-values="hasHiddenValues"
/>
</div>
</div>
</div>
<WidgetTitleCard :title="title" :action="action" @action="$emit('action', $event)">
<template #before v-if="selectedDate">
<LogerButtonTab @click="selectedDate=null">
<i class="fa fa-arrow-left"></i>
</LogerButtonTab>
</template>
<template #action v-if="selectedDate">
<span v-if="selectedDate" class="capitalize text-primary">{{ formatMonth(selectedDate) }}</span>
</template>

<section class="w-full card-text" >
<div
:class="[headerTemplate == 'grid' ? 'md:grid md:grid-cols-4' : 'md:flex']"
class="w-full mb-2 divide-y comparison-header md:px-10 text-body-1/50 md:space-x-2 md:divide-x md:divide-y-0 divide-dashed divide-opacity-20 divide-body-1 bg-base-lvl-2">
<div
v-for="header in state.headers"
:key="header.id"
@click="selectedDate = header.id"
class="flex items-center justify-between w-full px-4 py-2 cursor-pointer comparison-header__item md:py-6 md:justify-center md:flex-col previous-period hover:text-body/80"
>
<h6 class="period-title">{{ header.label }}</h6>
<span class="mt-2 text-xs period-value">
<NumberHider />
{{ formatMoney(header.value) }}
</span>
</div>
</div>
<LogerChart
class="bg-white"
style="height:300px; width: 100%"
label="name"
type="bar"
:labels="currentSeries[0].labels"
:options="state.options"
:series="state.series"
:has-hidden-values="hasHiddenValues"
/>
</section>
</WidgetTitleCard>
</template>


Expand Down
10 changes: 6 additions & 4 deletions resources/js/Pages/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
import OccurrenceCard from '@/Components/Modules/occurrence/OccurrenceCard.vue';
import { useAppContextStore } from '@/store';
import { ITransaction } from '@/domains/transactions/models';
import { router } from '@inertiajs/vue3';
import BudgetWidget from './BudgetWidget.vue';
import NetWorthWidget from './NetWorthWidget.vue';
defineProps({
revenue: {
Expand Down Expand Up @@ -147,9 +144,14 @@
<ChartComparison
class="w-full mt-4 mb-10 overflow-hidden bg-white rounded-lg"
:class="[cardShadow]"
:title="t('Spending summary')"
:title="$t('Spending summary')"
ref="ComparisonRevenue"
:data="revenue"
:action="{
label: 'Go to Trends',
iconClass: 'fa fa-chevron-right',
}"
@action="router.visit('/trends/income-expenses-graph')"
/>
</section>

Expand Down
Loading

0 comments on commit 080ecaf

Please sign in to comment.