Andrew Wallo 1 år sedan
förälder
incheckning
5756649ce3
44 ändrade filer med 1543 tillägg och 611 borttagningar
  1. 1
    5
      app/Contracts/ExportableReport.php
  2. 1
    2
      app/DTO/AccountBalanceDTO.php
  3. 1
    2
      app/DTO/AccountCategoryDTO.php
  4. 1
    2
      app/DTO/AccountDTO.php
  5. 1
    2
      app/DTO/ReportCategoryDTO.php
  6. 4
    2
      app/DTO/ReportDTO.php
  7. 1
    2
      app/Events/CompanyConfigured.php
  8. 1
    2
      app/Events/CompanyDefaultEvent.php
  9. 1
    2
      app/Events/CompanyDefaultUpdated.php
  10. 1
    2
      app/Events/CompanyGenerated.php
  11. 1
    2
      app/Events/CurrencyRateChanged.php
  12. 1
    2
      app/Events/DefaultCurrencyChanged.php
  13. 1
    2
      app/Events/PlaidSuccess.php
  14. 1
    2
      app/Events/StartTransactionImport.php
  15. 2
    4
      app/Filament/Company/Pages/Accounting/Transactions.php
  16. 59
    27
      app/Filament/Company/Pages/Reports/AccountBalances.php
  17. 147
    11
      app/Filament/Company/Pages/Reports/BaseReportPage.php
  18. 44
    12
      app/Filament/Company/Pages/Reports/TrialBalance.php
  19. 1
    2
      app/Jobs/ProcessTransactionImport.php
  20. 1
    2
      app/Jobs/ProcessTransactionUpdate.php
  21. 1
    2
      app/Listeners/CreateConnectedAccount.php
  22. 1
    2
      app/Listeners/HandleTransactionImport.php
  23. 1
    2
      app/Listeners/UpdateCurrencyRates.php
  24. 2
    0
      app/Models/Company.php
  25. 1
    3
      app/Providers/CurrencyServiceProvider.php
  26. 1
    4
      app/Providers/SquireServiceProvider.php
  27. 1
    2
      app/Services/AccountService.php
  28. 1
    2
      app/Services/ConnectedBankAccountService.php
  29. 1
    2
      app/Services/CurrencyService.php
  30. 45
    45
      app/Services/ReportService.php
  31. 43
    0
      app/Support/Column.php
  32. 45
    75
      app/Transformers/AccountBalanceReportTransformer.php
  33. 11
    12
      app/Transformers/BaseReportTransformer.php
  34. 55
    37
      app/Transformers/TrialBalanceReportTransformer.php
  35. 1
    2
      app/ValueObjects/Money.php
  36. 1
    2
      app/View/Models/InvoiceViewModel.php
  37. 2
    1
      composer.json
  38. 965
    225
      composer.lock
  39. 30
    20
      package-lock.json
  40. 7
    7
      package.json
  41. 32
    32
      resources/views/components/company/tables/reports/detailed-report.blade.php
  42. 0
    22
      resources/views/filament/company/pages/reports/account-balances.blade.php
  43. 25
    0
      resources/views/filament/company/pages/reports/detailed-report.blade.php
  44. 0
    23
      resources/views/filament/company/pages/reports/trial-balance.blade.php

+ 1
- 5
app/Contracts/ExportableReport.php Visa fil

@@ -17,9 +17,5 @@ interface ExportableReport
17 17
 
18 18
     public function getOverallTotals(): array;
19 19
 
20
-    public function getRightAlignedColumns(): array;
21
-
22
-    public function getLeftAlignedColumns(): array;
23
-
24
-    public function getCenterAlignedColumns(): array;
20
+    public function getColumns(): array;
25 21
 }

+ 1
- 2
app/DTO/AccountBalanceDTO.php Visa fil

@@ -12,8 +12,7 @@ class AccountBalanceDTO implements Wireable
12 12
         public string $creditBalance,
13 13
         public ?string $netMovement,
14 14
         public ?string $endingBalance,
15
-    ) {
16
-    }
15
+    ) {}
17 16
 
18 17
     public function toLivewire(): array
19 18
     {

+ 1
- 2
app/DTO/AccountCategoryDTO.php Visa fil

@@ -12,8 +12,7 @@ class AccountCategoryDTO implements Wireable
12 12
     public function __construct(
13 13
         public array $accounts,
14 14
         public AccountBalanceDTO $summary,
15
-    ) {
16
-    }
15
+    ) {}
17 16
 
18 17
     public function toLivewire(): array
19 18
     {

+ 1
- 2
app/DTO/AccountDTO.php Visa fil

@@ -10,8 +10,7 @@ class AccountDTO implements Wireable
10 10
         public string $accountName,
11 11
         public string $accountCode,
12 12
         public AccountBalanceDTO $balance,
13
-    ) {
14
-    }
13
+    ) {}
15 14
 
16 15
     public function toLivewire(): array
17 16
     {

+ 1
- 2
app/DTO/ReportCategoryDTO.php Visa fil

@@ -13,6 +13,5 @@ class ReportCategoryDTO
13 13
         public array $header,
14 14
         public array $data,
15 15
         public array $summary,
16
-    ) {
17
-    }
16
+    ) {}
18 17
 }

+ 4
- 2
app/DTO/ReportDTO.php Visa fil

@@ -12,14 +12,15 @@ class ReportDTO implements Wireable
12 12
          */
13 13
         public array $categories,
14 14
         public AccountBalanceDTO $overallTotal,
15
-    ) {
16
-    }
15
+        public array $fields,
16
+    ) {}
17 17
 
18 18
     public function toLivewire(): array
19 19
     {
20 20
         return [
21 21
             'categories' => $this->categories,
22 22
             'overallTotal' => $this->overallTotal->toLivewire(),
23
+            'fields' => $this->fields,
23 24
         ];
24 25
     }
25 26
 
@@ -28,6 +29,7 @@ class ReportDTO implements Wireable
28 29
         return new static(
29 30
             $value['categories'],
30 31
             AccountBalanceDTO::fromLivewire($value['overallTotal']),
32
+            $value['fields'],
31 33
         );
32 34
     }
33 35
 }

+ 1
- 2
app/Events/CompanyConfigured.php Visa fil

@@ -15,6 +15,5 @@ class CompanyConfigured
15 15
 
16 16
     public function __construct(
17 17
         public Company $company
18
-    ) {
19
-    }
18
+    ) {}
20 19
 }

+ 1
- 2
app/Events/CompanyDefaultEvent.php Visa fil

@@ -18,6 +18,5 @@ class CompanyDefaultEvent
18 18
      */
19 19
     public function __construct(
20 20
         public Model $model
21
-    ) {
22
-    }
21
+    ) {}
23 22
 }

+ 1
- 2
app/Events/CompanyDefaultUpdated.php Visa fil

@@ -19,6 +19,5 @@ class CompanyDefaultUpdated
19 19
     public function __construct(
20 20
         public Model $record,
21 21
         public array $data
22
-    ) {
23
-    }
22
+    ) {}
24 23
 }

+ 1
- 2
app/Events/CompanyGenerated.php Visa fil

@@ -21,6 +21,5 @@ class CompanyGenerated
21 21
         public string $country,
22 22
         public string $language = 'en',
23 23
         public string $currency = 'USD'
24
-    ) {
25
-    }
24
+    ) {}
26 25
 }

+ 1
- 2
app/Events/CurrencyRateChanged.php Visa fil

@@ -17,6 +17,5 @@ class CurrencyRateChanged
17 17
         public Currency $currency,
18 18
         public float $oldRate,
19 19
         public float $newRate
20
-    ) {
21
-    }
20
+    ) {}
22 21
 }

+ 1
- 2
app/Events/DefaultCurrencyChanged.php Visa fil

@@ -18,6 +18,5 @@ class DefaultCurrencyChanged
18 18
      */
19 19
     public function __construct(
20 20
         public Currency $currency
21
-    ) {
22
-    }
21
+    ) {}
23 22
 }

+ 1
- 2
app/Events/PlaidSuccess.php Visa fil

@@ -18,6 +18,5 @@ class PlaidSuccess
18 18
         public string $publicToken,
19 19
         public string $accessToken,
20 20
         public Company $company
21
-    ) {
22
-    }
21
+    ) {}
23 22
 }

+ 1
- 2
app/Events/StartTransactionImport.php Visa fil

@@ -20,6 +20,5 @@ class StartTransactionImport
20 20
         public ConnectedBankAccount $connectedBankAccount,
21 21
         public int | string $selectedBankAccountId,
22 22
         public string $startDate
23
-    ) {
24
-    }
23
+    ) {}
25 24
 }

+ 2
- 4
app/Filament/Company/Pages/Accounting/Transactions.php Visa fil

@@ -258,7 +258,7 @@ class Transactions extends Page implements HasTable
258 258
                             ->schema([
259 259
                                 Select::make('account_id')
260 260
                                     ->label('Category')
261
-                                    ->options(fn () => $this->getChartAccountOptions(nominalAccountsOnly: true, onlyWithTransactions: true))
261
+                                    ->options(fn () => $this->getChartAccountOptions(nominalAccountsOnly: true))
262 262
                                     ->multiple()
263 263
                                     ->searchable(),
264 264
                                 Select::make('reviewed')
@@ -713,9 +713,8 @@ class Transactions extends Page implements HasTable
713 713
         return 'uncategorized';
714 714
     }
715 715
 
716
-    protected function getChartAccountOptions(?TransactionType $type = null, ?bool $nominalAccountsOnly = null, ?bool $onlyWithTransactions = null, ?int $currentAccountId = null): array
716
+    protected function getChartAccountOptions(?TransactionType $type = null, ?bool $nominalAccountsOnly = null, ?int $currentAccountId = null): array
717 717
     {
718
-        $onlyWithTransactions ??= false;
719 718
         $nominalAccountsOnly ??= false;
720 719
 
721 720
         $excludedCategory = match ($type) {
@@ -727,7 +726,6 @@ class Transactions extends Page implements HasTable
727 726
         return Account::query()
728 727
             ->when($nominalAccountsOnly, fn (Builder $query) => $query->doesntHave('bankAccount'))
729 728
             ->when($excludedCategory, fn (Builder $query) => $query->whereNot('category', $excludedCategory))
730
-            ->when($onlyWithTransactions, fn (Builder $query) => $query->has('transactions'))
731 729
             ->where(function (Builder $query) use ($currentAccountId) {
732 730
                 $query->where('archived', false)
733 731
                     ->orWhere('id', $currentAccountId);

+ 59
- 27
app/Filament/Company/Pages/Reports/AccountBalances.php Visa fil

@@ -3,20 +3,19 @@
3 3
 namespace App\Filament\Company\Pages\Reports;
4 4
 
5 5
 use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
6 7
 use App\Services\ExportService;
7 8
 use App\Services\ReportService;
9
+use App\Support\Column;
8 10
 use App\Transformers\AccountBalanceReportTransformer;
9
-use Filament\Forms\Components\Checkbox;
10
-use Filament\Forms\Components\CheckboxList;
11
-use Filament\Forms\Components\Grid;
12
-use Filament\Forms\Components\Split;
13 11
 use Filament\Forms\Form;
12
+use Filament\Support\Enums\Alignment;
14 13
 use Guava\FilamentClusters\Forms\Cluster;
15 14
 use Symfony\Component\HttpFoundation\StreamedResponse;
16 15
 
17 16
 class AccountBalances extends BaseReportPage
18 17
 {
19
-    protected static string $view = 'filament.company.pages.reports.account-balances';
18
+    protected static string $view = 'filament.company.pages.reports.detailed-report';
20 19
 
21 20
     protected static ?string $slug = 'reports/account-balances';
22 21
 
@@ -24,8 +23,6 @@ class AccountBalances extends BaseReportPage
24 23
 
25 24
     protected ReportService $reportService;
26 25
 
27
-    public ExportableReport $accountBalanceReport;
28
-
29 26
     protected ExportService $exportService;
30 27
 
31 28
     public function boot(ReportService $reportService, ExportService $exportService): void
@@ -34,42 +31,77 @@ class AccountBalances extends BaseReportPage
34 31
         $this->exportService = $exportService;
35 32
     }
36 33
 
37
-    public function loadReportData(): void
34
+    /**
35
+     * @return array<Column>
36
+     */
37
+    public function getTable(): array
38 38
     {
39
-        $reportDTO = $this->reportService->buildAccountBalanceReport($this->startDate, $this->endDate);
40
-        $options = array_fill_keys($this->options, true);
41
-        $this->accountBalanceReport = new AccountBalanceReportTransformer($reportDTO, $options);
39
+        return [
40
+            Column::make('account_code')
41
+                ->label('Account Code')
42
+                ->toggleable()
43
+                ->alignment(Alignment::Center),
44
+            Column::make('account_name')
45
+                ->label('Account')
46
+                ->alignment(Alignment::Left),
47
+            Column::make('starting_balance')
48
+                ->label('Starting Balance')
49
+                ->toggleable()
50
+                ->alignment(Alignment::Right),
51
+            Column::make('debit_balance')
52
+                ->label('Debit')
53
+                ->toggleable()
54
+                ->alignment(Alignment::Right),
55
+            Column::make('credit_balance')
56
+                ->label('Credit')
57
+                ->toggleable()
58
+                ->alignment(Alignment::Right),
59
+            Column::make('net_movement')
60
+                ->label('Net Movement')
61
+                ->toggleable()
62
+                ->alignment(Alignment::Right),
63
+            Column::make('ending_balance')
64
+                ->label('Ending Balance')
65
+                ->toggleable()
66
+                ->alignment(Alignment::Right),
67
+        ];
42 68
     }
43 69
 
44 70
     public function form(Form $form): Form
45 71
     {
46 72
         return $form
47 73
             ->inlineLabel()
74
+            ->columns([
75
+                'lg' => 1,
76
+                '2xl' => 2,
77
+            ])
78
+            ->live()
48 79
             ->schema([
49
-                Split::make([
50
-                    $this->getDateRangeFormComponent(),
51
-                    Cluster::make([
52
-                        $this->getStartDateFormComponent(),
53
-                        $this->getEndDateFormComponent(),
54
-                    ])
55
-                        ->hiddenLabel(),
56
-                ])->live(),
57
-                CheckboxList::make('options')
58
-                    ->options([
59
-                        'showAccountCode' => 'Show Account Code',
60
-                        'showZeroBalances' => 'Show Zero Balances',
61
-                    ])
62
-                    ->columns(2),
80
+                $this->getDateRangeFormComponent(),
81
+                Cluster::make([
82
+                    $this->getStartDateFormComponent(),
83
+                    $this->getEndDateFormComponent(),
84
+                ])->hiddenLabel(),
63 85
             ]);
64 86
     }
65 87
 
88
+    protected function buildReport(array $columns): ReportDTO
89
+    {
90
+        return $this->reportService->buildAccountBalanceReport($this->startDate, $this->endDate, $columns);
91
+    }
92
+
93
+    protected function getTransformer(ReportDTO $reportDTO): ExportableReport
94
+    {
95
+        return new AccountBalanceReportTransformer($reportDTO);
96
+    }
97
+
66 98
     public function exportCSV(): StreamedResponse
67 99
     {
68
-        return $this->exportService->exportToCsv($this->company, $this->accountBalanceReport, $this->startDate, $this->endDate);
100
+        return $this->exportService->exportToCsv($this->company, $this->report, $this->startDate, $this->endDate);
69 101
     }
70 102
 
71 103
     public function exportPDF(): StreamedResponse
72 104
     {
73
-        return $this->exportService->exportToPdf($this->company, $this->accountBalanceReport, $this->startDate, $this->endDate);
105
+        return $this->exportService->exportToPdf($this->company, $this->report, $this->startDate, $this->endDate);
74 106
     }
75 107
 }

+ 147
- 11
app/Filament/Company/Pages/Reports/BaseReportPage.php Visa fil

@@ -2,25 +2,37 @@
2 2
 
3 3
 namespace App\Filament\Company\Pages\Reports;
4 4
 
5
+use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
5 7
 use App\Filament\Forms\Components\DateRangeSelect;
6 8
 use App\Models\Company;
9
+use App\Support\Column;
7 10
 use Filament\Actions\Action;
8 11
 use Filament\Actions\ActionGroup;
12
+use Filament\Forms\Components\Checkbox;
9 13
 use Filament\Forms\Components\Component;
10 14
 use Filament\Forms\Components\DatePicker;
15
+use Filament\Forms\Form;
11 16
 use Filament\Forms\Set;
12 17
 use Filament\Pages\Page;
18
+use Filament\Support\Enums\ActionSize;
13 19
 use Filament\Support\Enums\IconPosition;
14 20
 use Filament\Support\Enums\IconSize;
21
+use Filament\Support\Facades\FilamentIcon;
15 22
 use Illuminate\Support\Carbon;
23
+use Livewire\Attributes\Computed;
24
+use Livewire\Attributes\Session;
16 25
 use Symfony\Component\HttpFoundation\StreamedResponse;
17 26
 
18 27
 abstract class BaseReportPage extends Page
19 28
 {
29
+    #[Session]
20 30
     public string $startDate = '';
21 31
 
32
+    #[Session]
22 33
     public string $endDate = '';
23 34
 
35
+    #[Session]
24 36
     public string $dateRange = '';
25 37
 
26 38
     public string $fiscalYearStartDate = '';
@@ -29,35 +41,153 @@ abstract class BaseReportPage extends Page
29 41
 
30 42
     public Company $company;
31 43
 
32
-    public array $options = [];
44
+    #[Session]
45
+    public array $toggledTableColumns = [];
46
+
47
+    abstract protected function buildReport(array $columns): ReportDTO;
48
+
49
+    abstract public function exportCSV(): StreamedResponse;
50
+
51
+    abstract public function exportPDF(): StreamedResponse;
52
+
53
+    abstract protected function getTransformer(ReportDTO $reportDTO): ExportableReport;
54
+
55
+    /**
56
+     * @return array<Column>
57
+     */
58
+    abstract public function getTable(): array;
33 59
 
34 60
     public function mount(): void
61
+    {
62
+        $this->initializeProperties();
63
+
64
+        $this->loadDefaultDateRange();
65
+
66
+        $this->loadReportData();
67
+
68
+        $this->loadDefaultTableColumnToggleState();
69
+    }
70
+
71
+    protected function getForms(): array
72
+    {
73
+        return [
74
+            'toggleTableColumnForm',
75
+            'form',
76
+        ];
77
+    }
78
+
79
+    protected function initializeProperties(): void
35 80
     {
36 81
         $this->company = auth()->user()->currentCompany;
37 82
         $this->fiscalYearStartDate = $this->company->locale->fiscalYearStartDate();
38 83
         $this->fiscalYearEndDate = $this->company->locale->fiscalYearEndDate();
39
-        $this->dateRange = $this->getDefaultDateRange();
40
-        $this->setDateRange(Carbon::parse($this->fiscalYearStartDate), Carbon::parse($this->fiscalYearEndDate));
41
-        $this->options = ['showAccountCode'];
42
-
43
-        $this->loadReportData();
44 84
     }
45 85
 
46
-    abstract public function loadReportData(): void;
86
+    protected function loadDefaultDateRange(): void
87
+    {
88
+        if (empty($this->dateRange)) {
89
+            $this->dateRange = $this->getDefaultDateRange();
90
+            $this->setDateRange(Carbon::parse($this->fiscalYearStartDate), Carbon::parse($this->fiscalYearEndDate));
91
+        }
92
+    }
47 93
 
48
-    abstract public function exportCSV(): StreamedResponse;
94
+    public function loadReportData(): void
95
+    {
96
+        unset($this->report);
97
+    }
49 98
 
50
-    abstract public function exportPDF(): StreamedResponse;
99
+    protected function loadDefaultTableColumnToggleState(): void
100
+    {
101
+        $tableColumns = $this->getTable();
102
+
103
+        if (empty($this->toggledTableColumns)) {
104
+            foreach ($tableColumns as $column) {
105
+                if ($column->isToggleable()) {
106
+                    if ($column->isToggledHiddenByDefault()) {
107
+                        $this->toggledTableColumns[$column->getName()] = false;
108
+                    } else {
109
+                        $this->toggledTableColumns[$column->getName()] = true;
110
+                    }
111
+                } else {
112
+                    $this->toggledTableColumns[$column->getName()] = true;
113
+                }
114
+            }
115
+        }
116
+
117
+        foreach ($tableColumns as $column) {
118
+            $columnName = $column->getName();
119
+            if (! $column->isToggleable()) {
120
+                $this->toggledTableColumns[$columnName] = true;
121
+            }
122
+
123
+            if ($column->isToggleable() && $column->isToggledHiddenByDefault() && isset($this->toggledTableColumns[$columnName]) && $this->toggledTableColumns[$columnName]) {
124
+                $this->toggledTableColumns[$columnName] = false;
125
+            }
126
+        }
127
+    }
51 128
 
52 129
     public function getDefaultDateRange(): string
53 130
     {
54 131
         return 'FY-' . now()->year;
55 132
     }
56 133
 
134
+    protected function getToggledColumns(): array
135
+    {
136
+        return array_values(
137
+            array_filter(
138
+                $this->getTable(),
139
+                fn (Column $column) => $this->toggledTableColumns[$column->getName()] ?? false,
140
+            )
141
+        );
142
+    }
143
+
144
+    #[Computed(persist: true)]
145
+    public function report(): ExportableReport
146
+    {
147
+        $columns = $this->getToggledColumns();
148
+        $reportDTO = $this->buildReport($columns);
149
+
150
+        return $this->getTransformer($reportDTO);
151
+    }
152
+
57 153
     public function setDateRange(Carbon $start, Carbon $end): void
58 154
     {
59
-        $this->startDate = $start->format('Y-m-d');
60
-        $this->endDate = $end->isFuture() ? now()->format('Y-m-d') : $end->format('Y-m-d');
155
+        $this->startDate = $start->toDateString();
156
+        $this->endDate = $end->isFuture() ? now()->toDateString() : $end->toDateString();
157
+    }
158
+
159
+    public function toggleColumnsAction(): Action
160
+    {
161
+        return Action::make('toggleColumns')
162
+            ->label(__('filament-tables::table.actions.toggle_columns.label'))
163
+            ->iconButton()
164
+            ->size(ActionSize::Large)
165
+            ->icon(FilamentIcon::resolve('tables::actions.toggle-columns') ?? 'heroicon-m-view-columns')
166
+            ->color('gray');
167
+    }
168
+
169
+    public function toggleTableColumnForm(Form $form): Form
170
+    {
171
+        return $form
172
+            ->schema($this->getTableColumnToggleFormSchema())
173
+            ->statePath('toggledTableColumns');
174
+    }
175
+
176
+    /**
177
+     * @return array<Checkbox>
178
+     */
179
+    protected function getTableColumnToggleFormSchema(): array
180
+    {
181
+        $schema = [];
182
+
183
+        foreach ($this->getTable() as $column) {
184
+            if ($column->isToggleable()) {
185
+                $schema[] = Checkbox::make($column->getName())
186
+                    ->label($column->getLabel());
187
+            }
188
+        }
189
+
190
+        return $schema;
61 191
     }
62 192
 
63 193
     protected function getHeaderActions(): array
@@ -91,6 +221,12 @@ abstract class BaseReportPage extends Page
91 221
             ->endDateField('endDate');
92 222
     }
93 223
 
224
+    protected function resetDateRange(): void
225
+    {
226
+        $this->dateRange = $this->getDefaultDateRange();
227
+        $this->setDateRange(Carbon::parse($this->fiscalYearStartDate), Carbon::parse($this->fiscalYearEndDate));
228
+    }
229
+
94 230
     protected function getStartDateFormComponent(): Component
95 231
     {
96 232
         return DatePicker::make('startDate')

+ 44
- 12
app/Filament/Company/Pages/Reports/TrialBalance.php Visa fil

@@ -3,16 +3,19 @@
3 3
 namespace App\Filament\Company\Pages\Reports;
4 4
 
5 5
 use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
6 7
 use App\Services\ExportService;
7 8
 use App\Services\ReportService;
9
+use App\Support\Column;
8 10
 use App\Transformers\TrialBalanceReportTransformer;
9
-use Filament\Forms\Components\Split;
10 11
 use Filament\Forms\Form;
12
+use Filament\Support\Enums\Alignment;
13
+use Guava\FilamentClusters\Forms\Cluster;
11 14
 use Symfony\Component\HttpFoundation\StreamedResponse;
12 15
 
13 16
 class TrialBalance extends BaseReportPage
14 17
 {
15
-    protected static string $view = 'filament.company.pages.reports.trial-balance';
18
+    protected static string $view = 'filament.company.pages.reports.detailed-report';
16 19
 
17 20
     protected static ?string $slug = 'reports/trial-balance';
18 21
 
@@ -20,8 +23,6 @@ class TrialBalance extends BaseReportPage
20 23
 
21 24
     protected ReportService $reportService;
22 25
 
23
-    public ExportableReport $trialBalanceReport;
24
-
25 26
     protected ExportService $exportService;
26 27
 
27 28
     public function boot(ReportService $reportService, ExportService $exportService): void
@@ -30,31 +31,62 @@ class TrialBalance extends BaseReportPage
30 31
         $this->exportService = $exportService;
31 32
     }
32 33
 
33
-    public function loadReportData(): void
34
+    public function getTable(): array
34 35
     {
35
-        $reportDTO = $this->reportService->buildTrialBalanceReport($this->startDate, $this->endDate);
36
-        $this->trialBalanceReport = new TrialBalanceReportTransformer($reportDTO);
36
+        return [
37
+            Column::make('account_code')
38
+                ->label('Account Code')
39
+                ->toggleable()
40
+                ->alignment(Alignment::Center),
41
+            Column::make('account_name')
42
+                ->label('Account')
43
+                ->alignment(Alignment::Left),
44
+            Column::make('debit_balance')
45
+                ->label('Debit')
46
+                ->toggleable()
47
+                ->alignment(Alignment::Right),
48
+            Column::make('credit_balance')
49
+                ->label('Credit')
50
+                ->toggleable()
51
+                ->alignment(Alignment::Right),
52
+        ];
37 53
     }
38 54
 
39 55
     public function form(Form $form): Form
40 56
     {
41 57
         return $form
58
+            ->inlineLabel()
59
+            ->columns([
60
+                'lg' => 1,
61
+                '2xl' => 2,
62
+            ])
63
+            ->live()
42 64
             ->schema([
43
-                Split::make([
44
-                    $this->getDateRangeFormComponent(),
65
+                $this->getDateRangeFormComponent(),
66
+                Cluster::make([
45 67
                     $this->getStartDateFormComponent(),
46 68
                     $this->getEndDateFormComponent(),
47
-                ])->live(),
69
+                ])->hiddenLabel(),
48 70
             ]);
49 71
     }
50 72
 
73
+    protected function buildReport(array $columns): ReportDTO
74
+    {
75
+        return $this->reportService->buildTrialBalanceReport($this->startDate, $this->endDate, $columns);
76
+    }
77
+
78
+    protected function getTransformer(ReportDTO $reportDTO): ExportableReport
79
+    {
80
+        return new TrialBalanceReportTransformer($reportDTO);
81
+    }
82
+
51 83
     public function exportCSV(): StreamedResponse
52 84
     {
53
-        return $this->exportService->exportToCsv($this->company, $this->trialBalanceReport, $this->startDate, $this->endDate);
85
+        return $this->exportService->exportToCsv($this->company, $this->report, $this->startDate, $this->endDate);
54 86
     }
55 87
 
56 88
     public function exportPDF(): StreamedResponse
57 89
     {
58
-        return $this->exportService->exportToPdf($this->company, $this->trialBalanceReport, $this->startDate, $this->endDate);
90
+        return $this->exportService->exportToPdf($this->company, $this->report, $this->startDate, $this->endDate);
59 91
     }
60 92
 }

+ 1
- 2
app/Jobs/ProcessTransactionImport.php Visa fil

@@ -28,8 +28,7 @@ class ProcessTransactionImport implements ShouldQueue
28 28
         protected BankAccount $bankAccount,
29 29
         protected ConnectedBankAccount $connectedBankAccount,
30 30
         protected string $startDate
31
-    ) {
32
-    }
31
+    ) {}
33 32
 
34 33
     /**
35 34
      * Execute the job.

+ 1
- 2
app/Jobs/ProcessTransactionUpdate.php Visa fil

@@ -23,8 +23,7 @@ class ProcessTransactionUpdate implements ShouldQueue
23 23
     public function __construct(
24 24
         protected Company $company,
25 25
         protected string $itemId
26
-    ) {
27
-    }
26
+    ) {}
28 27
 
29 28
     /**
30 29
      * Execute the job.

+ 1
- 2
app/Listeners/CreateConnectedAccount.php Visa fil

@@ -15,8 +15,7 @@ class CreateConnectedAccount
15 15
      */
16 16
     public function __construct(
17 17
         protected PlaidService $plaidService
18
-    ) {
19
-    }
18
+    ) {}
20 19
 
21 20
     /**
22 21
      * Handle the event.

+ 1
- 2
app/Listeners/HandleTransactionImport.php Visa fil

@@ -14,8 +14,7 @@ class HandleTransactionImport
14 14
      */
15 15
     public function __construct(
16 16
         protected ConnectedBankAccountService $connectedBankAccountService
17
-    ) {
18
-    }
17
+    ) {}
19 18
 
20 19
     /**
21 20
      * Handle the event.

+ 1
- 2
app/Listeners/UpdateCurrencyRates.php Visa fil

@@ -15,8 +15,7 @@ readonly class UpdateCurrencyRates
15 15
      */
16 16
     public function __construct(
17 17
         private CurrencyHandler $currencyService
18
-    ) {
19
-    }
18
+    ) {}
20 19
 
21 20
     /**
22 21
      * Handle the event.

+ 2
- 0
app/Models/Company.php Visa fil

@@ -54,6 +54,8 @@ class Company extends FilamentCompaniesCompany implements HasAvatar
54 54
      * @var array<string, class-string>
55 55
      */
56 56
     protected $dispatchesEvents = [
57
+        'created' => CompanyCreated::class,
58
+        'updated' => CompanyUpdated::class,
57 59
         'deleted' => CompanyDeleted::class,
58 60
     ];
59 61
 

+ 1
- 3
app/Providers/CurrencyServiceProvider.php Visa fil

@@ -21,7 +21,5 @@ class CurrencyServiceProvider extends ServiceProvider
21 21
         });
22 22
     }
23 23
 
24
-    public function boot(): void
25
-    {
26
-    }
24
+    public function boot(): void {}
27 25
 }

+ 1
- 4
app/Providers/SquireServiceProvider.php Visa fil

@@ -10,10 +10,7 @@ use Squire\Repository;
10 10
 
11 11
 class SquireServiceProvider extends ServiceProvider
12 12
 {
13
-    public function register(): void
14
-    {
15
-
16
-    }
13
+    public function register(): void {}
17 14
 
18 15
     public function boot(): void
19 16
     {

+ 1
- 2
app/Services/AccountService.php Visa fil

@@ -15,8 +15,7 @@ class AccountService implements AccountHandler
15 15
 {
16 16
     public function __construct(
17 17
         protected JournalEntryRepository $journalEntryRepository
18
-    ) {
19
-    }
18
+    ) {}
20 19
 
21 20
     public function getDebitBalance(Account $account, string $startDate, string $endDate): Money
22 21
     {

+ 1
- 2
app/Services/ConnectedBankAccountService.php Visa fil

@@ -14,8 +14,7 @@ class ConnectedBankAccountService
14 14
     public function __construct(
15 15
         protected AccountSubtypeRepository $accountSubtypeRepository,
16 16
         protected ConnectedBankAccountRepository $connectedBankAccountRepository
17
-    ) {
18
-    }
17
+    ) {}
19 18
 
20 19
     public function getOrProcessBankAccountForConnectedBankAccount(Company $company, ConnectedBankAccount $connectedBankAccount, int | string $selectedBankAccountId): BankAccount
21 20
     {

+ 1
- 2
app/Services/CurrencyService.php Visa fil

@@ -14,8 +14,7 @@ class CurrencyService implements CurrencyHandler
14 14
         protected ?string $apiKey,
15 15
         protected ?string $baseUrl,
16 16
         protected Client $client
17
-    ) {
18
-    }
17
+    ) {}
19 18
 
20 19
     /**
21 20
      * Determine if the Currency Exchange Rate feature is enabled.

+ 45
- 45
app/Services/ReportService.php Visa fil

@@ -15,8 +15,7 @@ class ReportService
15 15
 {
16 16
     public function __construct(
17 17
         protected AccountService $accountService,
18
-    ) {
19
-    }
18
+    ) {}
20 19
 
21 20
     public function formatBalances(array $balances): AccountBalanceDTO
22 21
     {
@@ -49,14 +48,51 @@ class ReportService
49 48
             ->sortBy(static fn (Collection $groupedAccounts, string $key) => array_search($key, $allCategories, true));
50 49
     }
51 50
 
52
-    private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $fields, ?callable $initializeCategoryBalances = null): ReportDTO
51
+    public function buildAccountBalanceReport(string $startDate, string $endDate, array $columns = []): ReportDTO
52
+    {
53
+        $allCategories = $this->accountService->getAccountCategoryOrder();
54
+
55
+        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
56
+
57
+        $balanceFields = ['starting_balance', 'debit_balance', 'credit_balance', 'net_movement', 'ending_balance'];
58
+
59
+        return $this->buildReport(
60
+            $allCategories,
61
+            $categoryGroupedAccounts,
62
+            fn (Account $account) => $this->accountService->getBalances($account, $startDate, $endDate),
63
+            $balanceFields,
64
+            $columns,
65
+            fn (string $categoryName, array &$categorySummaryBalances) => $this->adjustAccountBalanceCategoryFields($categoryName, $categorySummaryBalances),
66
+        );
67
+    }
68
+
69
+    public function buildTrialBalanceReport(string $startDate, string $endDate, array $columns = []): ReportDTO
70
+    {
71
+        $allCategories = $this->accountService->getAccountCategoryOrder();
72
+
73
+        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
74
+
75
+        $balanceFields = ['debit_balance', 'credit_balance'];
76
+
77
+        return $this->buildReport($allCategories, $categoryGroupedAccounts, function (Account $account) use ($startDate, $endDate) {
78
+            $endingBalance = $this->accountService->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
79
+
80
+            if ($endingBalance === 0) {
81
+                return [];
82
+            }
83
+
84
+            return $this->calculateTrialBalance($account->category, $endingBalance);
85
+        }, $balanceFields, $columns);
86
+    }
87
+
88
+    private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $balanceFields, array $allFields, ?callable $initializeCategoryBalances = null): ReportDTO
53 89
     {
54 90
         $accountCategories = [];
55
-        $reportTotalBalances = array_fill_keys($fields, 0);
91
+        $reportTotalBalances = array_fill_keys($balanceFields, 0);
56 92
 
57 93
         foreach ($allCategories as $categoryName) {
58 94
             $accountsInCategory = $categoryGroupedAccounts[$categoryName] ?? collect();
59
-            $categorySummaryBalances = array_fill_keys($fields, 0);
95
+            $categorySummaryBalances = array_fill_keys($balanceFields, 0);
60 96
 
61 97
             if ($initializeCategoryBalances) {
62 98
                 $initializeCategoryBalances($categoryName, $categorySummaryBalances);
@@ -78,7 +114,7 @@ class ReportService
78 114
                     }
79 115
                 }
80 116
 
81
-                $filteredAccountBalances = $this->filterBalances($accountBalances, $fields);
117
+                $filteredAccountBalances = $this->filterBalances($accountBalances, $balanceFields);
82 118
                 $formattedAccountBalances = $this->formatBalances($filteredAccountBalances);
83 119
 
84 120
                 $categoryAccounts[] = new AccountDTO(
@@ -88,13 +124,13 @@ class ReportService
88 124
                 );
89 125
             }
90 126
 
91
-            foreach ($fields as $field) {
127
+            foreach ($balanceFields as $field) {
92 128
                 if (array_key_exists($field, $categorySummaryBalances)) {
93 129
                     $reportTotalBalances[$field] += $categorySummaryBalances[$field];
94 130
                 }
95 131
             }
96 132
 
97
-            $filteredCategorySummaryBalances = $this->filterBalances($categorySummaryBalances, $fields);
133
+            $filteredCategorySummaryBalances = $this->filterBalances($categorySummaryBalances, $balanceFields);
98 134
             $formattedCategorySummaryBalances = $this->formatBalances($filteredCategorySummaryBalances);
99 135
 
100 136
             $accountCategories[$categoryName] = new AccountCategoryDTO(
@@ -105,24 +141,7 @@ class ReportService
105 141
 
106 142
         $formattedReportTotalBalances = $this->formatBalances($reportTotalBalances);
107 143
 
108
-        return new ReportDTO($accountCategories, $formattedReportTotalBalances);
109
-    }
110
-
111
-    public function buildAccountBalanceReport(string $startDate, string $endDate): ReportDTO
112
-    {
113
-        $allCategories = $this->accountService->getAccountCategoryOrder();
114
-
115
-        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
116
-
117
-        $fields = ['starting_balance', 'debit_balance', 'credit_balance', 'net_movement', 'ending_balance'];
118
-
119
-        return $this->buildReport(
120
-            $allCategories,
121
-            $categoryGroupedAccounts,
122
-            fn (Account $account) => $this->accountService->getBalances($account, $startDate, $endDate),
123
-            $fields,
124
-            fn (string $categoryName, array &$categorySummaryBalances) => $this->adjustAccountBalanceCategoryFields($categoryName, $categorySummaryBalances),
125
-        );
144
+        return new ReportDTO($accountCategories, $formattedReportTotalBalances, $allFields);
126 145
     }
127 146
 
128 147
     private function adjustAccountBalanceCategoryFields(string $categoryName, array &$categorySummaryBalances): void
@@ -132,25 +151,6 @@ class ReportService
132 151
         }
133 152
     }
134 153
 
135
-    public function buildTrialBalanceReport(string $startDate, string $endDate): ReportDTO
136
-    {
137
-        $allCategories = $this->accountService->getAccountCategoryOrder();
138
-
139
-        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
140
-
141
-        $fields = ['debit_balance', 'credit_balance'];
142
-
143
-        return $this->buildReport($allCategories, $categoryGroupedAccounts, function (Account $account) use ($startDate, $endDate) {
144
-            $endingBalance = $this->accountService->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
145
-
146
-            if ($endingBalance === 0) {
147
-                return [];
148
-            }
149
-
150
-            return $this->calculateTrialBalance($account->category, $endingBalance);
151
-        }, $fields);
152
-    }
153
-
154 154
     private function calculateTrialBalance(AccountCategory $category, int $endingBalance): array
155 155
     {
156 156
         if (in_array($category, [AccountCategory::Asset, AccountCategory::Expense], true)) {

+ 43
- 0
app/Support/Column.php Visa fil

@@ -0,0 +1,43 @@
1
+<?php
2
+
3
+namespace App\Support;
4
+
5
+use Filament\Support\Components\Component;
6
+use Filament\Support\Concerns\HasAlignment;
7
+use Filament\Support\Enums\Alignment;
8
+use Filament\Tables\Columns\Concerns\CanBeHidden;
9
+use Filament\Tables\Columns\Concerns\CanBeToggled;
10
+use Filament\Tables\Columns\Concerns\HasLabel;
11
+use Filament\Tables\Columns\Concerns\HasName;
12
+
13
+class Column extends Component
14
+{
15
+    use CanBeHidden;
16
+    use CanBeToggled;
17
+    use HasAlignment;
18
+    use HasLabel;
19
+    use HasName;
20
+
21
+    final public function __construct(string $name)
22
+    {
23
+        $this->name($name);
24
+    }
25
+
26
+    public static function make(string $name): static
27
+    {
28
+        $static = app(static::class, ['name' => $name]);
29
+        $static->configure();
30
+
31
+        return $static;
32
+    }
33
+
34
+    public function getAlignmentClass(): string
35
+    {
36
+        return match ($this->getAlignment()) {
37
+            Alignment::Center, Alignment::Justify, Alignment::Between => 'text-center',
38
+            Alignment::Left, Alignment::Start => 'text-left',
39
+            Alignment::Right, Alignment::End => 'text-right',
40
+            default => '',
41
+        };
42
+    }
43
+}

+ 45
- 75
app/Transformers/AccountBalanceReportTransformer.php Visa fil

@@ -4,6 +4,7 @@ namespace App\Transformers;
4 4
 
5 5
 use App\DTO\AccountDTO;
6 6
 use App\DTO\ReportCategoryDTO;
7
+use App\Support\Column;
7 8
 
8 9
 class AccountBalanceReportTransformer extends BaseReportTransformer
9 10
 {
@@ -14,44 +15,7 @@ class AccountBalanceReportTransformer extends BaseReportTransformer
14 15
 
15 16
     public function getHeaders(): array
16 17
     {
17
-        $headers = ['Account', 'Starting Balance', 'Debit', 'Credit', 'Net Movement', 'Ending Balance'];
18
-
19
-        if ($this->options['showAccountCode'] ?? false) {
20
-            array_unshift($headers, 'Account Code');
21
-        }
22
-
23
-        return $headers;
24
-    }
25
-
26
-    public function getRightAlignedColumns(): array
27
-    {
28
-        $columns = [1, 2, 3, 4, 5];
29
-
30
-        if ($this->options['showAccountCode'] ?? false) {
31
-            $columns = [2, 3, 4, 5, 6];
32
-        }
33
-
34
-        return $columns;
35
-    }
36
-
37
-    public function getLeftAlignedColumns(): array
38
-    {
39
-        $columns = [0];
40
-
41
-        if ($this->options['showAccountCode'] ?? false) {
42
-            $columns = [1];
43
-        }
44
-
45
-        return $columns;
46
-    }
47
-
48
-    public function getCenterAlignedColumns(): array
49
-    {
50
-        if ($this->options['showAccountCode'] ?? false) {
51
-            return [0];
52
-        }
53
-
54
-        return [];
18
+        return array_map(fn (Column $column) => $column->getLabel(), $this->getColumns());
55 19
     }
56 20
 
57 21
     /**
@@ -62,40 +26,48 @@ class AccountBalanceReportTransformer extends BaseReportTransformer
62 26
         $categories = [];
63 27
 
64 28
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
65
-            $header = [$accountCategoryName, '', '', '', '', ''];
66
-
67
-            if ($this->options['showAccountCode'] ?? false) {
68
-                array_unshift($header, '');
29
+            // Initialize header with empty strings
30
+            $header = [];
31
+
32
+            foreach ($this->getColumns() as $index => $column) {
33
+                if ($column->getName() === 'account_name') {
34
+                    $header[$index] = $accountCategoryName;
35
+                } else {
36
+                    $header[$index] = '';
37
+                }
69 38
             }
70 39
 
71 40
             $data = array_map(function (AccountDTO $account) {
72
-                $row = [
73
-                    $account->accountName,
74
-                    $account->balance->startingBalance ?? '',
75
-                    $account->balance->debitBalance,
76
-                    $account->balance->creditBalance,
77
-                    $account->balance->netMovement ?? '',
78
-                    $account->balance->endingBalance ?? '',
79
-                ];
80
-
81
-                if ($this->options['showAccountCode'] ?? false) {
82
-                    array_unshift($row, $account->accountCode);
41
+                $row = [];
42
+
43
+                foreach ($this->getColumns() as $column) {
44
+                    $row[] = match ($column->getName()) {
45
+                        'account_code' => $account->accountCode,
46
+                        'account_name' => $account->accountName,
47
+                        'starting_balance' => $account->balance->startingBalance ?? '',
48
+                        'debit_balance' => $account->balance->debitBalance,
49
+                        'credit_balance' => $account->balance->creditBalance,
50
+                        'net_movement' => $account->balance->netMovement ?? '',
51
+                        'ending_balance' => $account->balance->endingBalance ?? '',
52
+                        default => '',
53
+                    };
83 54
                 }
84 55
 
85 56
                 return $row;
86 57
             }, $accountCategory->accounts);
87 58
 
88
-            $summary = [
89
-                'Total ' . $accountCategoryName,
90
-                $accountCategory->summary->startingBalance ?? '',
91
-                $accountCategory->summary->debitBalance,
92
-                $accountCategory->summary->creditBalance,
93
-                $accountCategory->summary->netMovement ?? '',
94
-                $accountCategory->summary->endingBalance ?? '',
95
-            ];
96
-
97
-            if ($this->options['showAccountCode'] ?? false) {
98
-                array_unshift($summary, '');
59
+            $summary = [];
60
+
61
+            foreach ($this->getColumns() as $column) {
62
+                $summary[] = match ($column->getName()) {
63
+                    'account_name' => 'Total ' . $accountCategoryName,
64
+                    'starting_balance' => $accountCategory->summary->startingBalance ?? '',
65
+                    'debit_balance' => $accountCategory->summary->debitBalance,
66
+                    'credit_balance' => $accountCategory->summary->creditBalance,
67
+                    'net_movement' => $accountCategory->summary->netMovement ?? '',
68
+                    'ending_balance' => $accountCategory->summary->endingBalance ?? '',
69
+                    default => '',
70
+                };
99 71
             }
100 72
 
101 73
             $categories[] = new ReportCategoryDTO(
@@ -110,17 +82,15 @@ class AccountBalanceReportTransformer extends BaseReportTransformer
110 82
 
111 83
     public function getOverallTotals(): array
112 84
     {
113
-        $totals = [
114
-            'Total for all accounts',
115
-            '',
116
-            $this->report->overallTotal->debitBalance,
117
-            $this->report->overallTotal->creditBalance,
118
-            '',
119
-            '',
120
-        ];
121
-
122
-        if ($this->options['showAccountCode'] ?? false) {
123
-            array_unshift($totals, '');
85
+        $totals = [];
86
+
87
+        foreach ($this->getColumns() as $column) {
88
+            $totals[] = match ($column->getName()) {
89
+                'account_name' => 'Total for all accounts',
90
+                'debit_balance' => $this->report->overallTotal->debitBalance,
91
+                'credit_balance' => $this->report->overallTotal->creditBalance,
92
+                default => '',
93
+            };
124 94
         }
125 95
 
126 96
         return $totals;

+ 11
- 12
app/Transformers/BaseReportTransformer.php Visa fil

@@ -4,39 +4,38 @@ namespace App\Transformers;
4 4
 
5 5
 use App\Contracts\ExportableReport;
6 6
 use App\DTO\ReportDTO;
7
+use Filament\Support\Enums\Alignment;
7 8
 use Livewire\Wireable;
8 9
 
9 10
 abstract class BaseReportTransformer implements ExportableReport, Wireable
10 11
 {
11 12
     protected ReportDTO $report;
12 13
 
13
-    protected array $options;
14
-
15
-    public function __construct(ReportDTO $report, array $options = [])
14
+    public function __construct(ReportDTO $report)
16 15
     {
17 16
         $this->report = $report;
18
-        $this->options = $options;
17
+    }
18
+
19
+    public function getColumns(): array
20
+    {
21
+        return $this->report->fields;
19 22
     }
20 23
 
21 24
     public function getAlignmentClass(int $index): string
22 25
     {
23
-        if (in_array($index, $this->getRightAlignedColumns())) {
26
+        $column = $this->getColumns()[$index];
27
+
28
+        if ($column->getAlignment() === Alignment::Right) {
24 29
             return 'text-right';
25 30
         }
26 31
 
27
-        if (in_array($index, $this->getCenterAlignedColumns())) {
32
+        if ($column->getAlignment() === Alignment::Center) {
28 33
             return 'text-center';
29 34
         }
30 35
 
31 36
         return 'text-left';
32 37
     }
33 38
 
34
-    abstract public function getRightAlignedColumns(): array;
35
-
36
-    abstract public function getCenterAlignedColumns(): array;
37
-
38
-    abstract public function getLeftAlignedColumns(): array;
39
-
40 39
     public function toLivewire(): array
41 40
     {
42 41
         return [

+ 55
- 37
app/Transformers/TrialBalanceReportTransformer.php Visa fil

@@ -4,6 +4,7 @@ namespace App\Transformers;
4 4
 
5 5
 use App\DTO\AccountDTO;
6 6
 use App\DTO\ReportCategoryDTO;
7
+use App\Support\Column;
7 8
 
8 9
 class TrialBalanceReportTransformer extends BaseReportTransformer
9 10
 {
@@ -14,22 +15,7 @@ class TrialBalanceReportTransformer extends BaseReportTransformer
14 15
 
15 16
     public function getHeaders(): array
16 17
     {
17
-        return ['', 'Account', 'Debit', 'Credit'];
18
-    }
19
-
20
-    public function getRightAlignedColumns(): array
21
-    {
22
-        return [2, 3];
23
-    }
24
-
25
-    public function getLeftAlignedColumns(): array
26
-    {
27
-        return [1];
28
-    }
29
-
30
-    public function getCenterAlignedColumns(): array
31
-    {
32
-        return [0];
18
+        return array_map(fn (Column $column) => $column->getLabel(), $this->getColumns());
33 19
     }
34 20
 
35 21
     /**
@@ -40,22 +26,48 @@ class TrialBalanceReportTransformer extends BaseReportTransformer
40 26
         $categories = [];
41 27
 
42 28
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
29
+            // Initialize header with empty strings
30
+            $header = [];
31
+
32
+            foreach ($this->getColumns() as $index => $column) {
33
+                if ($column->getName() === 'account_name') {
34
+                    $header[$index] = $accountCategoryName;
35
+                } else {
36
+                    $header[$index] = '';
37
+                }
38
+            }
39
+
40
+            $data = array_map(function (AccountDTO $account) {
41
+                $row = [];
42
+
43
+                foreach ($this->getColumns() as $column) {
44
+                    $row[] = match ($column->getName()) {
45
+                        'account_code' => $account->accountCode,
46
+                        'account_name' => $account->accountName,
47
+                        'debit_balance' => $account->balance->debitBalance,
48
+                        'credit_balance' => $account->balance->creditBalance,
49
+                        default => '',
50
+                    };
51
+                }
52
+
53
+                return $row;
54
+            }, $accountCategory->accounts);
55
+
56
+            $summary = [];
57
+
58
+            foreach ($this->getColumns() as $column) {
59
+                $summary[] = match ($column->getName()) {
60
+                    'account_name' => 'Total ' . $accountCategoryName,
61
+                    'debit_balance' => $accountCategory->summary->debitBalance,
62
+                    'credit_balance' => $accountCategory->summary->creditBalance,
63
+                    default => '',
64
+                };
65
+            }
66
+
43 67
             $categories[] = new ReportCategoryDTO(
44
-                header: ['', $accountCategoryName, '', ''],
45
-                data: array_map(static function (AccountDTO $account) {
46
-                    return [
47
-                        $account->accountCode,
48
-                        $account->accountName,
49
-                        $account->balance->debitBalance,
50
-                        $account->balance->creditBalance,
51
-                    ];
52
-                }, $accountCategory->accounts),
53
-                summary: [
54
-                    '',
55
-                    'Total ' . $accountCategoryName,
56
-                    $accountCategory->summary->debitBalance,
57
-                    $accountCategory->summary->creditBalance,
58
-                ],
68
+                header: $header,
69
+                data: $data,
70
+                summary: $summary,
59 71
             );
60 72
         }
61 73
 
@@ -64,11 +76,17 @@ class TrialBalanceReportTransformer extends BaseReportTransformer
64 76
 
65 77
     public function getOverallTotals(): array
66 78
     {
67
-        return [
68
-            '',
69
-            'Total for all accounts',
70
-            $this->report->overallTotal->debitBalance,
71
-            $this->report->overallTotal->creditBalance,
72
-        ];
79
+        $totals = [];
80
+
81
+        foreach ($this->getColumns() as $column) {
82
+            $totals[] = match ($column->getName()) {
83
+                'account_name' => 'Total for all accounts',
84
+                'debit_balance' => $this->report->overallTotal->debitBalance,
85
+                'credit_balance' => $this->report->overallTotal->creditBalance,
86
+                default => '',
87
+            };
88
+        }
89
+
90
+        return $totals;
73 91
     }
74 92
 }

+ 1
- 2
app/ValueObjects/Money.php Visa fil

@@ -12,8 +12,7 @@ class Money
12 12
     public function __construct(
13 13
         private readonly int $amount,
14 14
         private readonly string $currencyCode
15
-    ) {
16
-    }
15
+    ) {}
17 16
 
18 17
     public function getAmount(): int
19 18
     {

+ 1
- 2
app/View/Models/InvoiceViewModel.php Visa fil

@@ -14,8 +14,7 @@ class InvoiceViewModel
14 14
     public function __construct(
15 15
         public DocumentDefault $invoice,
16 16
         public ?array $data = null
17
-    ) {
18
-    }
17
+    ) {}
19 18
 
20 19
     public function logo(): ?string
21 20
     {

+ 2
- 1
composer.json Visa fil

@@ -35,7 +35,8 @@
35 35
         "mockery/mockery": "^1.6",
36 36
         "nunomaduro/collision": "^8.0",
37 37
         "phpunit/phpunit": "^10.5",
38
-        "spatie/laravel-ignition": "^2.4"
38
+        "spatie/laravel-ignition": "^2.4",
39
+        "spatie/laravel-ray": "^1.36"
39 40
     },
40 41
     "autoload": {
41 42
         "psr-4": {

+ 965
- 225
composer.lock
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


+ 30
- 20
package-lock.json Visa fil

@@ -6,14 +6,14 @@
6 6
         "": {
7 7
             "devDependencies": {
8 8
                 "@tailwindcss/forms": "^0.5.7",
9
-                "@tailwindcss/typography": "^0.5.10",
10
-                "autoprefixer": "^10.4.18",
11
-                "axios": "^1.6.4",
9
+                "@tailwindcss/typography": "^0.5.13",
10
+                "autoprefixer": "^10.4.19",
11
+                "axios": "^1.7.2",
12 12
                 "laravel-vite-plugin": "^1.0",
13
-                "postcss": "^8.4.35",
14
-                "postcss-nesting": "^12.1.0",
15
-                "tailwindcss": "^3.4.1",
16
-                "vite": "^5.0"
13
+                "postcss": "^8.4.38",
14
+                "postcss-nesting": "^12.1.5",
15
+                "tailwindcss": "^3.4.4",
16
+                "vite": "^5.3"
17 17
             }
18 18
         },
19 19
         "node_modules/@alloc/quick-lru": {
@@ -931,9 +931,9 @@
931 931
             }
932 932
         },
933 933
         "node_modules/caniuse-lite": {
934
-            "version": "1.0.30001634",
935
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001634.tgz",
936
-            "integrity": "sha512-fbBYXQ9q3+yp1q1gBk86tOFs4pyn/yxFm5ZNP18OXJDfA3txImOY9PhfxVggZ4vRHDqoU8NrKU81eN0OtzOgRA==",
934
+            "version": "1.0.30001636",
935
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
936
+            "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
937 937
             "dev": true,
938 938
             "funding": [
939 939
                 {
@@ -1079,9 +1079,9 @@
1079 1079
             "dev": true
1080 1080
         },
1081 1081
         "node_modules/electron-to-chromium": {
1082
-            "version": "1.4.803",
1083
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.803.tgz",
1084
-            "integrity": "sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==",
1082
+            "version": "1.4.807",
1083
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.807.tgz",
1084
+            "integrity": "sha512-kSmJl2ZwhNf/bcIuCH/imtNOKlpkLDn2jqT5FJ+/0CXjhnFaOa9cOe9gHKKy71eM49izwuQjZhKk+lWQ1JxB7A==",
1085 1085
             "dev": true
1086 1086
         },
1087 1087
         "node_modules/emoji-regex": {
@@ -1273,15 +1273,16 @@
1273 1273
             }
1274 1274
         },
1275 1275
         "node_modules/glob": {
1276
-            "version": "10.4.1",
1277
-            "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
1278
-            "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
1276
+            "version": "10.4.2",
1277
+            "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz",
1278
+            "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==",
1279 1279
             "dev": true,
1280 1280
             "dependencies": {
1281 1281
                 "foreground-child": "^3.1.0",
1282 1282
                 "jackspeak": "^3.1.2",
1283 1283
                 "minimatch": "^9.0.4",
1284 1284
                 "minipass": "^7.1.2",
1285
+                "package-json-from-dist": "^1.0.0",
1285 1286
                 "path-scurry": "^1.11.1"
1286 1287
             },
1287 1288
             "bin": {
@@ -1331,12 +1332,15 @@
1331 1332
             }
1332 1333
         },
1333 1334
         "node_modules/is-core-module": {
1334
-            "version": "2.13.1",
1335
-            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
1336
-            "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
1335
+            "version": "2.14.0",
1336
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz",
1337
+            "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==",
1337 1338
             "dev": true,
1338 1339
             "dependencies": {
1339
-                "hasown": "^2.0.0"
1340
+                "hasown": "^2.0.2"
1341
+            },
1342
+            "engines": {
1343
+                "node": ">= 0.4"
1340 1344
             },
1341 1345
             "funding": {
1342 1346
                 "url": "https://github.com/sponsors/ljharb"
@@ -1622,6 +1626,12 @@
1622 1626
                 "node": ">= 6"
1623 1627
             }
1624 1628
         },
1629
+        "node_modules/package-json-from-dist": {
1630
+            "version": "1.0.0",
1631
+            "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
1632
+            "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
1633
+            "dev": true
1634
+        },
1625 1635
         "node_modules/path-key": {
1626 1636
             "version": "3.1.1",
1627 1637
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",

+ 7
- 7
package.json Visa fil

@@ -7,13 +7,13 @@
7 7
     },
8 8
     "devDependencies": {
9 9
         "@tailwindcss/forms": "^0.5.7",
10
-        "@tailwindcss/typography": "^0.5.10",
11
-        "autoprefixer": "^10.4.18",
12
-        "axios": "^1.6.4",
10
+        "@tailwindcss/typography": "^0.5.13",
11
+        "autoprefixer": "^10.4.19",
12
+        "axios": "^1.7.2",
13 13
         "laravel-vite-plugin": "^1.0",
14
-        "postcss": "^8.4.35",
15
-        "postcss-nesting": "^12.1.0",
16
-        "tailwindcss": "^3.4.1",
17
-        "vite": "^5.0"
14
+        "postcss": "^8.4.38",
15
+        "postcss-nesting": "^12.1.5",
16
+        "tailwindcss": "^3.4.4",
17
+        "vite": "^5.3"
18 18
     }
19 19
 }

+ 32
- 32
resources/views/components/company/tables/reports/detailed-report.blade.php Visa fil

@@ -1,8 +1,8 @@
1
-<table class="w-full table-auto divide-y divide-gray-200 text-start dark:divide-white/5">
1
+<table class="w-full table-auto divide-y divide-gray-200 dark:divide-white/5">
2 2
     <thead class="divide-y divide-gray-200 dark:divide-white/5">
3 3
         <tr class="bg-gray-50 dark:bg-white/5">
4 4
             @foreach($report->getHeaders() as $index => $header)
5
-                <th class="px-3 py-3.5 sm:first-of-type:ps-6 sm:last-of-type:pe-6 {{ $report->getAlignmentClass($index) }}">
5
+                <th wire:key="header-{{ $index }}" class="px-3 py-3.5 sm:first-of-type:ps-6 sm:last-of-type:pe-6 {{ $report->getAlignmentClass($index) }}">
6 6
                     <span class="text-sm font-semibold text-gray-950 dark:text-white">
7 7
                         {{ $header }}
8 8
                     </span>
@@ -10,48 +10,48 @@
10 10
             @endforeach
11 11
         </tr>
12 12
     </thead>
13
-    @foreach($report->getCategories() as $category)
14
-        <tbody class="divide-y divide-gray-200 whitespace-nowrap dark:divide-white/5">
15
-        <tr class="bg-gray-50 dark:bg-white/5">
16
-            @foreach($category->header as $index => $header)
17
-                <x-filament-tables::cell class="{{ $report->getAlignmentClass($index) }}">
18
-                    <div class="px-3 py-2 text-sm font-semibold text-gray-950 dark:text-white">
19
-                        {{ $header }}
20
-                    </div>
21
-                </x-filament-tables::cell>
13
+    @foreach($report->getCategories() as $categoryIndex => $category)
14
+        <tbody wire:key="category-{{ $categoryIndex }}" class="divide-y divide-gray-200 whitespace-nowrap dark:divide-white/5">
15
+            <tr class="bg-gray-50 dark:bg-white/5">
16
+                @foreach($category->header as $headerIndex => $header)
17
+                    <x-filament-tables::cell wire:key="category-{{ $categoryIndex }}-header-{{ $headerIndex }}" class="{{ $report->getAlignmentClass($headerIndex) }}">
18
+                        <div class="px-3 py-2 text-sm font-semibold text-gray-950 dark:text-white">
19
+                            {{ $header }}
20
+                        </div>
21
+                    </x-filament-tables::cell>
22
+                @endforeach
23
+            </tr>
24
+            @foreach($category->data as $dataIndex => $account)
25
+                <tr wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}">
26
+                    @foreach($account as $cellIndex => $cell)
27
+                        <x-filament-tables::cell wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}-cell-{{ $cellIndex }}" class="{{ $report->getAlignmentClass($cellIndex) }}">
28
+                            <div class="px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white">
29
+                                {{ $cell }}
30
+                            </div>
31
+                        </x-filament-tables::cell>
32
+                    @endforeach
33
+                </tr>
22 34
             @endforeach
23
-        </tr>
24
-        @foreach($category->data as $account)
25
-            <tr>
26
-                @foreach($account as $index => $cell)
27
-                    <x-filament-tables::cell class="{{ $report->getAlignmentClass($index) }}">
28
-                        <div class="px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white">
35
+            <tr wire:key="category-{{ $categoryIndex }}-summary">
36
+                @foreach($category->summary as $summaryIndex => $cell)
37
+                    <x-filament-tables::cell wire:key="category-{{ $categoryIndex }}-summary-{{ $summaryIndex }}" class="{{ $report->getAlignmentClass($summaryIndex) }}">
38
+                        <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
29 39
                             {{ $cell }}
30 40
                         </div>
31 41
                     </x-filament-tables::cell>
32 42
                 @endforeach
33 43
             </tr>
34
-        @endforeach
35
-        <tr>
36
-            @foreach($category->summary as $index => $cell)
37
-                <x-filament-tables::cell class="{{ $report->getAlignmentClass($index) }}">
38
-                    <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
39
-                        {{ $cell }}
40
-                    </div>
44
+            <tr wire:key="category-{{ $categoryIndex }}-spacer">
45
+                <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
46
+                    <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
41 47
                 </x-filament-tables::cell>
42
-            @endforeach
43
-        </tr>
44
-        <tr>
45
-            <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
46
-                <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
47
-            </x-filament-tables::cell>
48
-        </tr>
48
+            </tr>
49 49
         </tbody>
50 50
     @endforeach
51 51
     <tfoot>
52 52
         <tr class="bg-gray-50 dark:bg-white/5">
53 53
             @foreach($report->getOverallTotals() as $index => $total)
54
-                <x-filament-tables::cell class="{{ $report->getAlignmentClass($index) }}">
54
+                <x-filament-tables::cell wire:key="footer-total-{{ $index }}" class="{{ $report->getAlignmentClass($index) }}">
55 55
                     <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
56 56
                         {{ $total }}
57 57
                     </div>

+ 0
- 22
resources/views/filament/company/pages/reports/account-balances.blade.php Visa fil

@@ -1,22 +0,0 @@
1
-<x-filament-panels::page>
2
-    <div class="flex flex-col gap-y-6">
3
-        <x-filament-tables::container>
4
-            <div class="p-6 divide-y divide-gray-200 dark:divide-white/5">
5
-                <form wire:submit.prevent="loadReportData" class="w-full">
6
-                    <div class="flex flex-col md:flex-row items-start justify-center gap-4 md:gap-12">
7
-                        <div class="flex-grow">
8
-                            {{ $this->form }}
9
-                        </div>
10
-                        <x-filament::button type="submit" class="mt-4 md:mt-0 flex-shrink-0">
11
-                            Update Report
12
-                        </x-filament::button>
13
-                    </div>
14
-                </form>
15
-            </div>
16
-            <div class="divide-y divide-gray-200 overflow-x-auto dark:divide-white/10 dark:border-t-white/10">
17
-                <x-company.tables.reports.detailed-report :report="$accountBalanceReport" />
18
-            </div>
19
-            <div class="es-table__footer-ctn border-t border-gray-200"></div>
20
-        </x-filament-tables::container>
21
-    </div>
22
-</x-filament-panels::page>

+ 25
- 0
resources/views/filament/company/pages/reports/detailed-report.blade.php Visa fil

@@ -0,0 +1,25 @@
1
+<x-filament-panels::page>
2
+    <div class="flex flex-col gap-y-6">
3
+        <x-filament-tables::container>
4
+            <div class="p-6 divide-y divide-gray-200 dark:divide-white/5">
5
+                <form wire:submit.prevent="loadReportData">
6
+                    <div class="flex flex-col lg:flex-row items-start lg:items-center justify-center gap-4 lg:gap-12">
7
+                        {{ $this->form }}
8
+                        <x-filament-tables::column-toggle.dropdown
9
+                            class="my-auto"
10
+                            :form="$this->toggleTableColumnForm"
11
+                            :trigger-action="$this->toggleColumnsAction"
12
+                        />
13
+                        <x-filament::button type="submit" class="flex-shrink-0">
14
+                            Update Report
15
+                        </x-filament::button>
16
+                    </div>
17
+                </form>
18
+            </div>
19
+            <div class="relative divide-y divide-gray-200 overflow-x-auto dark:divide-white/10 dark:border-t-white/10">
20
+                <x-company.tables.reports.detailed-report :report="$this->report" />
21
+            </div>
22
+            <div class="es-table__footer-ctn border-t border-gray-200"></div>
23
+        </x-filament-tables::container>
24
+    </div>
25
+</x-filament-panels::page>

+ 0
- 23
resources/views/filament/company/pages/reports/trial-balance.blade.php Visa fil

@@ -1,23 +0,0 @@
1
-<x-filament-panels::page>
2
-    <div class="flex flex-col gap-y-6">
3
-        <x-filament-tables::container>
4
-            <div class="p-6 divide-y divide-gray-200 dark:divide-white/5">
5
-                <form wire:submit.prevent="loadReportData" class="w-full">
6
-                    <div class="flex flex-col md:flex-row items-end justify-center gap-4 md:gap-6">
7
-                        <div class="flex-grow">
8
-                            {{ $this->form }}
9
-                        </div>
10
-                        <x-filament::button type="submit" class="mt-4 md:mt-0">
11
-                            Update Report
12
-                        </x-filament::button>
13
-                    </div>
14
-                </form>
15
-            </div>
16
-            <div class="divide-y divide-gray-200 overflow-x-auto dark:divide-white/10 dark:border-t-white/10">
17
-                <x-company.tables.reports.detailed-report :report="$trialBalanceReport" />
18
-            </div>
19
-            <div class="es-table__footer-ctn border-t border-gray-200"></div>
20
-        </x-filament-tables::container>
21
-    </div>
22
-</x-filament-panels::page>
23
-

Laddar…
Avbryt
Spara