From 120c5d28e85689d3acc42141c60144f0bef79c13 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Thu, 30 Apr 2026 09:12:05 +0700 Subject: [PATCH] feat: add analysis feature tests and support for cross-database date extraction in AnalysisController --- app/Http/Controllers/AnalysisController.php | 30 ++++--- tests/Feature/Admin/AnalysisTest.php | 98 +++++++++++++++++++++ 2 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 tests/Feature/Admin/AnalysisTest.php diff --git a/app/Http/Controllers/AnalysisController.php b/app/Http/Controllers/AnalysisController.php index d5467f8..a03e400 100644 --- a/app/Http/Controllers/AnalysisController.php +++ b/app/Http/Controllers/AnalysisController.php @@ -56,21 +56,24 @@ public function __invoke(Request $request) ]; $stats['profit_margin'] = $this->calculateMargin($stats['net_profit'], $stats['total_revenue']); + $isSqlite = DB::getDriverName() === 'sqlite'; + $monthSelect = $isSqlite ? "CAST(strftime('%m', created_at) AS INTEGER)" : 'MONTH(created_at)'; + // Revenue vs Purchases per month $revenueByMonth = $applyFilter(DB::table('orders')) ->select( - DB::raw('MONTH(created_at) as month'), + DB::raw("$monthSelect as month"), DB::raw('SUM(total) as total') ) - ->groupBy(DB::raw('MONTH(created_at)')) + ->groupBy(DB::raw($monthSelect)) ->get(); $purchasesByMonth = $applyFilter(DB::table('purchases')) ->select( - DB::raw('MONTH(created_at) as month'), + DB::raw("$monthSelect as month"), DB::raw('SUM(total) as total') ) - ->groupBy(DB::raw('MONTH(created_at)')) + ->groupBy(DB::raw($monthSelect)) ->get(); $monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'Mei', 'Jun', 'Jul', 'Agu', 'Sep', 'Okt', 'Nov', 'Des']; @@ -138,12 +141,14 @@ public function __invoke(Request $request) ->limit(5) ->get(); + $hourSelect = $isSqlite ? "CAST(strftime('%H', created_at) AS INTEGER)" : 'HOUR(created_at)'; + $salesByHourRaw = $applyFilter(DB::table('orders')) ->select( - DB::raw('HOUR(created_at) as hour'), + DB::raw("$hourSelect as hour"), DB::raw('COUNT(*) as total') ) - ->groupBy(DB::raw('HOUR(created_at)')) + ->groupBy(DB::raw($hourSelect)) ->get(); $salesByHour = collect(range(0, 23))->map(function ($hour) use ($salesByHourRaw) { @@ -158,29 +163,30 @@ public function __invoke(Request $request) // Revenue & Profit & Volume per month $ordersByMonth = $applyFilter(DB::table('orders')) ->select( - DB::raw('MONTH(created_at) as month'), + DB::raw("$monthSelect as month"), DB::raw('SUM(total) as revenue'), DB::raw('SUM(cogs) as cogs'), DB::raw('COUNT(*) as count') ) - ->groupBy(DB::raw('MONTH(created_at)')) + ->groupBy(DB::raw($monthSelect)) ->get(); $expensesByMonth = $applyFilter(DB::table('expenses')) ->select( - DB::raw('MONTH(created_at) as month'), + DB::raw("$monthSelect as month"), DB::raw('SUM(amount) as total') ) - ->groupBy(DB::raw('MONTH(created_at)')) + ->groupBy(DB::raw($monthSelect)) ->get(); + $monthSelectPeriod = $isSqlite ? "CAST(strftime('%m', period_month) AS INTEGER)" : 'MONTH(period_month)'; $payrollsByMonth = $applyFilter(DB::table('payrolls'), 'period_month') ->select( - DB::raw('MONTH(period_month) as month'), + DB::raw("$monthSelectPeriod as month"), DB::raw('SUM(total_salary) as total') ) ->whereNull('deleted_at') - ->groupBy(DB::raw('MONTH(period_month)')) + ->groupBy(DB::raw($monthSelectPeriod)) ->get(); $revenueVsProfit = collect(range(1, 12))->map(function ($month) use ($ordersByMonth, $expensesByMonth, $payrollsByMonth, $monthNames) { diff --git a/tests/Feature/Admin/AnalysisTest.php b/tests/Feature/Admin/AnalysisTest.php new file mode 100644 index 0000000..0e723d2 --- /dev/null +++ b/tests/Feature/Admin/AnalysisTest.php @@ -0,0 +1,98 @@ +assertRedirect(route('login')); + }); + + it('returns 403 when user has no permission to view analysis', function () { + actingAs(createUnauthorizedUser()) + ->get(route('analysis')) + ->assertStatus(403); + }); +}); + +describe('Analysis Module - Authorized Actions', function () { + beforeEach(function () { + $user = createAuthorizedUser([ + 'View:Analysis', + ]); + actingAs($user); + }); + + it('can access analysis page and view statistics', function () { + // Create dummy data + Order::factory()->count(2)->create([ + 'created_at' => now(), + 'total' => 150000, + 'cogs' => 75000, + ]); + + Expense::factory()->create([ + 'created_at' => now(), + 'amount' => 10000, + ]); + + Purchase::factory()->create([ + 'created_at' => now(), + 'total' => 50000, + ]); + + get(route('analysis')) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('analysis') + ->has('stats') + ->has('salesByMonth') + ->has('revenueVsProfit') + ->has('transactionVolume') + ->has('salesByHour') + ->has('paymentMethods') + ->has('orderStatuses') + ->has('orderChannels') + ->has('topProducts') + ->has('topCustomers') + ->has('topCategories') + ); + }); + + it('can filter analysis by period', function () { + get(route('analysis', ['period' => 'month'])) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('analysis') + ->has('filters', fn ($f) => $f + ->where('period', 'month') + ->where('start_date', null) + ->where('end_date', null) + ) + ); + }); + + it('can filter analysis by date range', function () { + get(route('analysis', ['start_date' => '2026-01-01', 'end_date' => '2026-01-31'])) + ->assertOk() + ->assertInertia(fn ($page) => $page + ->component('analysis') + ->has('filters', fn ($f) => $f + ->where('period', 'all') + ->where('start_date', '2026-01-01') + ->where('end_date', '2026-01-31') + ) + ); + }); +});