diff --git a/app/Domains/Transaction/Services/ReportService.php b/app/Domains/Transaction/Services/ReportService.php index 2ee1738c..f8e75bd6 100644 --- a/app/Domains/Transaction/Services/ReportService.php +++ b/app/Domains/Transaction/Services/ReportService.php @@ -58,6 +58,23 @@ public static function generateExpensesByPeriod($teamId, $startDate, $timeUnitDi }, $resultGroup)->sortBy('date'); } + public static function generateExpensesByPeriodInDate($teamId, $startDate, $endDate, $categories = null) + { + $rangeStartAt = Carbon::createFromFormat('Y-m-d', $startDate)->startOfMonth()->format('Y-m-d'); + $rangeEndAt = Carbon::createFromFormat('Y-m-d', $endDate)->endOfMonth()->format('Y-m-d'); + + $results = self::getExpensesByCategoriesInPeriod($teamId, $rangeStartAt, $rangeEndAt, $categories); + $resultGroup = $results->groupBy('date'); + + return $resultGroup->map(function ($monthItems) { + return [ + 'date' => $monthItems->first()->date, + 'data' => $monthItems->sortByDesc('total_amount')->values(), + 'total' => $monthItems->sum('total_amount'), + ]; + }, $resultGroup)->sortBy('date'); + } + public static function getAssignedByPeriod($teamId, $startDate, $timeUnitDiff = 2, $timeUnit = 'month', $categories = null) { $rangeEndAt = Carbon::createFromFormat('Y-m-d', $startDate)->endOfMonth()->format('Y-m-d'); diff --git a/app/Http/Controllers/Finance/FinanceTrendController.php b/app/Http/Controllers/Finance/FinanceTrendController.php index c5572f04..7e80194a 100644 --- a/app/Http/Controllers/Finance/FinanceTrendController.php +++ b/app/Http/Controllers/Finance/FinanceTrendController.php @@ -210,13 +210,19 @@ public function spendingYear() $excludedAccounts = collect(explode(',', $filters['category']))->map(fn ($id) => "-$id")->all(); } + $endDate = Carbon::createFromFormat('Y-m-d', $startDate)->endOfYear()->format('Y-m-d'); + $startDate = Carbon::createFromFormat('Y-m-d', $startDate)->startOfYear()->format('Y-m-d'); + + $monthlyExpensesInYear = ReportService::generateExpensesByPeriodInDate($teamId, $startDate, $endDate, $excludedAccounts); + return [ - 'data' => ReportService::generateExpensesByPeriod($teamId, $startDate, 12, 'month', $excludedAccounts), + 'data' => $monthlyExpensesInYear, 'metaData' => [ 'name' => 'spendingYear', - 'title' => 'Expenses', + 'title' => 'Expenses this year', 'props' => [ 'headerTemplate' => 'grid', + 'total' => $monthlyExpensesInYear->sum('total'), ], ], ]; diff --git a/components.d.ts b/components.d.ts index aaee9aa8..fe2a977d 100644 --- a/components.d.ts +++ b/components.d.ts @@ -7,12 +7,31 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + IIonEllipsisVertical: typeof import('~icons/ion/ellipsis-vertical')['default'] + IMaterialSymbolsBrightnessAlertOutlineRounded: typeof import('~icons/material-symbols/brightness-alert-outline-rounded')['default'] IMdiBankTransfer: typeof import('~icons/mdi/bank-transfer')['default'] IMdiBankTransferIn: typeof import('~icons/mdi/bank-transfer-in')['default'] IMdiBankTransferOut: typeof import('~icons/mdi/bank-transfer-out')['default'] + IMdiBell: typeof import('~icons/mdi/bell')['default'] IMdiCallSplit: typeof import('~icons/mdi/call-split')['default'] + IMdiCheck: typeof import('~icons/mdi/check')['default'] + IMdiChevronLeft: typeof import('~icons/mdi/chevron-left')['default'] + IMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default'] IMdiClose: typeof import('~icons/mdi/close')['default'] + IMdiEdit: typeof import('~icons/mdi/edit')['default'] + IMdiEllipsisVertical: typeof import('~icons/mdi/ellipsis-vertical')['default'] + IMdiEmailCheck: typeof import('~icons/mdi/email-check')['default'] + IMdiExport: typeof import('~icons/mdi/export')['default'] IMdiFile: typeof import('~icons/mdi/file')['default'] + IMdiFilter: typeof import('~icons/mdi/filter')['default'] + IMdiHistory: typeof import('~icons/mdi/history')['default'] + IMdiLink: typeof import('~icons/mdi/link')['default'] + IMdiLock: typeof import('~icons/mdi/lock')['default'] + IMdiMinus: typeof import('~icons/mdi/minus')['default'] + IMdiMoney: typeof import('~icons/mdi/money')['default'] + IMdiPlus: typeof import('~icons/mdi/plus')['default'] + IMdiSearch: typeof import('~icons/mdi/search')['default'] + IMdiSort: typeof import('~icons/mdi/sort')['default'] IMdiStar: typeof import('~icons/mdi/star')['default'] IMdiStarOutline: typeof import('~icons/mdi/star-outline')['default'] IMdiSync: typeof import('~icons/mdi/sync')['default'] diff --git a/resources/js/Components/molecules/WidgetTitleCard.vue b/resources/js/Components/molecules/WidgetTitleCard.vue index 1f07634a..80cf9c34 100644 --- a/resources/js/Components/molecules/WidgetTitleCard.vue +++ b/resources/js/Components/molecules/WidgetTitleCard.vue @@ -3,7 +3,7 @@ import { AtButton } from "atmosphere-ui"; withDefaults(defineProps<{ - title: string; + title?: string; action?: { label: string, iconClass?: string, diff --git a/resources/js/Components/widgets/ChartComparison.vue b/resources/js/Components/widgets/ChartComparison.vue index 2c9866cd..2efa68f1 100644 --- a/resources/js/Components/widgets/ChartComparison.vue +++ b/resources/js/Components/widgets/ChartComparison.vue @@ -10,48 +10,29 @@ import { formatMonth } from "@/utils"; import formatMoney from "@/utils/formatMoney"; import WidgetTitleCard from "../molecules/WidgetTitleCard.vue"; -const props = defineProps({ - title: { - type: String - }, - hideHeader: { - type: Boolean - }, - headerTemplate: { - type: String, - default: 'row' - }, - type: { - type: String, - default: "bar" - }, - data: { - type: Object, - required: true - }, - groupTotal: { - type: String, - default: "total" - }, - dataItemTotal: { - type: String, - default: "total" - }, - dataItemLabel: { - type: Function - }, - hideDivider: { - type: Boolean - }, - headerLabel: { - type: String, - }, - headerTitleDate: { - default: true, - } +const props = withDefaults(defineProps<{ + title: string, + hideHeader: boolean + headerTemplate: "row" | "grid", + type: string, + data: Record, + groupTotal: string, + dataItemTotal: string, + dataItemLabel: Function + hideDivider: boolean + headerLabel: string + headerTitleDate: boolean + withPadding: boolean +}>(), { + headerTemplate: 'row', + type: "bar", + groupTotal: "total", + dataItemTotal: "total", + headerTitleDate: true, + withPadding: true }); -const emit = defineEmits(['click']); +const emit = defineEmits(['click', 'subitem-clicked', 'action']); const selectedDate = ref(null) const currentSeries = computed(() => { @@ -110,7 +91,7 @@ const handleSelection = (index: number) => { :hide-divider="hideDivider" @action="$emit('action', $event)" :border="false" - :with-padding="false" + :with-padding="withPadding" >