Andrew Wallo 1 year ago
parent
commit
5756649ce3
44 changed files with 1543 additions and 611 deletions
  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 View File

17
 
17
 
18
     public function getOverallTotals(): array;
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 View File

12
         public string $creditBalance,
12
         public string $creditBalance,
13
         public ?string $netMovement,
13
         public ?string $netMovement,
14
         public ?string $endingBalance,
14
         public ?string $endingBalance,
15
-    ) {
16
-    }
15
+    ) {}
17
 
16
 
18
     public function toLivewire(): array
17
     public function toLivewire(): array
19
     {
18
     {

+ 1
- 2
app/DTO/AccountCategoryDTO.php View File

12
     public function __construct(
12
     public function __construct(
13
         public array $accounts,
13
         public array $accounts,
14
         public AccountBalanceDTO $summary,
14
         public AccountBalanceDTO $summary,
15
-    ) {
16
-    }
15
+    ) {}
17
 
16
 
18
     public function toLivewire(): array
17
     public function toLivewire(): array
19
     {
18
     {

+ 1
- 2
app/DTO/AccountDTO.php View File

10
         public string $accountName,
10
         public string $accountName,
11
         public string $accountCode,
11
         public string $accountCode,
12
         public AccountBalanceDTO $balance,
12
         public AccountBalanceDTO $balance,
13
-    ) {
14
-    }
13
+    ) {}
15
 
14
 
16
     public function toLivewire(): array
15
     public function toLivewire(): array
17
     {
16
     {

+ 1
- 2
app/DTO/ReportCategoryDTO.php View File

13
         public array $header,
13
         public array $header,
14
         public array $data,
14
         public array $data,
15
         public array $summary,
15
         public array $summary,
16
-    ) {
17
-    }
16
+    ) {}
18
 }
17
 }

+ 4
- 2
app/DTO/ReportDTO.php View File

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

+ 1
- 2
app/Events/CompanyConfigured.php View File

15
 
15
 
16
     public function __construct(
16
     public function __construct(
17
         public Company $company
17
         public Company $company
18
-    ) {
19
-    }
18
+    ) {}
20
 }
19
 }

+ 1
- 2
app/Events/CompanyDefaultEvent.php View File

18
      */
18
      */
19
     public function __construct(
19
     public function __construct(
20
         public Model $model
20
         public Model $model
21
-    ) {
22
-    }
21
+    ) {}
23
 }
22
 }

+ 1
- 2
app/Events/CompanyDefaultUpdated.php View File

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

+ 1
- 2
app/Events/CompanyGenerated.php View File

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

+ 1
- 2
app/Events/CurrencyRateChanged.php View File

17
         public Currency $currency,
17
         public Currency $currency,
18
         public float $oldRate,
18
         public float $oldRate,
19
         public float $newRate
19
         public float $newRate
20
-    ) {
21
-    }
20
+    ) {}
22
 }
21
 }

+ 1
- 2
app/Events/DefaultCurrencyChanged.php View File

18
      */
18
      */
19
     public function __construct(
19
     public function __construct(
20
         public Currency $currency
20
         public Currency $currency
21
-    ) {
22
-    }
21
+    ) {}
23
 }
22
 }

+ 1
- 2
app/Events/PlaidSuccess.php View File

18
         public string $publicToken,
18
         public string $publicToken,
19
         public string $accessToken,
19
         public string $accessToken,
20
         public Company $company
20
         public Company $company
21
-    ) {
22
-    }
21
+    ) {}
23
 }
22
 }

+ 1
- 2
app/Events/StartTransactionImport.php View File

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

+ 2
- 4
app/Filament/Company/Pages/Accounting/Transactions.php View File

258
                             ->schema([
258
                             ->schema([
259
                                 Select::make('account_id')
259
                                 Select::make('account_id')
260
                                     ->label('Category')
260
                                     ->label('Category')
261
-                                    ->options(fn () => $this->getChartAccountOptions(nominalAccountsOnly: true, onlyWithTransactions: true))
261
+                                    ->options(fn () => $this->getChartAccountOptions(nominalAccountsOnly: true))
262
                                     ->multiple()
262
                                     ->multiple()
263
                                     ->searchable(),
263
                                     ->searchable(),
264
                                 Select::make('reviewed')
264
                                 Select::make('reviewed')
713
         return 'uncategorized';
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
         $nominalAccountsOnly ??= false;
718
         $nominalAccountsOnly ??= false;
720
 
719
 
721
         $excludedCategory = match ($type) {
720
         $excludedCategory = match ($type) {
727
         return Account::query()
726
         return Account::query()
728
             ->when($nominalAccountsOnly, fn (Builder $query) => $query->doesntHave('bankAccount'))
727
             ->when($nominalAccountsOnly, fn (Builder $query) => $query->doesntHave('bankAccount'))
729
             ->when($excludedCategory, fn (Builder $query) => $query->whereNot('category', $excludedCategory))
728
             ->when($excludedCategory, fn (Builder $query) => $query->whereNot('category', $excludedCategory))
730
-            ->when($onlyWithTransactions, fn (Builder $query) => $query->has('transactions'))
731
             ->where(function (Builder $query) use ($currentAccountId) {
729
             ->where(function (Builder $query) use ($currentAccountId) {
732
                 $query->where('archived', false)
730
                 $query->where('archived', false)
733
                     ->orWhere('id', $currentAccountId);
731
                     ->orWhere('id', $currentAccountId);

+ 59
- 27
app/Filament/Company/Pages/Reports/AccountBalances.php View File

3
 namespace App\Filament\Company\Pages\Reports;
3
 namespace App\Filament\Company\Pages\Reports;
4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
6
 use App\Services\ExportService;
7
 use App\Services\ExportService;
7
 use App\Services\ReportService;
8
 use App\Services\ReportService;
9
+use App\Support\Column;
8
 use App\Transformers\AccountBalanceReportTransformer;
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
 use Filament\Forms\Form;
11
 use Filament\Forms\Form;
12
+use Filament\Support\Enums\Alignment;
14
 use Guava\FilamentClusters\Forms\Cluster;
13
 use Guava\FilamentClusters\Forms\Cluster;
15
 use Symfony\Component\HttpFoundation\StreamedResponse;
14
 use Symfony\Component\HttpFoundation\StreamedResponse;
16
 
15
 
17
 class AccountBalances extends BaseReportPage
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
     protected static ?string $slug = 'reports/account-balances';
20
     protected static ?string $slug = 'reports/account-balances';
22
 
21
 
24
 
23
 
25
     protected ReportService $reportService;
24
     protected ReportService $reportService;
26
 
25
 
27
-    public ExportableReport $accountBalanceReport;
28
-
29
     protected ExportService $exportService;
26
     protected ExportService $exportService;
30
 
27
 
31
     public function boot(ReportService $reportService, ExportService $exportService): void
28
     public function boot(ReportService $reportService, ExportService $exportService): void
34
         $this->exportService = $exportService;
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
     public function form(Form $form): Form
70
     public function form(Form $form): Form
45
     {
71
     {
46
         return $form
72
         return $form
47
             ->inlineLabel()
73
             ->inlineLabel()
74
+            ->columns([
75
+                'lg' => 1,
76
+                '2xl' => 2,
77
+            ])
78
+            ->live()
48
             ->schema([
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
     public function exportCSV(): StreamedResponse
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
     public function exportPDF(): StreamedResponse
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 View File

2
 
2
 
3
 namespace App\Filament\Company\Pages\Reports;
3
 namespace App\Filament\Company\Pages\Reports;
4
 
4
 
5
+use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
5
 use App\Filament\Forms\Components\DateRangeSelect;
7
 use App\Filament\Forms\Components\DateRangeSelect;
6
 use App\Models\Company;
8
 use App\Models\Company;
9
+use App\Support\Column;
7
 use Filament\Actions\Action;
10
 use Filament\Actions\Action;
8
 use Filament\Actions\ActionGroup;
11
 use Filament\Actions\ActionGroup;
12
+use Filament\Forms\Components\Checkbox;
9
 use Filament\Forms\Components\Component;
13
 use Filament\Forms\Components\Component;
10
 use Filament\Forms\Components\DatePicker;
14
 use Filament\Forms\Components\DatePicker;
15
+use Filament\Forms\Form;
11
 use Filament\Forms\Set;
16
 use Filament\Forms\Set;
12
 use Filament\Pages\Page;
17
 use Filament\Pages\Page;
18
+use Filament\Support\Enums\ActionSize;
13
 use Filament\Support\Enums\IconPosition;
19
 use Filament\Support\Enums\IconPosition;
14
 use Filament\Support\Enums\IconSize;
20
 use Filament\Support\Enums\IconSize;
21
+use Filament\Support\Facades\FilamentIcon;
15
 use Illuminate\Support\Carbon;
22
 use Illuminate\Support\Carbon;
23
+use Livewire\Attributes\Computed;
24
+use Livewire\Attributes\Session;
16
 use Symfony\Component\HttpFoundation\StreamedResponse;
25
 use Symfony\Component\HttpFoundation\StreamedResponse;
17
 
26
 
18
 abstract class BaseReportPage extends Page
27
 abstract class BaseReportPage extends Page
19
 {
28
 {
29
+    #[Session]
20
     public string $startDate = '';
30
     public string $startDate = '';
21
 
31
 
32
+    #[Session]
22
     public string $endDate = '';
33
     public string $endDate = '';
23
 
34
 
35
+    #[Session]
24
     public string $dateRange = '';
36
     public string $dateRange = '';
25
 
37
 
26
     public string $fiscalYearStartDate = '';
38
     public string $fiscalYearStartDate = '';
29
 
41
 
30
     public Company $company;
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
     public function mount(): void
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
         $this->company = auth()->user()->currentCompany;
81
         $this->company = auth()->user()->currentCompany;
37
         $this->fiscalYearStartDate = $this->company->locale->fiscalYearStartDate();
82
         $this->fiscalYearStartDate = $this->company->locale->fiscalYearStartDate();
38
         $this->fiscalYearEndDate = $this->company->locale->fiscalYearEndDate();
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
     public function getDefaultDateRange(): string
129
     public function getDefaultDateRange(): string
53
     {
130
     {
54
         return 'FY-' . now()->year;
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
     public function setDateRange(Carbon $start, Carbon $end): void
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
     protected function getHeaderActions(): array
193
     protected function getHeaderActions(): array
91
             ->endDateField('endDate');
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
     protected function getStartDateFormComponent(): Component
230
     protected function getStartDateFormComponent(): Component
95
     {
231
     {
96
         return DatePicker::make('startDate')
232
         return DatePicker::make('startDate')

+ 44
- 12
app/Filament/Company/Pages/Reports/TrialBalance.php View File

3
 namespace App\Filament\Company\Pages\Reports;
3
 namespace App\Filament\Company\Pages\Reports;
4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
+use App\DTO\ReportDTO;
6
 use App\Services\ExportService;
7
 use App\Services\ExportService;
7
 use App\Services\ReportService;
8
 use App\Services\ReportService;
9
+use App\Support\Column;
8
 use App\Transformers\TrialBalanceReportTransformer;
10
 use App\Transformers\TrialBalanceReportTransformer;
9
-use Filament\Forms\Components\Split;
10
 use Filament\Forms\Form;
11
 use Filament\Forms\Form;
12
+use Filament\Support\Enums\Alignment;
13
+use Guava\FilamentClusters\Forms\Cluster;
11
 use Symfony\Component\HttpFoundation\StreamedResponse;
14
 use Symfony\Component\HttpFoundation\StreamedResponse;
12
 
15
 
13
 class TrialBalance extends BaseReportPage
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
     protected static ?string $slug = 'reports/trial-balance';
20
     protected static ?string $slug = 'reports/trial-balance';
18
 
21
 
20
 
23
 
21
     protected ReportService $reportService;
24
     protected ReportService $reportService;
22
 
25
 
23
-    public ExportableReport $trialBalanceReport;
24
-
25
     protected ExportService $exportService;
26
     protected ExportService $exportService;
26
 
27
 
27
     public function boot(ReportService $reportService, ExportService $exportService): void
28
     public function boot(ReportService $reportService, ExportService $exportService): void
30
         $this->exportService = $exportService;
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
     public function form(Form $form): Form
55
     public function form(Form $form): Form
40
     {
56
     {
41
         return $form
57
         return $form
58
+            ->inlineLabel()
59
+            ->columns([
60
+                'lg' => 1,
61
+                '2xl' => 2,
62
+            ])
63
+            ->live()
42
             ->schema([
64
             ->schema([
43
-                Split::make([
44
-                    $this->getDateRangeFormComponent(),
65
+                $this->getDateRangeFormComponent(),
66
+                Cluster::make([
45
                     $this->getStartDateFormComponent(),
67
                     $this->getStartDateFormComponent(),
46
                     $this->getEndDateFormComponent(),
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
     public function exportCSV(): StreamedResponse
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
     public function exportPDF(): StreamedResponse
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 View File

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

+ 1
- 2
app/Jobs/ProcessTransactionUpdate.php View File

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

+ 1
- 2
app/Listeners/CreateConnectedAccount.php View File

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

+ 1
- 2
app/Listeners/HandleTransactionImport.php View File

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

+ 1
- 2
app/Listeners/UpdateCurrencyRates.php View File

15
      */
15
      */
16
     public function __construct(
16
     public function __construct(
17
         private CurrencyHandler $currencyService
17
         private CurrencyHandler $currencyService
18
-    ) {
19
-    }
18
+    ) {}
20
 
19
 
21
     /**
20
     /**
22
      * Handle the event.
21
      * Handle the event.

+ 2
- 0
app/Models/Company.php View File

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

+ 1
- 3
app/Providers/CurrencyServiceProvider.php View File

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 View File

10
 
10
 
11
 class SquireServiceProvider extends ServiceProvider
11
 class SquireServiceProvider extends ServiceProvider
12
 {
12
 {
13
-    public function register(): void
14
-    {
15
-
16
-    }
13
+    public function register(): void {}
17
 
14
 
18
     public function boot(): void
15
     public function boot(): void
19
     {
16
     {

+ 1
- 2
app/Services/AccountService.php View File

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

+ 1
- 2
app/Services/ConnectedBankAccountService.php View File

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

+ 1
- 2
app/Services/CurrencyService.php View File

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

+ 45
- 45
app/Services/ReportService.php View File

15
 {
15
 {
16
     public function __construct(
16
     public function __construct(
17
         protected AccountService $accountService,
17
         protected AccountService $accountService,
18
-    ) {
19
-    }
18
+    ) {}
20
 
19
 
21
     public function formatBalances(array $balances): AccountBalanceDTO
20
     public function formatBalances(array $balances): AccountBalanceDTO
22
     {
21
     {
49
             ->sortBy(static fn (Collection $groupedAccounts, string $key) => array_search($key, $allCategories, true));
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
         $accountCategories = [];
90
         $accountCategories = [];
55
-        $reportTotalBalances = array_fill_keys($fields, 0);
91
+        $reportTotalBalances = array_fill_keys($balanceFields, 0);
56
 
92
 
57
         foreach ($allCategories as $categoryName) {
93
         foreach ($allCategories as $categoryName) {
58
             $accountsInCategory = $categoryGroupedAccounts[$categoryName] ?? collect();
94
             $accountsInCategory = $categoryGroupedAccounts[$categoryName] ?? collect();
59
-            $categorySummaryBalances = array_fill_keys($fields, 0);
95
+            $categorySummaryBalances = array_fill_keys($balanceFields, 0);
60
 
96
 
61
             if ($initializeCategoryBalances) {
97
             if ($initializeCategoryBalances) {
62
                 $initializeCategoryBalances($categoryName, $categorySummaryBalances);
98
                 $initializeCategoryBalances($categoryName, $categorySummaryBalances);
78
                     }
114
                     }
79
                 }
115
                 }
80
 
116
 
81
-                $filteredAccountBalances = $this->filterBalances($accountBalances, $fields);
117
+                $filteredAccountBalances = $this->filterBalances($accountBalances, $balanceFields);
82
                 $formattedAccountBalances = $this->formatBalances($filteredAccountBalances);
118
                 $formattedAccountBalances = $this->formatBalances($filteredAccountBalances);
83
 
119
 
84
                 $categoryAccounts[] = new AccountDTO(
120
                 $categoryAccounts[] = new AccountDTO(
88
                 );
124
                 );
89
             }
125
             }
90
 
126
 
91
-            foreach ($fields as $field) {
127
+            foreach ($balanceFields as $field) {
92
                 if (array_key_exists($field, $categorySummaryBalances)) {
128
                 if (array_key_exists($field, $categorySummaryBalances)) {
93
                     $reportTotalBalances[$field] += $categorySummaryBalances[$field];
129
                     $reportTotalBalances[$field] += $categorySummaryBalances[$field];
94
                 }
130
                 }
95
             }
131
             }
96
 
132
 
97
-            $filteredCategorySummaryBalances = $this->filterBalances($categorySummaryBalances, $fields);
133
+            $filteredCategorySummaryBalances = $this->filterBalances($categorySummaryBalances, $balanceFields);
98
             $formattedCategorySummaryBalances = $this->formatBalances($filteredCategorySummaryBalances);
134
             $formattedCategorySummaryBalances = $this->formatBalances($filteredCategorySummaryBalances);
99
 
135
 
100
             $accountCategories[$categoryName] = new AccountCategoryDTO(
136
             $accountCategories[$categoryName] = new AccountCategoryDTO(
105
 
141
 
106
         $formattedReportTotalBalances = $this->formatBalances($reportTotalBalances);
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
     private function adjustAccountBalanceCategoryFields(string $categoryName, array &$categorySummaryBalances): void
147
     private function adjustAccountBalanceCategoryFields(string $categoryName, array &$categorySummaryBalances): void
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
     private function calculateTrialBalance(AccountCategory $category, int $endingBalance): array
154
     private function calculateTrialBalance(AccountCategory $category, int $endingBalance): array
155
     {
155
     {
156
         if (in_array($category, [AccountCategory::Asset, AccountCategory::Expense], true)) {
156
         if (in_array($category, [AccountCategory::Asset, AccountCategory::Expense], true)) {

+ 43
- 0
app/Support/Column.php View File

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 View File

4
 
4
 
5
 use App\DTO\AccountDTO;
5
 use App\DTO\AccountDTO;
6
 use App\DTO\ReportCategoryDTO;
6
 use App\DTO\ReportCategoryDTO;
7
+use App\Support\Column;
7
 
8
 
8
 class AccountBalanceReportTransformer extends BaseReportTransformer
9
 class AccountBalanceReportTransformer extends BaseReportTransformer
9
 {
10
 {
14
 
15
 
15
     public function getHeaders(): array
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
         $categories = [];
26
         $categories = [];
63
 
27
 
64
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
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
             $data = array_map(function (AccountDTO $account) {
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
                 return $row;
56
                 return $row;
86
             }, $accountCategory->accounts);
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
             $categories[] = new ReportCategoryDTO(
73
             $categories[] = new ReportCategoryDTO(
110
 
82
 
111
     public function getOverallTotals(): array
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
         return $totals;
96
         return $totals;

+ 11
- 12
app/Transformers/BaseReportTransformer.php View File

4
 
4
 
5
 use App\Contracts\ExportableReport;
5
 use App\Contracts\ExportableReport;
6
 use App\DTO\ReportDTO;
6
 use App\DTO\ReportDTO;
7
+use Filament\Support\Enums\Alignment;
7
 use Livewire\Wireable;
8
 use Livewire\Wireable;
8
 
9
 
9
 abstract class BaseReportTransformer implements ExportableReport, Wireable
10
 abstract class BaseReportTransformer implements ExportableReport, Wireable
10
 {
11
 {
11
     protected ReportDTO $report;
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
         $this->report = $report;
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
     public function getAlignmentClass(int $index): string
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
             return 'text-right';
29
             return 'text-right';
25
         }
30
         }
26
 
31
 
27
-        if (in_array($index, $this->getCenterAlignedColumns())) {
32
+        if ($column->getAlignment() === Alignment::Center) {
28
             return 'text-center';
33
             return 'text-center';
29
         }
34
         }
30
 
35
 
31
         return 'text-left';
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
     public function toLivewire(): array
39
     public function toLivewire(): array
41
     {
40
     {
42
         return [
41
         return [

+ 55
- 37
app/Transformers/TrialBalanceReportTransformer.php View File

4
 
4
 
5
 use App\DTO\AccountDTO;
5
 use App\DTO\AccountDTO;
6
 use App\DTO\ReportCategoryDTO;
6
 use App\DTO\ReportCategoryDTO;
7
+use App\Support\Column;
7
 
8
 
8
 class TrialBalanceReportTransformer extends BaseReportTransformer
9
 class TrialBalanceReportTransformer extends BaseReportTransformer
9
 {
10
 {
14
 
15
 
15
     public function getHeaders(): array
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
         $categories = [];
26
         $categories = [];
41
 
27
 
42
         foreach ($this->report->categories as $accountCategoryName => $accountCategory) {
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
             $categories[] = new ReportCategoryDTO(
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
 
76
 
65
     public function getOverallTotals(): array
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 View File

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

+ 1
- 2
app/View/Models/InvoiceViewModel.php View File

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

+ 2
- 1
composer.json View File

35
         "mockery/mockery": "^1.6",
35
         "mockery/mockery": "^1.6",
36
         "nunomaduro/collision": "^8.0",
36
         "nunomaduro/collision": "^8.0",
37
         "phpunit/phpunit": "^10.5",
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
     "autoload": {
41
     "autoload": {
41
         "psr-4": {
42
         "psr-4": {

+ 965
- 225
composer.lock
File diff suppressed because it is too large
View File


+ 30
- 20
package-lock.json View File

6
         "": {
6
         "": {
7
             "devDependencies": {
7
             "devDependencies": {
8
                 "@tailwindcss/forms": "^0.5.7",
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
                 "laravel-vite-plugin": "^1.0",
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
         "node_modules/@alloc/quick-lru": {
19
         "node_modules/@alloc/quick-lru": {
931
             }
931
             }
932
         },
932
         },
933
         "node_modules/caniuse-lite": {
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
             "dev": true,
937
             "dev": true,
938
             "funding": [
938
             "funding": [
939
                 {
939
                 {
1079
             "dev": true
1079
             "dev": true
1080
         },
1080
         },
1081
         "node_modules/electron-to-chromium": {
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
             "dev": true
1085
             "dev": true
1086
         },
1086
         },
1087
         "node_modules/emoji-regex": {
1087
         "node_modules/emoji-regex": {
1273
             }
1273
             }
1274
         },
1274
         },
1275
         "node_modules/glob": {
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
             "dev": true,
1279
             "dev": true,
1280
             "dependencies": {
1280
             "dependencies": {
1281
                 "foreground-child": "^3.1.0",
1281
                 "foreground-child": "^3.1.0",
1282
                 "jackspeak": "^3.1.2",
1282
                 "jackspeak": "^3.1.2",
1283
                 "minimatch": "^9.0.4",
1283
                 "minimatch": "^9.0.4",
1284
                 "minipass": "^7.1.2",
1284
                 "minipass": "^7.1.2",
1285
+                "package-json-from-dist": "^1.0.0",
1285
                 "path-scurry": "^1.11.1"
1286
                 "path-scurry": "^1.11.1"
1286
             },
1287
             },
1287
             "bin": {
1288
             "bin": {
1331
             }
1332
             }
1332
         },
1333
         },
1333
         "node_modules/is-core-module": {
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
             "dev": true,
1338
             "dev": true,
1338
             "dependencies": {
1339
             "dependencies": {
1339
-                "hasown": "^2.0.0"
1340
+                "hasown": "^2.0.2"
1341
+            },
1342
+            "engines": {
1343
+                "node": ">= 0.4"
1340
             },
1344
             },
1341
             "funding": {
1345
             "funding": {
1342
                 "url": "https://github.com/sponsors/ljharb"
1346
                 "url": "https://github.com/sponsors/ljharb"
1622
                 "node": ">= 6"
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
         "node_modules/path-key": {
1635
         "node_modules/path-key": {
1626
             "version": "3.1.1",
1636
             "version": "3.1.1",
1627
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1637
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",

+ 7
- 7
package.json View File

7
     },
7
     },
8
     "devDependencies": {
8
     "devDependencies": {
9
         "@tailwindcss/forms": "^0.5.7",
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
         "laravel-vite-plugin": "^1.0",
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 View File

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
     <thead class="divide-y divide-gray-200 dark:divide-white/5">
2
     <thead class="divide-y divide-gray-200 dark:divide-white/5">
3
         <tr class="bg-gray-50 dark:bg-white/5">
3
         <tr class="bg-gray-50 dark:bg-white/5">
4
             @foreach($report->getHeaders() as $index => $header)
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
                     <span class="text-sm font-semibold text-gray-950 dark:text-white">
6
                     <span class="text-sm font-semibold text-gray-950 dark:text-white">
7
                         {{ $header }}
7
                         {{ $header }}
8
                     </span>
8
                     </span>
10
             @endforeach
10
             @endforeach
11
         </tr>
11
         </tr>
12
     </thead>
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
             @endforeach
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
                             {{ $cell }}
39
                             {{ $cell }}
30
                         </div>
40
                         </div>
31
                     </x-filament-tables::cell>
41
                     </x-filament-tables::cell>
32
                 @endforeach
42
                 @endforeach
33
             </tr>
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
                 </x-filament-tables::cell>
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
         </tbody>
49
         </tbody>
50
     @endforeach
50
     @endforeach
51
     <tfoot>
51
     <tfoot>
52
         <tr class="bg-gray-50 dark:bg-white/5">
52
         <tr class="bg-gray-50 dark:bg-white/5">
53
             @foreach($report->getOverallTotals() as $index => $total)
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
                     <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
55
                     <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
56
                         {{ $total }}
56
                         {{ $total }}
57
                     </div>
57
                     </div>

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

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 View File

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 View File

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
-

Loading…
Cancel
Save