From 119a1712b05654cd60857ef660e047e86a2ab0de Mon Sep 17 00:00:00 2001 From: Darko Gjorgjijoski Date: Tue, 7 Apr 2026 17:28:34 +0200 Subject: [PATCH] Port expense report grouped itemized view + i18n + return types from master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports the net behaviour from three master commits into v3.0 as a single change, because v3.0 has already diverged structurally (controller moved from V1/Admin/Report to Company/Report, blade has its own CSS rework using the bundled fonts partial, and v3.0's App\Facades\Pdf replaces Barryvdh\DomPDF\Facade\Pdf). The three source commits are: 834b53ea (grouped itemized expenses), e22050bc (DomPDF facade + Pint — adapted to v3.0's App\Facades\Pdf), 0e9f18d4 (expenses.uncategorized + pdf_expense_group_total_label i18n keys + View|Response return type). Controller: replaces the expenseCategories aggregate fetch with an itemized Expense query ordered by date, groups by category name with expenses.uncategorized fallback, and shares an expenseGroups collection of {name, expenses, total} plus the overall totalExpense. Adds expense_category_id to applyFilters. Updates the docblock return type from JsonResponse to View|Response. Keeps v3.0's App\Facades\Pdf. Blade: replaces the single expenseCategories aggregate table with a per-group itemized table (date / note / amount columns + per-group total line using the new pdf_expense_group_total_label i18n key). Adds the item-table-* CSS classes and removes the old expense-total-table bottom block. lang/en.json: adds expenses.uncategorized = "Uncategorized" and pdf_expense_group_total_label = "Group total:". --- .../Report/ExpensesReportController.php | 31 ++++-- lang/en.json | 2 + .../views/app/pdf/reports/expenses.blade.php | 98 ++++++++++++++----- 3 files changed, 98 insertions(+), 33 deletions(-) diff --git a/app/Http/Controllers/Company/Report/ExpensesReportController.php b/app/Http/Controllers/Company/Report/ExpensesReportController.php index a20bac06..3743af64 100644 --- a/app/Http/Controllers/Company/Report/ExpensesReportController.php +++ b/app/Http/Controllers/Company/Report/ExpensesReportController.php @@ -9,8 +9,9 @@ use App\Models\CompanySetting; use App\Models\Currency; use App\Models\Expense; use Carbon\Carbon; -use Illuminate\Http\JsonResponse; +use Illuminate\Contracts\View\View; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Support\Facades\App; class ExpensesReportController extends Controller @@ -19,7 +20,7 @@ class ExpensesReportController extends Controller * Handle the incoming request. * * @param string $hash - * @return JsonResponse + * @return View|Response */ public function __invoke(Request $request, $hash) { @@ -31,14 +32,26 @@ class ExpensesReportController extends Controller App::setLocale($locale); - $expenseCategories = Expense::with('category') + // Fetch individual expenses (filtered and ordered by date), then group by category + $expenses = Expense::with('category') ->whereCompanyId($company->id) - ->applyFilters($request->only(['from_date', 'to_date'])) - ->expensesAttributes() + ->applyFilters($request->only(['from_date', 'to_date', 'expense_category_id'])) + ->orderBy('expense_date', 'asc') ->get(); - $totalAmount = 0; - foreach ($expenseCategories as $category) { - $totalAmount += $category->total_amount; + + $totalAmount = $expenses->sum('base_amount'); + + $grouped = $expenses->groupBy(function ($item) { + return $item->category ? $item->category->name : trans('expenses.uncategorized'); + }); + + $expenseGroups = collect(); + foreach ($grouped as $categoryName => $group) { + $expenseGroups->push([ + 'name' => $categoryName, + 'expenses' => $group, + 'total' => $group->sum('base_amount'), + ]); } $dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id); @@ -62,7 +75,7 @@ class ExpensesReportController extends Controller ->get(); view()->share([ - 'expenseCategories' => $expenseCategories, + 'expenseGroups' => $expenseGroups, 'colorSettings' => $colorSettings, 'totalExpense' => $totalAmount, 'company' => $company, diff --git a/lang/en.json b/lang/en.json index 4c8af13f..d645c39d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -675,6 +675,7 @@ "currency": "Currency", "contact": "Contact", "category": "Category", + "uncategorized": "Uncategorized", "from_date": "From Date", "to_date": "To Date", "expense_date": "Date", @@ -1722,6 +1723,7 @@ "pdf_total_tax_label": "TOTAL TAX", "pdf_tax_types_label": "Tax Types", "pdf_expenses_label": "Expenses", + "pdf_expense_group_total_label": "Group total:", "pdf_bill_to": "Bill to,", "pdf_ship_to": "Ship to,", "pdf_received_from": "Received from:", diff --git a/resources/views/app/pdf/reports/expenses.blade.php b/resources/views/app/pdf/reports/expenses.blade.php index 22c40468..c8f4d0fb 100644 --- a/resources/views/app/pdf/reports/expenses.blade.php +++ b/resources/views/app/pdf/reports/expenses.blade.php @@ -134,6 +134,62 @@ line-height: 21px; color: #5851D8; } + + /* -- Items Table (grouped itemized expenses) -- */ + + .item-table-heading-left { + font-size: 13.5px; + text-align: left; + color: rgba(0, 0, 0, 0.85); + padding: 5px; + padding-bottom: 10px; + } + + .item-table-heading-right { + font-size: 13.5px; + text-align: right; + color: rgba(0, 0, 0, 0.85); + padding: 5px; + padding-bottom: 10px; + } + + tr.item-table-heading-row th { + border-bottom: 0.620315px solid #E8E8E8; + font-size: 12px; + line-height: 18px; + } + + tr.item-row td { + font-size: 12px; + line-height: 18px; + } + + .item-cell-left { + font-size: 13px; + color: #040405; + text-align: left; + padding: 5px; + padding-top: 10px; + border-color: #d9d9d9; + } + + .item-cell-right { + font-size: 13px; + color: #040405; + text-align: right; + padding: 5px; + padding-top: 10px; + border-color: #d9d9d9; + } + + .item-table-group-total { + font-size: 14px; + font-weight: bold; + text-align: right; + color: rgba(0, 0, 0, 0.85); + padding: 5px; + padding-bottom: 10px; + } @@ -156,33 +212,27 @@

@lang('pdf_expenses_label')

-
- - @foreach ($expenseCategories as $expenseCategory) - - - + @foreach ($expenseGroups as $group) +

{{ $group['name'] }}

+
-

- {{ $expenseCategory->category->name }} -

-
-

- {!! format_money_pdf($expenseCategory->total_amount, $currency) !!} -

-
+ + + + + + @foreach ($group['expenses'] as $expense) + + + + @endforeach -
@lang('expenses.date')@lang('expenses.note')@lang('expenses.amount')
{{ $expense->formatted_expense_date }}{{ $expense->notes ? $expense->notes : '-' }}{!! format_money_pdf($expense->base_amount, $currency) !!}
-
+ +
+

@lang('pdf_expense_group_total_label')   {!! format_money_pdf($group['total'], $currency) !!}

+
+ @endforeach - - - - - -
-

{!! format_money_pdf($totalExpense, $currency) !!}

-