From cc9415f74a510135db0841226d87f5b5d85d84d9 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Sat, 18 Nov 2023 07:33:58 -0400 Subject: [PATCH 1/4] fix: take out expense with last month comparison --- resources/js/Pages/Dashboard.vue | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/js/Pages/Dashboard.vue b/resources/js/Pages/Dashboard.vue index 0dc53102..15677d7b 100644 --- a/resources/js/Pages/Dashboard.vue +++ b/resources/js/Pages/Dashboard.vue @@ -117,7 +117,7 @@
-
+
- - + />
+ +
Date: Wed, 22 Nov 2023 21:50:13 -0400 Subject: [PATCH 2/4] feat: year comparison in net-worth --- .../Transaction/Services/ReportService.php | 27 ++++++----- .../Finance/FinanceTrendController.php | 7 ++- resources/js/Components/ChartNetworth.vue | 8 ++-- resources/js/Pages/Finance/Transactions.vue | 5 +- resources/js/Pages/Trends/AccountFilters.vue | 47 ++++++++++--------- resources/js/Pages/Trends/Overview.vue | 4 +- 6 files changed, 56 insertions(+), 42 deletions(-) diff --git a/app/Domains/Transaction/Services/ReportService.php b/app/Domains/Transaction/Services/ReportService.php index 6c6f30d6..bc352442 100644 --- a/app/Domains/Transaction/Services/ReportService.php +++ b/app/Domains/Transaction/Services/ReportService.php @@ -62,29 +62,34 @@ public static function getIncomeVsExpenses($teamId, $timeUnitDiff = 2, $startDat $endDate = Carbon::now()->endOfMonth()->format('Y-m-d'); $startDate = Carbon::now()->subMonth($timeUnitDiff)->startOfMonth()->format('Y-m-d'); + if ($timeUnit == 'year') { + $endDate = Carbon::now()->endOfYear()->format('Y-m-d'); + $startDate = Carbon::now()->subYear($timeUnitDiff)->startOfYear()->format('Y-m-d'); + } + $expenses = self::getExpensesByCategoriesInPeriod($teamId, $startDate, $endDate); - $expensesGroup = $expenses->groupBy('date'); + $expensesGroup = $expenses->groupBy($timeUnit); $income = self::getIncomeByPayeeInPeriod($teamId, $startDate, $endDate); - $incomeCategories = $income->groupBy('date'); + $incomeCategories = $income->groupBy($timeUnit); $dates = $expensesGroup->keys(); - return $dates->map(function ($month) use ($incomeCategories, $expensesGroup) { - $incomeData = $incomeCategories->get($month); - $expenseData = $expensesGroup->get($month); + return $dates->map(function ($dateUnit) use ($incomeCategories, $expensesGroup) { + $incomeData = $incomeCategories->get($dateUnit); + $expenseData = $expensesGroup->get($dateUnit); return [ - 'date' => $month, - 'month_date' => $month, + 'date' => $dateUnit, + 'date_unit' => $dateUnit, 'income' => $incomeData?->values()->all() ?? [], "expense" => $expenseData?->values()->all() ?? [], 'assets' => $incomeData?->sum('total_amount') ?? 0, 'debts' => $expenseData?->sum('total') ?? 0, ]; - })->sortBy('date')->values()->toArray(); + })->sortByDesc($timeUnit)->values()->toArray(); } public static function generateCurrentPreviousReport($teamId, $timeUnit = 'month', $timeUnitDiff = 2, $type = 'expenses') @@ -137,7 +142,7 @@ public static function getIncomeByPayeeInPeriod($teamId, $startDate, $endDate) ->balance() ->inDateFrame($startDate, $endDate) ->incomePayees() - ->selectRaw('date_format(transaction_lines.date, "%Y-%m-%01") as date, payees.name, payees.id') + ->selectRaw('date_format(transaction_lines.date, "%Y-%m-%01") as date, date_format(transaction_lines.date, "%Y-%m-%01") as month, year(transaction_lines.date) as year, payees.name, payees.id') ->groupByRaw('date_format(transaction_lines.date, "%Y-%m"), payees.id') ->orderByDesc('date') ->join('transactions', 'transactions.id', 'transaction_lines.transaction_id') @@ -150,7 +155,7 @@ public static function getExpensesByCategoriesInPeriod($teamId, $startDate, $end ->balance() ->inDateFrame($startDate, $endDate) ->expenseCategories($categories) - ->selectRaw('date_format(transactions.date, "%Y-%m-01") as date, categories.name, categories.id') + ->selectRaw('date_format(transactions.date, "%Y-%m-01") as date, date_format(transactions.date, "%Y-%m-01") as month, year(transactions.date) as year, categories.name, categories.id') ->groupByRaw('date_format(transactions.date, "%Y-%m"), categories.id') ->orderBy('date') ->get(); @@ -162,7 +167,7 @@ public static function getExpensesInPeriod($teamId, $startDate, $endDate) ->balance() ->inDateFrame($startDate, $endDate) ->expenseCategories() - ->selectRaw('date_format(transaction_lines.date, "%Y-%m-%d") as date, date_format(transaction_lines.date, "%Y-%m") as month') + ->selectRaw('date_format(transaction_lines.date, "%Y-%m-%d") as date, year(transaction_lines.date) as year, date_format(transaction_lines.date, "%Y-%m") as month') ->groupByRaw('date_format(transaction_lines.date, "%Y-%m"), transaction_lines.date') ->orderByDesc('date') ->join('transactions', 'transactions.id', 'transaction_lines.transaction_id') diff --git a/app/Http/Controllers/Finance/FinanceTrendController.php b/app/Http/Controllers/Finance/FinanceTrendController.php index 88114c81..524d51e4 100644 --- a/app/Http/Controllers/Finance/FinanceTrendController.php +++ b/app/Http/Controllers/Finance/FinanceTrendController.php @@ -143,8 +143,13 @@ public function incomeExpensesGraph() [$startDate, $endDate] = $this->getFilterDates($filters); $teamId = request()->user()->current_team_id; + $span = [ + "month" => 12, + "year" => 2 + ]; + return [ - 'data' => ReportService::getIncomeVsExpenses($teamId, 12), + 'data' => ReportService::getIncomeVsExpenses($teamId, 2, $startDate, 'year'), 'metaData' => [ 'name' => 'incomeExpensesGraph', 'title' => 'Income vs Expenses', diff --git a/resources/js/Components/ChartNetworth.vue b/resources/js/Components/ChartNetworth.vue index d70fe8a7..ae3aa500 100644 --- a/resources/js/Components/ChartNetworth.vue +++ b/resources/js/Components/ChartNetworth.vue @@ -30,20 +30,20 @@ const currentSeries = computed(() => { return [{ name: 'Debts', data: Object.values(props.data).map(item => Math.abs(item.debts)), - labels: Object.values(props.data).map(item => item.month_date) + labels: Object.values(props.data).map(item => item.date_unit) }, { name: 'Assets', data: Object.values(props.data).map(item => item.assets), - labels: Object.values(props.data).map(item => item.month_date) + labels: Object.values(props.data).map(item => item.date_unit) }]; }) const state = reactive({ headers: Object.entries(props.data).map(([dateString, item]) => ({ - label: formatMonth(item.month_date), + label: item.date_unit, value: [item.debts, item.assets], - id: item.month_date + id: item.date_unit })), options: { colors: ["#7B77D1", "#F37EA1"], diff --git a/resources/js/Pages/Finance/Transactions.vue b/resources/js/Pages/Finance/Transactions.vue index d8843b60..c4f31b5d 100644 --- a/resources/js/Pages/Finance/Transactions.vue +++ b/resources/js/Pages/Finance/Transactions.vue @@ -201,8 +201,8 @@ const listData = computed(() => { -
-
+
+

All transactions in @@ -224,6 +224,7 @@ const listData = computed(() => {

+ diff --git a/resources/js/Pages/Trends/Overview.vue b/resources/js/Pages/Trends/Overview.vue index 7f2cef45..bafc5aa6 100644 --- a/resources/js/Pages/Trends/Overview.vue +++ b/resources/js/Pages/Trends/Overview.vue @@ -129,7 +129,6 @@ const handleCategories = (categories: ICategory[]) => { From febc827ced0071c7dcb1099815e0c91553a6ebf1 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Wed, 22 Nov 2023 22:21:09 -0400 Subject: [PATCH 3/4] feat: upgrade packages --- package-lock.json | 2 +- package.json | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f768915..23e5fd34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8453,7 +8453,7 @@ "version": "3.28.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz", "integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==", - "devOptional": true, + "dev": true, "bin": { "rollup": "dist/bin/rollup" }, diff --git a/package.json b/package.json index b73fe7cf..85f5b0bc 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@vitejs/plugin-vue": "^4.5.0", "@vue/server-renderer": "^3.3.8", "@vueuse/core": "^10.6.1", - "atmosphere-ui": "^1.3.2", + "atmosphere-ui": "^1.3.3", "autoprefixer": "^10.4.15", "axios": "^1.4.0", "date-fns": "^2.30.0", @@ -56,13 +56,13 @@ "sass": "^1.69.5", "slug": "^8.2.3", "tailwindcss": "^3.3.5", - "typescript": "^5.2.2", - "unplugin-icons": "^0.16.6", + "typescript": "^5.3.2", + "unplugin-icons": "^0.17.4", "unplugin-vue-components": "^0.25.2", "uuid": "^9.0.1", "vite": "^4.5.0", - "vite-plugin-pwa": "^0.16.7", - "vue": "^3.3.4", + "vite-plugin-pwa": "^0.17.0", + "vue": "^3.3.8", "vue-multiselect": "^3.0.0-beta.3", "vue3-apexcharts": "^1.4.4", "vueuse-temporals": "^1.6.0" From dfc73c7ff0dd47b19f610840129ecdca404e48d9 Mon Sep 17 00:00:00 2001 From: Jesus Guerrero Date: Fri, 24 Nov 2023 00:16:36 -0400 Subject: [PATCH 4/4] feat: add account filters --- .../Services/TransactionService.php | 14 ++-- package-lock.json | 71 +++++++++++-------- .../Finance/Partials/FinanceTemplate.vue | 12 ++-- resources/js/Pages/Finance/Transactions.vue | 16 +++-- .../transactions/components/AccountFilter.vue | 41 +++++++++++ .../components}/AccountFilters.vue | 0 6 files changed, 107 insertions(+), 47 deletions(-) create mode 100644 resources/js/domains/transactions/components/AccountFilter.vue rename resources/js/{Pages/Trends => domains/transactions/components}/AccountFilters.vue (100%) diff --git a/app/Domains/Transaction/Services/TransactionService.php b/app/Domains/Transaction/Services/TransactionService.php index 8a341790..7e0cd347 100644 --- a/app/Domains/Transaction/Services/TransactionService.php +++ b/app/Domains/Transaction/Services/TransactionService.php @@ -223,8 +223,8 @@ public function getNetWorthMonth($teamId, $endDate) public static function getNetWorth($teamId, $startDate, $endDate) { return DB::select(" - with data (month_date, total, type, balance_type, detail_type) AS ( - SELECT LAST_DAY(tl.date) as month_date, tl.amount * tl.type, tl.type, accounts.balance_type, adt.name + with data (date_unit, total, type, balance_type, detail_type) AS ( + SELECT LAST_DAY(tl.date) as date_unit, tl.amount * tl.type, tl.type, accounts.balance_type, adt.name FROM transaction_lines tl INNER JOIN transactions t on tl.transaction_id = t.id INNER JOIN accounts on tl.account_id = accounts.id @@ -234,12 +234,12 @@ public static function getNetWorth($teamId, $startDate, $endDate) AND tl.team_id = :teamId AND balance_type IS NOT null ) - SELECT month_date, - SUM(SUM(CASE WHEN balance_type = 'debit' THEN total ELSE 0 END)) over (ORDER BY month_date) as assets, - SUM(SUM(CASE WHEN balance_type = 'credit' THEN total ELSE 0 END)) over (ORDER BY month_date) as debts + SELECT date_unit, + SUM(SUM(CASE WHEN balance_type = 'debit' THEN total ELSE 0 END)) over (ORDER BY date_unit) as assets, + SUM(SUM(CASE WHEN balance_type = 'credit' THEN total ELSE 0 END)) over (ORDER BY date_unit) as debts FROM DATA - GROUP BY month_date - ORDER BY month_date DESC + GROUP BY date_unit + ORDER BY date_unit DESC LIMIT 12; ", [ 'teamId' => $teamId, diff --git a/package-lock.json b/package-lock.json index 23e5fd34..0bc82c40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "@vitejs/plugin-vue": "^4.5.0", "@vue/server-renderer": "^3.3.8", "@vueuse/core": "^10.6.1", - "atmosphere-ui": "^1.3.2", + "atmosphere-ui": "^1.3.3", "autoprefixer": "^10.4.15", "axios": "^1.4.0", "date-fns": "^2.30.0", @@ -53,13 +53,13 @@ "sass": "^1.69.5", "slug": "^8.2.3", "tailwindcss": "^3.3.5", - "typescript": "^5.2.2", - "unplugin-icons": "^0.16.6", + "typescript": "^5.3.2", + "unplugin-icons": "^0.17.4", "unplugin-vue-components": "^0.25.2", "uuid": "^9.0.1", "vite": "^4.5.0", - "vite-plugin-pwa": "^0.16.7", - "vue": "^3.3.4", + "vite-plugin-pwa": "^0.17.0", + "vue": "^3.3.8", "vue-multiselect": "^3.0.0-beta.3", "vue3-apexcharts": "^1.4.4", "vueuse-temporals": "^1.6.0" @@ -4017,9 +4017,9 @@ } }, "node_modules/atmosphere-ui": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/atmosphere-ui/-/atmosphere-ui-1.3.2.tgz", - "integrity": "sha512-wQW3o+UaWxzeWXwrB2ziDU/BJyHtuwGKXOIJMDlpE6rp24925wmryO0LKIV0dhMemSeCo7ZMoIMo6AVGahT3lg==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/atmosphere-ui/-/atmosphere-ui-1.3.3.tgz", + "integrity": "sha512-AjIGfvf/QeS41CqJ13kn++cLBcgFHDUvrSsREDX54QGnYGyFGuceEvsTJ6EZh+JKsOU4mtDEFSNlm1aJiaMShw==", "dev": true, "dependencies": { "@popperjs/core": "^2.11.8", @@ -6931,8 +6931,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "devOptional": true, - "peer": true + "devOptional": true }, "node_modules/jsonfile": { "version": "6.1.0", @@ -7380,7 +7379,6 @@ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", "devOptional": true, - "peer": true, "dependencies": { "acorn": "^8.10.0", "pathe": "^1.1.1", @@ -7771,8 +7769,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", - "devOptional": true, - "peer": true + "devOptional": true }, "node_modules/pathval": { "version": "1.1.1", @@ -7874,7 +7871,6 @@ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", "devOptional": true, - "peer": true, "dependencies": { "jsonc-parser": "^3.2.0", "mlly": "^1.2.0", @@ -9449,9 +9445,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "devOptional": true, "bin": { "tsc": "bin/tsc", @@ -9465,8 +9461,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", - "devOptional": true, - "peer": true + "devOptional": true }, "node_modules/unbox-primitive": { "version": "1.0.2", @@ -9639,18 +9634,18 @@ } }, "node_modules/unplugin-icons": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.16.6.tgz", - "integrity": "sha512-jL70sAC7twp4hI/MTfm+vyvTRtHqiEIzf3XOjJz7yzhMEEQnk5Ey5YIXRAU03Mc4BF99ITvvnBzfyRZee86OeA==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/unplugin-icons/-/unplugin-icons-0.17.4.tgz", + "integrity": "sha512-PHLxjBx3ZV8RUBvfMafFl8uWH88jHeZgOijcRpkwgne7y2Ovx7WI0Ltzzw3fjZQ7dGaDhB8udyKVdm9N2S6BIw==", "dev": true, "dependencies": { "@antfu/install-pkg": "^0.1.1", "@antfu/utils": "^0.7.6", - "@iconify/utils": "^2.1.9", + "@iconify/utils": "^2.1.11", "debug": "^4.3.4", "kolorist": "^1.8.0", - "local-pkg": "^0.4.3", - "unplugin": "^1.4.0" + "local-pkg": "^0.5.0", + "unplugin": "^1.5.0" }, "funding": { "url": "https://github.com/sponsors/antfu" @@ -9680,6 +9675,22 @@ } } }, + "node_modules/unplugin-icons/node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/unplugin-vue-components": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/unplugin-vue-components/-/unplugin-vue-components-0.25.2.tgz", @@ -9979,13 +9990,13 @@ } }, "node_modules/vite-plugin-pwa": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.16.7.tgz", - "integrity": "sha512-4WMA5unuKlHs+koNoykeuCfTcqEGbiTRr8sVYUQMhc6tWxZpSRnv9Ojk4LKmqVhoPGHfBVCdGaMo8t9Qidkc1Q==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.17.0.tgz", + "integrity": "sha512-cOyEG8EEc7JHmyMapTnjK2j0g2BIC3ErlmOHyGzVu8hqjyF9Jt6yWMmVNFtpA6v/NNyzP28ARf3vwzIAzR1kaw==", "dev": true, "dependencies": { "debug": "^4.3.4", - "fast-glob": "^3.3.1", + "fast-glob": "^3.3.2", "pretty-bytes": "^6.1.1", "workbox-build": "^7.0.0", "workbox-window": "^7.0.0" @@ -9997,7 +10008,7 @@ "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0", "workbox-build": "^7.0.0", "workbox-window": "^7.0.0" } diff --git a/resources/js/Pages/Finance/Partials/FinanceTemplate.vue b/resources/js/Pages/Finance/Partials/FinanceTemplate.vue index edbd0d05..5f3b3162 100644 --- a/resources/js/Pages/Finance/Partials/FinanceTemplate.vue +++ b/resources/js/Pages/Finance/Partials/FinanceTemplate.vue @@ -6,8 +6,6 @@ import { useImportModal } from '@/domains/transactions/useImportModal'; import { PANEL_SIZES } from '@/utils/constants'; - const { toggleModal: toggleImportModal } = useImportModal(); - const props = defineProps({ title: { type: String @@ -32,6 +30,9 @@ }, forceShowPanel: { type: Boolean, + }, + hidePanel: { + type: Boolean } }); @@ -53,11 +54,14 @@