diff --git a/src/app/Http/Controllers/DashboardController.php b/src/app/Http/Controllers/DashboardController.php index 6673c4b..186e314 100644 --- a/src/app/Http/Controllers/DashboardController.php +++ b/src/app/Http/Controllers/DashboardController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class DashboardController extends Controller @@ -12,28 +13,45 @@ public function index() return view('dashboard'); } - public function summary(): JsonResponse + private function dateRange(Request $request): array { + $from = $request->query('from'); + $to = $request->query('to'); + return [ + $from ?: null, + $to ? $to . ' 23:59:59' : null, + ]; + } + + public function summary(Request $request): JsonResponse + { + [$from, $to] = $this->dateRange($request); + $totalSuppliers = DB::table('PL_SUPPLIERACCOUNT')->count(); $totalProducts = DB::table('STK_STOCKITEM')->where('ISACTVE', 'Y')->count(); $totalCustomers = DB::table('SL_CUSTOMERACCOUNT')->count(); - $purchaseSpend = DB::table('PL_BILLTRAN') - ->selectRaw('COALESCE(SUM(QTYTOINVOICE * COSTPRICE / COSTPRICEPER), 0) as total') - ->value('total'); + $purchaseQ = DB::table('PL_BILLTRAN as il') + ->join('PL_BILL as i', 'il.REFNO', '=', 'i.REFNO'); + if ($from) $purchaseQ->where('i.DOCDTETME', '>=', $from); + if ($to) $purchaseQ->where('i.DOCDTETME', '<=', $to); + $purchaseSpend = $purchaseQ->selectRaw('COALESCE(SUM(il.QTYTOINVOICE * il.COSTPRICE / il.COSTPRICEPER), 0) as total')->value('total'); - $salesRevenue = DB::table('SL_SALESINVOICETRAN') - ->selectRaw('COALESCE(SUM(QTYTOINVOICE * SELLINGPRICE / SELLINGPRICEPER), 0) as total') - ->value('total'); + $salesQ = DB::table('SL_SALESINVOICETRAN as sl') + ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO'); + if ($from) $salesQ->where('si.DOCDTETME', '>=', $from); + if ($to) $salesQ->where('si.DOCDTETME', '<=', $to); - $salesCost = DB::table('SL_SALESINVOICETRAN') - ->selectRaw('COALESCE(SUM(QTYTOINVOICE * COSTPRICE / COSTPRICEPER), 0) as total') - ->value('total'); + $salesRevenue = (clone $salesQ)->selectRaw('COALESCE(SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER), 0) as total')->value('total'); + $salesCost = (clone $salesQ)->selectRaw('COALESCE(SUM(sl.QTYTOINVOICE * sl.COSTPRICE / sl.COSTPRICEPER), 0) as total')->value('total'); $grossProfit = $salesRevenue - $salesCost; $margin = $salesRevenue > 0 ? round(($grossProfit / $salesRevenue) * 100, 1) : 0; - $totalInvoices = DB::table('PL_BILL')->count(); + $invoiceQ = DB::table('PL_BILL'); + if ($from) $invoiceQ->where('DOCDTETME', '>=', $from); + if ($to) $invoiceQ->where('DOCDTETME', '<=', $to); + $totalInvoices = $invoiceQ->count(); return response()->json([ 'total_suppliers' => $totalSuppliers, @@ -47,29 +65,37 @@ public function summary(): JsonResponse ]); } - public function topSuppliers(): JsonResponse + public function topSuppliers(Request $request): JsonResponse { - $suppliers = DB::table('PL_BILLTRAN as il') + [$from, $to] = $this->dateRange($request); + + $q = DB::table('PL_BILLTRAN as il') ->join('PL_BILL as i', 'il.REFNO', '=', 'i.REFNO') ->selectRaw('i.ACCNO as code, i.SUPLNME as name, COUNT(DISTINCT i.REFNO) as invoice_count, SUM(il.QTYTOINVOICE * il.COSTPRICE / il.COSTPRICEPER) as total_spend, SUM(il.QTYTOINVOICE) as total_qty') ->groupBy('i.ACCNO', 'i.SUPLNME') ->orderByDesc('total_spend') - ->limit(10) - ->get(); + ->limit(10); + if ($from) $q->where('i.DOCDTETME', '>=', $from); + if ($to) $q->where('i.DOCDTETME', '<=', $to); - return response()->json($suppliers); + return response()->json($q->get()); } - public function topProducts(): JsonResponse + public function topProducts(Request $request): JsonResponse { - $products = DB::table('SL_SALESINVOICETRAN as sl') + [$from, $to] = $this->dateRange($request); + + $q = DB::table('SL_SALESINVOICETRAN as sl') ->join('STK_STOCKITEM as p', 'sl.STOCKCODE', '=', 'p.STOCKCODE') + ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO') ->selectRaw('sl.STOCKCODE as code, p.DESCRIPTION as name, SUM(sl.QTYTOINVOICE) as total_qty_sold, SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER) as total_revenue, SUM(sl.QTYTOINVOICE * sl.COSTPRICE / sl.COSTPRICEPER) as total_cost') ->groupBy('sl.STOCKCODE', 'p.DESCRIPTION') ->orderByDesc('total_revenue') - ->limit(10) - ->get() - ->map(function ($item) { + ->limit(10); + if ($from) $q->where('si.DOCDTETME', '>=', $from); + if ($to) $q->where('si.DOCDTETME', '<=', $to); + + $products = $q->get()->map(function ($item) { $item->margin = $item->total_revenue > 0 ? round((($item->total_revenue - $item->total_cost) / $item->total_revenue) * 100, 1) : 0; @@ -79,29 +105,36 @@ public function topProducts(): JsonResponse return response()->json($products); } - public function spendOverTime(): JsonResponse + public function spendOverTime(Request $request): JsonResponse { - $data = DB::table('PL_BILLTRAN as il') + [$from, $to] = $this->dateRange($request); + + $q = DB::table('PL_BILLTRAN as il') ->join('PL_BILL as i', 'il.REFNO', '=', 'i.REFNO') ->selectRaw("DATE_FORMAT(i.DOCDTETME, '%Y-%m') as month, SUM(il.QTYTOINVOICE * il.COSTPRICE / il.COSTPRICEPER) as total_spend") ->whereNotNull('i.DOCDTETME') ->groupByRaw("DATE_FORMAT(i.DOCDTETME, '%Y-%m')") - ->orderBy('month') - ->get(); + ->orderBy('month'); + if ($from) $q->where('i.DOCDTETME', '>=', $from); + if ($to) $q->where('i.DOCDTETME', '<=', $to); - return response()->json($data); + return response()->json($q->get()); } - public function salesOverTime(): JsonResponse + public function salesOverTime(Request $request): JsonResponse { - $data = DB::table('SL_SALESINVOICETRAN as sl') + [$from, $to] = $this->dateRange($request); + + $q = DB::table('SL_SALESINVOICETRAN as sl') ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO') ->selectRaw("DATE_FORMAT(si.DOCDTETME, '%Y-%m') as month, SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER) as total_revenue, SUM(sl.QTYTOINVOICE * sl.COSTPRICE / sl.COSTPRICEPER) as total_cost") ->whereNotNull('si.DOCDTETME') ->groupByRaw("DATE_FORMAT(si.DOCDTETME, '%Y-%m')") - ->orderBy('month') - ->get() - ->map(function ($item) { + ->orderBy('month'); + if ($from) $q->where('si.DOCDTETME', '>=', $from); + if ($to) $q->where('si.DOCDTETME', '<=', $to); + + $data = $q->get()->map(function ($item) { $item->gross_profit = round($item->total_revenue - $item->total_cost, 2); return $item; }); @@ -109,17 +142,21 @@ public function salesOverTime(): JsonResponse return response()->json($data); } - public function categoryBreakdown(): JsonResponse + public function categoryBreakdown(Request $request): JsonResponse { - $data = DB::table('SL_SALESINVOICETRAN as sl') + [$from, $to] = $this->dateRange($request); + + $q = DB::table('SL_SALESINVOICETRAN as sl') ->join('STK_STOCKITEM as p', 'sl.STOCKCODE', '=', 'p.STOCKCODE') + ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO') ->leftJoin('STK_STOCKCATEGORY as c', 'p.CATEGORY', '=', 'c.STCKCTGRYCDE') ->selectRaw("COALESCE(c.STCKCTGRYDESC, 'Uncategorized') as category, SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER) as revenue, SUM(sl.QTYTOINVOICE * sl.COSTPRICE / sl.COSTPRICEPER) as cost, SUM(sl.QTYTOINVOICE) as qty") ->groupByRaw("COALESCE(c.STCKCTGRYDESC, 'Uncategorized')") - ->orderByDesc('revenue') - ->get(); + ->orderByDesc('revenue'); + if ($from) $q->where('si.DOCDTETME', '>=', $from); + if ($to) $q->where('si.DOCDTETME', '<=', $to); - return response()->json($data); + return response()->json($q->get()); } public function supplierProducts(string $supplierCode): JsonResponse @@ -154,4 +191,61 @@ public function supplierProducts(string $supplierCode): JsonResponse 'timeline' => $timeline, ]); } + + public function productDetails(Request $request): JsonResponse + { + $stockCode = $request->query('code', ''); + if (!$stockCode) { + return response()->json(['error' => 'Missing code parameter'], 400); + } + + $product = DB::table('STK_STOCKITEM as p') + ->leftJoin('STK_STOCKCATEGORY as c', 'p.CATEGORY', '=', 'c.STCKCTGRYCDE') + ->where('p.STOCKCODE', $stockCode) + ->selectRaw("p.STOCKCODE as code, p.DESCRIPTION as name, COALESCE(c.STCKCTGRYDESC, 'Uncategorized') as category") + ->first(); + + // Sales timeline + $salesTimeline = DB::table('SL_SALESINVOICETRAN as sl') + ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO') + ->where('sl.STOCKCODE', $stockCode) + ->whereNotNull('si.DOCDTETME') + ->selectRaw("DATE_FORMAT(si.DOCDTETME, '%Y-%m') as month, SUM(sl.QTYTOINVOICE) as qty_sold, SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER) as revenue, SUM(sl.QTYTOINVOICE * sl.COSTPRICE / sl.COSTPRICEPER) as cost") + ->groupByRaw("DATE_FORMAT(si.DOCDTETME, '%Y-%m')") + ->orderBy('month') + ->get() + ->map(function ($item) { + $item->profit = round($item->revenue - $item->cost, 2); + return $item; + }); + + // Top customers buying this product + $topCustomers = DB::table('SL_SALESINVOICETRAN as sl') + ->join('SL_SALESINVOICE as si', 'sl.REFNO', '=', 'si.REFNO') + ->join('SL_CUSTOMERACCOUNT as ca', 'si.ACCNO', '=', 'ca.DBTRCDE') + ->where('sl.STOCKCODE', $stockCode) + ->selectRaw('ca.DBTRCDE as code, ca.DBTRNME as name, SUM(sl.QTYTOINVOICE) as total_qty, SUM(sl.QTYTOINVOICE * sl.SELLINGPRICE / sl.SELLINGPRICEPER) as total_revenue, COUNT(DISTINCT si.REFNO) as invoice_count') + ->groupBy('ca.DBTRCDE', 'ca.DBTRNME') + ->orderByDesc('total_revenue') + ->limit(15) + ->get(); + + // Suppliers providing this product + $suppliers = DB::table('PL_BILLTRAN as il') + ->join('PL_BILL as i', 'il.REFNO', '=', 'i.REFNO') + ->join('PL_SUPPLIERACCOUNT as sa', 'i.ACCNO', '=', 'sa.SUPLCDE') + ->where('il.STOCKCODE', $stockCode) + ->selectRaw('sa.SUPLCDE as code, sa.SUPLNME as name, SUM(il.QTYTOINVOICE) as total_qty, SUM(il.QTYTOINVOICE * il.COSTPRICE / il.COSTPRICEPER) as total_cost, COUNT(DISTINCT i.REFNO) as invoice_count') + ->groupBy('sa.SUPLCDE', 'sa.SUPLNME') + ->orderByDesc('total_cost') + ->limit(10) + ->get(); + + return response()->json([ + 'product' => $product, + 'sales_timeline' => $salesTimeline, + 'top_customers' => $topCustomers, + 'suppliers' => $suppliers, + ]); + } } diff --git a/src/resources/views/dashboard.blade.php b/src/resources/views/dashboard.blade.php index 03541b2..1bc364f 100644 --- a/src/resources/views/dashboard.blade.php +++ b/src/resources/views/dashboard.blade.php @@ -117,6 +117,17 @@ + +
+ + + + + + + + +
@@ -273,12 +284,12 @@