Explorar el Código

account transactions report wip

3.x
Andrew Wallo hace 1 año
padre
commit
8dc8af4694

+ 14
- 0
app/DTO/AccountTransactionDTO.php Ver fichero

1
+<?php
2
+
3
+namespace App\DTO;
4
+
5
+class AccountTransactionDTO
6
+{
7
+    public function __construct(
8
+        public string $date,
9
+        public string $description,
10
+        public string $debit,
11
+        public string $credit,
12
+        public string $balance,
13
+    ) {}
14
+}

+ 0
- 5
app/DTO/ReportCategoryDTO.php Ver fichero

4
 
4
 
5
 class ReportCategoryDTO
5
 class ReportCategoryDTO
6
 {
6
 {
7
-    /**
8
-     * @param  string[]  $header
9
-     * @param  string[][]  $data
10
-     * @param  string[]  $summary
11
-     */
12
     public function __construct(
7
     public function __construct(
13
         public array $header,
8
         public array $header,
14
         public array $data,
9
         public array $data,

+ 5
- 5
app/DTO/ReportDTO.php Ver fichero

11
          * @var AccountCategoryDTO[]
11
          * @var AccountCategoryDTO[]
12
          */
12
          */
13
         public array $categories,
13
         public array $categories,
14
-        public AccountBalanceDTO $overallTotal,
15
-        public array $fields,
14
+        public ?AccountBalanceDTO $overallTotal = null,
15
+        public array $fields = [],
16
     ) {}
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
             'fields' => $this->fields,
24
         ];
24
         ];
25
     }
25
     }
28
     {
28
     {
29
         return new static(
29
         return new static(
30
             $value['categories'],
30
             $value['categories'],
31
-            AccountBalanceDTO::fromLivewire($value['overallTotal']),
32
-            $value['fields'],
31
+            isset($value['overallTotal']) ? AccountBalanceDTO::fromLivewire($value['overallTotal']) : null,
32
+            $value['fields'] ?? [],
33
         );
33
         );
34
     }
34
     }
35
 }
35
 }

+ 2
- 1
app/Filament/Company/Pages/Reports.php Ver fichero

3
 namespace App\Filament\Company\Pages;
3
 namespace App\Filament\Company\Pages;
4
 
4
 
5
 use App\Filament\Company\Pages\Reports\AccountBalances;
5
 use App\Filament\Company\Pages\Reports\AccountBalances;
6
+use App\Filament\Company\Pages\Reports\AccountTransactions;
6
 use App\Filament\Company\Pages\Reports\TrialBalance;
7
 use App\Filament\Company\Pages\Reports\TrialBalance;
7
 use App\Infolists\Components\ReportEntry;
8
 use App\Infolists\Components\ReportEntry;
8
 use Filament\Infolists\Components\Section;
9
 use Filament\Infolists\Components\Section;
46
                             ->description('A record of all transactions for a company. The general ledger is the core of a company\'s financial records.')
47
                             ->description('A record of all transactions for a company. The general ledger is the core of a company\'s financial records.')
47
                             ->icon('heroicon-o-adjustments-horizontal')
48
                             ->icon('heroicon-o-adjustments-horizontal')
48
                             ->iconColor(Color::Amber)
49
                             ->iconColor(Color::Amber)
49
-                            ->url('#'),
50
+                            ->url(AccountTransactions::getUrl()),
50
                     ]),
51
                     ]),
51
             ]);
52
             ]);
52
     }
53
     }

+ 96
- 0
app/Filament/Company/Pages/Reports/AccountTransactions.php Ver fichero

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

+ 2
- 2
app/Services/AccountService.php Ver fichero

38
         return new Money($balances['net_movement'], $account->currency_code);
38
         return new Money($balances['net_movement'], $account->currency_code);
39
     }
39
     }
40
 
40
 
41
-    public function getStartingBalance(Account $account, string $startDate): ?Money
41
+    public function getStartingBalance(Account $account, string $startDate, bool $override = false): ?Money
42
     {
42
     {
43
-        if (in_array($account->category, [AccountCategory::Expense, AccountCategory::Revenue], true)) {
43
+        if ($override === false && in_array($account->category, [AccountCategory::Expense, AccountCategory::Revenue], true)) {
44
             return null;
44
             return null;
45
         }
45
         }
46
 
46
 

+ 91
- 0
app/Services/ReportService.php Ver fichero

5
 use App\DTO\AccountBalanceDTO;
5
 use App\DTO\AccountBalanceDTO;
6
 use App\DTO\AccountCategoryDTO;
6
 use App\DTO\AccountCategoryDTO;
7
 use App\DTO\AccountDTO;
7
 use App\DTO\AccountDTO;
8
+use App\DTO\AccountTransactionDTO;
8
 use App\DTO\ReportDTO;
9
 use App\DTO\ReportDTO;
9
 use App\Enums\Accounting\AccountCategory;
10
 use App\Enums\Accounting\AccountCategory;
10
 use App\Models\Accounting\Account;
11
 use App\Models\Accounting\Account;
12
+use App\Models\Accounting\JournalEntry;
11
 use App\Support\Column;
13
 use App\Support\Column;
12
 use App\Utilities\Currency\CurrencyAccessor;
14
 use App\Utilities\Currency\CurrencyAccessor;
15
+use Illuminate\Database\Eloquent\Builder;
13
 use Illuminate\Database\Eloquent\Collection;
16
 use Illuminate\Database\Eloquent\Collection;
14
 
17
 
15
 class ReportService
18
 class ReportService
90
         }, $balanceFields, $columns);
93
         }, $balanceFields, $columns);
91
     }
94
     }
92
 
95
 
96
+    public function buildAccountTransactionsReport(string $startDate, string $endDate, array $columns = []): ReportDTO
97
+    {
98
+        $accounts = Account::whereHas('journalEntries.transaction', static function (Builder $query) use ($startDate, $endDate) {
99
+            $query->whereBetween('posted_at', [$startDate, $endDate]);
100
+        })
101
+            ->with(['journalEntries' => static function ($query) use ($startDate, $endDate) {
102
+                $query->whereHas('transaction', static function (Builder $query) use ($startDate, $endDate) {
103
+                    $query->whereBetween('posted_at', [$startDate, $endDate]);
104
+                })
105
+                    ->with(['transaction' => static function ($query) {
106
+                        $query->select('id', 'posted_at', 'description');
107
+                    }])
108
+                    ->select('id', 'account_id', 'transaction_id', 'type', 'amount');
109
+            }])
110
+            ->select(['id', 'name', 'category', 'subtype_id', 'currency_code'])
111
+            ->get()
112
+            ->lazy();
113
+
114
+        $reportCategories = [];
115
+
116
+        foreach ($accounts as $account) {
117
+            $accountTransactions = [];
118
+            $startingBalance = $this->accountService->getStartingBalance($account, $startDate, true);
119
+
120
+            $currentBalance = $startingBalance?->getAmount() ?? 0;
121
+            $totalDebit = 0;
122
+            $totalCredit = 0;
123
+
124
+            $accountTransactions[] = new AccountTransactionDTO(
125
+                date: 'Starting Balance',
126
+                description: '',
127
+                debit: '',
128
+                credit: '',
129
+                balance: $startingBalance?->format() ?? 0,
130
+            );
131
+
132
+            $journalEntriesGroupedByTransaction = $account->journalEntries->groupBy('transaction_id');
133
+
134
+            foreach ($journalEntriesGroupedByTransaction as $transactionId => $journalEntries) {
135
+                $transaction = $journalEntries->first()->transaction;
136
+
137
+                $debitAmount = $journalEntries->sumDebits()->getAmount();
138
+                $creditAmount = $journalEntries->sumCredits()->getAmount();
139
+
140
+                // Adjust balance
141
+                $currentBalance += $debitAmount;
142
+                $currentBalance -= $creditAmount;
143
+
144
+                $totalDebit += $debitAmount;
145
+                $totalCredit += $creditAmount;
146
+
147
+                $accountTransactions[] = new AccountTransactionDTO(
148
+                    date: $transaction->posted_at->format('Y-m-d'),
149
+                    description: $transaction->description,
150
+                    debit: $debitAmount ? money($debitAmount, $account->currency_code)->format() : '',
151
+                    credit: $creditAmount ? money($creditAmount, $account->currency_code)->format() : '',
152
+                    balance: money($currentBalance, $account->currency_code)->format(),
153
+                );
154
+            }
155
+
156
+            $balanceChange = $currentBalance - ($startingBalance?->getAmount() ?? 0);
157
+
158
+            $accountTransactions[] = new AccountTransactionDTO(
159
+                date: 'Totals and Ending Balance',
160
+                description: '',
161
+                debit: money($totalDebit, $account->currency_code)->format(),
162
+                credit: money($totalCredit, $account->currency_code)->format(),
163
+                balance: money($currentBalance, $account->currency_code)->format(),
164
+            );
165
+
166
+            $accountTransactions[] = new AccountTransactionDTO(
167
+                date: 'Balance Change',
168
+                description: '',
169
+                debit: '',
170
+                credit: '',
171
+                balance: money($balanceChange, $account->currency_code)->format(),
172
+            );
173
+
174
+            $reportCategories[] = [
175
+                'category' => $account->name,
176
+                'under' => 'Under: ' . $account->category->getLabel() . ' > ' . $account->subtype->name,
177
+                'transactions' => $accountTransactions,
178
+            ];
179
+        }
180
+
181
+        return new ReportDTO(categories: $reportCategories, fields: $columns);
182
+    }
183
+
93
     private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $balanceFields, array $allFields, ?callable $initializeCategoryBalances = null): ReportDTO
184
     private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $balanceFields, array $allFields, ?callable $initializeCategoryBalances = null): ReportDTO
94
     {
185
     {
95
         $accountCategories = [];
186
         $accountCategories = [];

+ 81
- 0
app/Transformers/AccountTransactionReportTransformer.php Ver fichero

1
+<?php
2
+
3
+namespace App\Transformers;
4
+
5
+use App\DTO\AccountDTO;
6
+use App\DTO\AccountTransactionDTO;
7
+use App\DTO\ReportCategoryDTO;
8
+use App\Support\Column;
9
+
10
+class AccountTransactionReportTransformer extends BaseReportTransformer
11
+{
12
+    public function getTitle(): string
13
+    {
14
+        return 'Account Transactions';
15
+    }
16
+
17
+    public function getHeaders(): array
18
+    {
19
+        return array_map(fn (Column $column) => $column->getLabel(), $this->getColumns());
20
+    }
21
+
22
+    /**
23
+     * @return ReportCategoryDTO[]
24
+     */
25
+    public function getCategories(): array
26
+    {
27
+        $categories = [];
28
+
29
+        foreach ($this->report->categories as $categoryData) {
30
+            // Initialize header with account and category information
31
+            $header = [
32
+                [
33
+                    'column' => 'category',
34
+                    'value' => $categoryData['category'],
35
+                ],
36
+                [
37
+                    'column' => 'under',
38
+                    'value' => $categoryData['under'],
39
+                ],
40
+            ];
41
+
42
+            // Map transaction data
43
+            $data = array_map(function (AccountTransactionDTO $transaction) {
44
+                $row = [];
45
+
46
+                foreach ($this->getColumns() as $column) {
47
+                    $row[] = match ($column->getName()) {
48
+                        'date' => $transaction->date,
49
+                        'description' => $transaction->description,
50
+                        'debit' => $transaction->debit,
51
+                        'credit' => $transaction->credit,
52
+                        'balance' => $transaction->balance,
53
+                        default => '',
54
+                    };
55
+                }
56
+
57
+                return $row;
58
+            }, $categoryData['transactions']);
59
+
60
+            // Extract summary from the last transaction if it's "Totals and Ending Balance"
61
+            $summary = [];
62
+            if (count($data) > 1) {
63
+                $summaryTransaction = end($data);
64
+                $summary = array_slice($summaryTransaction, 1); // Skip the first element, which is the date
65
+            }
66
+
67
+            $categories[] = new ReportCategoryDTO(
68
+                header: $header,
69
+                data: $data,
70
+                summary: $summary
71
+            );
72
+        }
73
+
74
+        return $categories;
75
+    }
76
+
77
+    public function getOverallTotals(): array
78
+    {
79
+        return [];
80
+    }
81
+}

+ 297
- 220
composer.lock
La diferencia del archivo ha sido suprimido porque es demasiado grande
Ver fichero


+ 22
- 22
package-lock.json Ver fichero

931
             }
931
             }
932
         },
932
         },
933
         "node_modules/caniuse-lite": {
933
         "node_modules/caniuse-lite": {
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==",
934
+            "version": "1.0.30001639",
935
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001639.tgz",
936
+            "integrity": "sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==",
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.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==",
1082
+            "version": "1.4.816",
1083
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.816.tgz",
1084
+            "integrity": "sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==",
1085
             "dev": true
1085
             "dev": true
1086
         },
1086
         },
1087
         "node_modules/emoji-regex": {
1087
         "node_modules/emoji-regex": {
1471
             "dev": true
1471
             "dev": true
1472
         },
1472
         },
1473
         "node_modules/lru-cache": {
1473
         "node_modules/lru-cache": {
1474
-            "version": "10.2.2",
1475
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
1476
-            "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
1474
+            "version": "10.3.0",
1475
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz",
1476
+            "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==",
1477
             "dev": true,
1477
             "dev": true,
1478
             "engines": {
1478
             "engines": {
1479
                 "node": "14 || >=16.14"
1479
                 "node": "14 || >=16.14"
1532
             }
1532
             }
1533
         },
1533
         },
1534
         "node_modules/minimatch": {
1534
         "node_modules/minimatch": {
1535
-            "version": "9.0.4",
1536
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
1537
-            "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
1535
+            "version": "9.0.5",
1536
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
1537
+            "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
1538
             "dev": true,
1538
             "dev": true,
1539
             "dependencies": {
1539
             "dependencies": {
1540
                 "brace-expansion": "^2.0.1"
1540
                 "brace-expansion": "^2.0.1"
1700
             }
1700
             }
1701
         },
1701
         },
1702
         "node_modules/postcss": {
1702
         "node_modules/postcss": {
1703
-            "version": "8.4.38",
1704
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
1705
-            "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
1703
+            "version": "8.4.39",
1704
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
1705
+            "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
1706
             "dev": true,
1706
             "dev": true,
1707
             "funding": [
1707
             "funding": [
1708
                 {
1708
                 {
1720
             ],
1720
             ],
1721
             "dependencies": {
1721
             "dependencies": {
1722
                 "nanoid": "^3.3.7",
1722
                 "nanoid": "^3.3.7",
1723
-                "picocolors": "^1.0.0",
1723
+                "picocolors": "^1.0.1",
1724
                 "source-map-js": "^1.2.0"
1724
                 "source-map-js": "^1.2.0"
1725
             },
1725
             },
1726
             "engines": {
1726
             "engines": {
2339
             "dev": true
2339
             "dev": true
2340
         },
2340
         },
2341
         "node_modules/update-browserslist-db": {
2341
         "node_modules/update-browserslist-db": {
2342
-            "version": "1.0.16",
2343
-            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz",
2344
-            "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==",
2342
+            "version": "1.1.0",
2343
+            "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
2344
+            "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==",
2345
             "dev": true,
2345
             "dev": true,
2346
             "funding": [
2346
             "funding": [
2347
                 {
2347
                 {
2375
             "dev": true
2375
             "dev": true
2376
         },
2376
         },
2377
         "node_modules/vite": {
2377
         "node_modules/vite": {
2378
-            "version": "5.3.1",
2379
-            "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.1.tgz",
2380
-            "integrity": "sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ==",
2378
+            "version": "5.3.2",
2379
+            "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz",
2380
+            "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==",
2381
             "dev": true,
2381
             "dev": true,
2382
             "dependencies": {
2382
             "dependencies": {
2383
                 "esbuild": "^0.21.3",
2383
                 "esbuild": "^0.21.3",

+ 70
- 0
resources/views/components/company/tables/reports/account-transactions.blade.php Ver fichero

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">
3
+        <tr class="bg-gray-50 dark:bg-white/5">
4
+            @foreach($report->getHeaders() as $index => $header)
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">
7
+                        {{ $header }}
8
+                    </span>
9
+                </th>
10
+            @endforeach
11
+        </tr>
12
+    </thead>
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
+            <!-- Category Header -->
16
+            <tr class="bg-gray-50 dark:bg-white/5">
17
+                <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}" class="text-left">
18
+                    <div class="px-3 py-2">
19
+                        <div class="text-sm font-semibold text-gray-950 dark:text-white">
20
+                            {{ $category->header[0]['value'] }}
21
+                        </div>
22
+                        <div class="text-sm text-gray-700 dark:text-gray-300">
23
+                            {{ $category->header[1]['value'] }}
24
+                        </div>
25
+                    </div>
26
+                </x-filament-tables::cell>
27
+            </tr>
28
+            <!-- Transactions Data -->
29
+            @foreach($category->data as $dataIndex => $transaction)
30
+                <tr wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}"
31
+                    @class([
32
+                        'bg-gray-50 dark:bg-white/5' => $loop->first || $loop->last || $loop->remaining === 1,
33
+                    ])>
34
+                    @foreach($transaction as $cellIndex => $cell)
35
+                        <x-filament-tables::cell wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}-cell-{{ $cellIndex }}" class="{{ $report->getAlignmentClass($cellIndex) }}">
36
+                            <div
37
+                                @class([
38
+                                    'px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white',
39
+                                    'font-semibold' => $loop->parent->first || $loop->parent->last || $loop->parent->remaining === 1,
40
+                                ])>
41
+                                {{ $cell }}
42
+                            </div>
43
+                        </x-filament-tables::cell>
44
+                    @endforeach
45
+                </tr>
46
+            @endforeach
47
+            <!-- Spacer Row -->
48
+            @unless($loop->last)
49
+                <tr wire:key="category-{{ $categoryIndex }}-spacer">
50
+                    <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
51
+                        <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
52
+                    </x-filament-tables::cell>
53
+                </tr>
54
+            @endunless
55
+        </tbody>
56
+    @endforeach
57
+    @if (filled($report->getOverallTotals()))
58
+        <tfoot>
59
+            <tr class="bg-gray-50 dark:bg-white/5">
60
+                @foreach($report->getOverallTotals() as $index => $total)
61
+                    <x-filament-tables::cell wire:key="footer-total-{{ $index }}" class="{{ $report->getAlignmentClass($index) }}">
62
+                        <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
63
+                            {{ $total }}
64
+                        </div>
65
+                    </x-filament-tables::cell>
66
+                @endforeach
67
+            </tr>
68
+        </tfoot>
69
+    @endif
70
+</table>

+ 35
- 0
resources/views/filament/company/pages/reports/account-transactions.blade.php Ver fichero

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="divide-y divide-gray-200 overflow-x-auto overflow-y-hidden dark:divide-white/10 dark:border-t-white/10">
20
+                <div wire:init="loadReportData" class="flex items-center justify-center">
21
+                    <div wire:loading.delay wire:target="loadReportData">
22
+                        <x-filament::loading-indicator class="p-6 text-primary-700 dark:text-primary-300" />
23
+                    </div>
24
+                </div>
25
+
26
+                <div wire:loading.remove wire:target="loadReportData">
27
+                    @if($this->report)
28
+                        <x-company.tables.reports.account-transactions :report="$this->report" />
29
+                    @endif
30
+                </div>
31
+            </div>
32
+            <div class="es-table__footer-ctn border-t border-gray-200"></div>
33
+        </x-filament-tables::container>
34
+    </div>
35
+</x-filament-panels::page>

Loading…
Cancelar
Guardar