Browse Source

Merge pull request #62 from andrewdwallo/development-3.x

Development 3.x
3.x
Andrew Wallo 1 year ago
parent
commit
a7ed093557
No account linked to committer's email address
26 changed files with 856 additions and 613 deletions
  1. 1
    25
      app/DTO/AccountBalanceDTO.php
  2. 1
    19
      app/DTO/AccountCategoryDTO.php
  3. 2
    21
      app/DTO/AccountDTO.php
  4. 5
    0
      app/DTO/AccountTransactionDTO.php
  5. 1
    21
      app/DTO/ReportDTO.php
  6. 1
    1
      app/Filament/Company/Clusters/Settings/Resources/CurrencyResource/Pages/CreateCurrency.php
  7. 1
    1
      app/Filament/Company/Clusters/Settings/Resources/DiscountResource/Pages/CreateDiscount.php
  8. 1
    1
      app/Filament/Company/Clusters/Settings/Resources/TaxResource/Pages/CreateTax.php
  9. 2
    0
      app/Filament/Company/Pages/Reports/AccountTransactions.php
  10. 0
    2
      app/Filament/Company/Pages/Reports/TrialBalance.php
  11. 59
    12
      app/Services/AccountService.php
  12. 59
    25
      app/Services/ReportService.php
  13. 4
    1
      app/Transformers/AccountBalanceReportTransformer.php
  14. 5
    1
      app/Transformers/AccountTransactionReportTransformer.php
  15. 1
    16
      app/Transformers/BaseReportTransformer.php
  16. 4
    1
      app/Transformers/TrialBalanceReportTransformer.php
  17. 205
    205
      composer.lock
  18. 2
    2
      database/migrations/2024_01_01_234943_create_transactions_table.php
  19. 2
    2
      database/migrations/2024_01_11_062614_create_journal_entries_table.php
  20. 321
    126
      package-lock.json
  21. 9
    14
      resources/css/filament/user/theme.css
  22. 8
    8
      resources/views/components/company/reports/account-transactions-report-pdf.blade.php
  23. 14
    2
      resources/views/components/company/reports/report-pdf.blade.php
  24. 77
    54
      resources/views/components/company/tables/reports/account-transactions.blade.php
  25. 62
    45
      resources/views/components/company/tables/reports/detailed-report.blade.php
  26. 9
    8
      resources/views/filament/company/pages/reports/detailed-report.blade.php

+ 1
- 25
app/DTO/AccountBalanceDTO.php View File

2
 
2
 
3
 namespace App\DTO;
3
 namespace App\DTO;
4
 
4
 
5
-use Livewire\Wireable;
6
-
7
-class AccountBalanceDTO implements Wireable
5
+class AccountBalanceDTO
8
 {
6
 {
9
     public function __construct(
7
     public function __construct(
10
         public ?string $startingBalance,
8
         public ?string $startingBalance,
13
         public ?string $netMovement,
11
         public ?string $netMovement,
14
         public ?string $endingBalance,
12
         public ?string $endingBalance,
15
     ) {}
13
     ) {}
16
-
17
-    public function toLivewire(): array
18
-    {
19
-        return [
20
-            'startingBalance' => $this->startingBalance,
21
-            'debitBalance' => $this->debitBalance,
22
-            'creditBalance' => $this->creditBalance,
23
-            'netMovement' => $this->netMovement,
24
-            'endingBalance' => $this->endingBalance,
25
-        ];
26
-    }
27
-
28
-    public static function fromLivewire($value): static
29
-    {
30
-        return new static(
31
-            $value['startingBalance'],
32
-            $value['debitBalance'],
33
-            $value['creditBalance'],
34
-            $value['netMovement'],
35
-            $value['endingBalance'],
36
-        );
37
-    }
38
 }
14
 }

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

2
 
2
 
3
 namespace App\DTO;
3
 namespace App\DTO;
4
 
4
 
5
-use Livewire\Wireable;
6
-
7
-class AccountCategoryDTO implements Wireable
5
+class AccountCategoryDTO
8
 {
6
 {
9
     /**
7
     /**
10
      * @param  AccountDTO[]  $accounts
8
      * @param  AccountDTO[]  $accounts
13
         public array $accounts,
11
         public array $accounts,
14
         public AccountBalanceDTO $summary,
12
         public AccountBalanceDTO $summary,
15
     ) {}
13
     ) {}
16
-
17
-    public function toLivewire(): array
18
-    {
19
-        return [
20
-            'accounts' => $this->accounts,
21
-            'summary' => $this->summary->toLivewire(),
22
-        ];
23
-    }
24
-
25
-    public static function fromLivewire($value): static
26
-    {
27
-        return new static(
28
-            $value['accounts'],
29
-            AccountBalanceDTO::fromLivewire($value['summary']),
30
-        );
31
-    }
32
 }
14
 }

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

2
 
2
 
3
 namespace App\DTO;
3
 namespace App\DTO;
4
 
4
 
5
-use Livewire\Wireable;
6
-
7
-class AccountDTO implements Wireable
5
+class AccountDTO
8
 {
6
 {
9
     public function __construct(
7
     public function __construct(
10
         public string $accountName,
8
         public string $accountName,
11
         public string $accountCode,
9
         public string $accountCode,
10
+        public ?int $accountId,
12
         public AccountBalanceDTO $balance,
11
         public AccountBalanceDTO $balance,
13
     ) {}
12
     ) {}
14
-
15
-    public function toLivewire(): array
16
-    {
17
-        return [
18
-            'accountName' => $this->accountName,
19
-            'accountCode' => $this->accountCode,
20
-            'balance' => $this->balance->toLivewire(),
21
-        ];
22
-    }
23
-
24
-    public static function fromLivewire($value): static
25
-    {
26
-        return new static(
27
-            $value['accountName'],
28
-            $value['accountCode'],
29
-            AccountBalanceDTO::fromLivewire($value['balance']),
30
-        );
31
-    }
32
 }
13
 }

+ 5
- 0
app/DTO/AccountTransactionDTO.php View File

2
 
2
 
3
 namespace App\DTO;
3
 namespace App\DTO;
4
 
4
 
5
+use App\Enums\Accounting\TransactionType;
6
+
5
 class AccountTransactionDTO
7
 class AccountTransactionDTO
6
 {
8
 {
7
     public function __construct(
9
     public function __construct(
10
+        public ?int $id,
8
         public string $date,
11
         public string $date,
9
         public string $description,
12
         public string $description,
10
         public string $debit,
13
         public string $debit,
11
         public string $credit,
14
         public string $credit,
12
         public string $balance,
15
         public string $balance,
16
+        public ?TransactionType $type,
17
+        public ?string $tableAction,
13
     ) {}
18
     ) {}
14
 }
19
 }

+ 1
- 21
app/DTO/ReportDTO.php View File

2
 
2
 
3
 namespace App\DTO;
3
 namespace App\DTO;
4
 
4
 
5
-use Livewire\Wireable;
6
-
7
-class ReportDTO implements Wireable
5
+class ReportDTO
8
 {
6
 {
9
     public function __construct(
7
     public function __construct(
10
         /**
8
         /**
14
         public ?AccountBalanceDTO $overallTotal = null,
12
         public ?AccountBalanceDTO $overallTotal = null,
15
         public array $fields = [],
13
         public array $fields = [],
16
     ) {}
14
     ) {}
17
-
18
-    public function toLivewire(): array
19
-    {
20
-        return [
21
-            'categories' => $this->categories,
22
-            'overallTotal' => $this->overallTotal?->toLivewire(),
23
-            'fields' => $this->fields,
24
-        ];
25
-    }
26
-
27
-    public static function fromLivewire($value): static
28
-    {
29
-        return new static(
30
-            $value['categories'],
31
-            isset($value['overallTotal']) ? AccountBalanceDTO::fromLivewire($value['overallTotal']) : null,
32
-            $value['fields'] ?? [],
33
-        );
34
-    }
35
 }
15
 }

+ 1
- 1
app/Filament/Company/Clusters/Settings/Resources/CurrencyResource/Pages/CreateCurrency.php View File

39
             throw new Halt('No authenticated user found');
39
             throw new Halt('No authenticated user found');
40
         }
40
         }
41
 
41
 
42
-        return $this->handleRecordCreationWithUniqueField($data, new Currency(), $user);
42
+        return $this->handleRecordCreationWithUniqueField($data, new Currency, $user);
43
     }
43
     }
44
 }
44
 }

+ 1
- 1
app/Filament/Company/Clusters/Settings/Resources/DiscountResource/Pages/CreateDiscount.php View File

41
 
41
 
42
         $evaluatedTypes = [DiscountType::Sales, DiscountType::Purchase];
42
         $evaluatedTypes = [DiscountType::Sales, DiscountType::Purchase];
43
 
43
 
44
-        return $this->handleRecordCreationWithUniqueField($data, new Discount(), $user, 'type', $evaluatedTypes);
44
+        return $this->handleRecordCreationWithUniqueField($data, new Discount, $user, 'type', $evaluatedTypes);
45
     }
45
     }
46
 }
46
 }

+ 1
- 1
app/Filament/Company/Clusters/Settings/Resources/TaxResource/Pages/CreateTax.php View File

41
 
41
 
42
         $evaluatedTypes = [TaxType::Sales, TaxType::Purchase];
42
         $evaluatedTypes = [TaxType::Sales, TaxType::Purchase];
43
 
43
 
44
-        return $this->handleRecordCreationWithUniqueField($data, new Tax(), $user, 'type', $evaluatedTypes);
44
+        return $this->handleRecordCreationWithUniqueField($data, new Tax, $user, 'type', $evaluatedTypes);
45
     }
45
     }
46
 }
46
 }

+ 2
- 0
app/Filament/Company/Pages/Reports/AccountTransactions.php View File

18
 use Guava\FilamentClusters\Forms\Cluster;
18
 use Guava\FilamentClusters\Forms\Cluster;
19
 use Illuminate\Contracts\Support\Htmlable;
19
 use Illuminate\Contracts\Support\Htmlable;
20
 use Illuminate\Support\Collection;
20
 use Illuminate\Support\Collection;
21
+use Livewire\Attributes\Url;
21
 use Symfony\Component\HttpFoundation\StreamedResponse;
22
 use Symfony\Component\HttpFoundation\StreamedResponse;
22
 
23
 
23
 class AccountTransactions extends BaseReportPage
24
 class AccountTransactions extends BaseReportPage
32
 
33
 
33
     protected ExportService $exportService;
34
     protected ExportService $exportService;
34
 
35
 
36
+    #[Url]
35
     public ?string $account_id = 'all';
37
     public ?string $account_id = 'all';
36
 
38
 
37
     public function boot(ReportService $reportService, ExportService $exportService): void
39
     public function boot(ReportService $reportService, ExportService $exportService): void

+ 0
- 2
app/Filament/Company/Pages/Reports/TrialBalance.php View File

43
                 ->alignment(Alignment::Left),
43
                 ->alignment(Alignment::Left),
44
             Column::make('debit_balance')
44
             Column::make('debit_balance')
45
                 ->label('Debit')
45
                 ->label('Debit')
46
-                ->toggleable()
47
                 ->alignment(Alignment::Right),
46
                 ->alignment(Alignment::Right),
48
             Column::make('credit_balance')
47
             Column::make('credit_balance')
49
                 ->label('Credit')
48
                 ->label('Credit')
50
-                ->toggleable()
51
                 ->alignment(Alignment::Right),
49
                 ->alignment(Alignment::Right),
52
         ];
50
         ];
53
     }
51
     }

+ 59
- 12
app/Services/AccountService.php View File

5
 use App\Contracts\AccountHandler;
5
 use App\Contracts\AccountHandler;
6
 use App\Enums\Accounting\AccountCategory;
6
 use App\Enums\Accounting\AccountCategory;
7
 use App\Models\Accounting\Account;
7
 use App\Models\Accounting\Account;
8
+use App\Models\Accounting\JournalEntry;
8
 use App\Models\Accounting\Transaction;
9
 use App\Models\Accounting\Transaction;
9
-use App\Models\Banking\BankAccount;
10
 use App\Repositories\Accounting\JournalEntryRepository;
10
 use App\Repositories\Accounting\JournalEntryRepository;
11
 use App\Utilities\Currency\CurrencyAccessor;
11
 use App\Utilities\Currency\CurrencyAccessor;
12
 use App\ValueObjects\Money;
12
 use App\ValueObjects\Money;
13
+use Illuminate\Support\Facades\DB;
13
 
14
 
14
 class AccountService implements AccountHandler
15
 class AccountService implements AccountHandler
15
 {
16
 {
65
         return new Money($endingBalance, $account->currency_code);
66
         return new Money($endingBalance, $account->currency_code);
66
     }
67
     }
67
 
68
 
69
+    public function getRetainedEarnings(string $startDate): Money
70
+    {
71
+        $revenue = JournalEntry::whereHas('account', static function ($query) {
72
+            $query->where('category', AccountCategory::Revenue);
73
+        })
74
+            ->where('type', 'credit')
75
+            ->whereHas('transaction', static function ($query) use ($startDate) {
76
+                $query->where('posted_at', '<=', $startDate);
77
+            })
78
+            ->sum('amount');
79
+
80
+        $expense = JournalEntry::whereHas('account', static function ($query) {
81
+            $query->where('category', AccountCategory::Expense);
82
+        })
83
+            ->where('type', 'debit')
84
+            ->whereHas('transaction', static function ($query) use ($startDate) {
85
+                $query->where('posted_at', '<=', $startDate);
86
+            })
87
+            ->sum('amount');
88
+
89
+        $retainedEarnings = $revenue - $expense;
90
+
91
+        return new Money($retainedEarnings, CurrencyAccessor::getDefaultCurrency());
92
+    }
93
+
68
     private function calculateNetMovementByCategory(AccountCategory $category, int $debitBalance, int $creditBalance): int
94
     private function calculateNetMovementByCategory(AccountCategory $category, int $debitBalance, int $creditBalance): int
69
     {
95
     {
70
         return match ($category) {
96
         return match ($category) {
125
 
151
 
126
     public function getTotalBalanceForAllBankAccounts(string $startDate, string $endDate): Money
152
     public function getTotalBalanceForAllBankAccounts(string $startDate, string $endDate): Money
127
     {
153
     {
128
-        $bankAccounts = BankAccount::with('account')
129
-            ->get();
154
+        $accountIds = Account::whereHas('bankAccount')
155
+            ->pluck('id')
156
+            ->toArray();
130
 
157
 
131
-        $totalBalance = 0;
132
-
133
-        foreach ($bankAccounts as $bankAccount) {
134
-            $account = $bankAccount->account;
135
-
136
-            if ($account) {
137
-                $endingBalance = $this->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
138
-                $totalBalance += $endingBalance;
139
-            }
158
+        if (empty($accountIds)) {
159
+            return new Money(0, CurrencyAccessor::getDefaultCurrency());
140
         }
160
         }
141
 
161
 
162
+        $result = DB::table('journal_entries')
163
+            ->join('transactions', 'journal_entries.transaction_id', '=', 'transactions.id')
164
+            ->whereIn('journal_entries.account_id', $accountIds)
165
+            ->where('transactions.posted_at', '<=', $endDate)
166
+            ->selectRaw('
167
+            SUM(CASE
168
+                WHEN transactions.posted_at <= ? AND journal_entries.type = "debit" THEN journal_entries.amount
169
+                WHEN transactions.posted_at <= ? AND journal_entries.type = "credit" THEN -journal_entries.amount
170
+                ELSE 0
171
+            END) AS totalStartingBalance,
172
+            SUM(CASE
173
+                WHEN transactions.posted_at BETWEEN ? AND ? AND journal_entries.type = "debit" THEN journal_entries.amount
174
+                WHEN transactions.posted_at BETWEEN ? AND ? AND journal_entries.type = "credit" THEN -journal_entries.amount
175
+                ELSE 0
176
+            END) AS totalNetMovement
177
+        ', [
178
+                $startDate,
179
+                $startDate,
180
+                $startDate,
181
+                $endDate,
182
+                $startDate,
183
+                $endDate,
184
+            ])
185
+            ->first();
186
+
187
+        $totalBalance = $result->totalStartingBalance + $result->totalNetMovement;
188
+
142
         return new Money($totalBalance, CurrencyAccessor::getDefaultCurrency());
189
         return new Money($totalBalance, CurrencyAccessor::getDefaultCurrency());
143
     }
190
     }
144
 
191
 

+ 59
- 25
app/Services/ReportService.php View File

12
 use App\Support\Column;
12
 use App\Support\Column;
13
 use App\Utilities\Currency\CurrencyAccessor;
13
 use App\Utilities\Currency\CurrencyAccessor;
14
 use Illuminate\Database\Eloquent\Builder;
14
 use Illuminate\Database\Eloquent\Builder;
15
-use Illuminate\Database\Eloquent\Collection;
16
 use Illuminate\Database\Eloquent\Relations\Relation;
15
 use Illuminate\Database\Eloquent\Relations\Relation;
16
+use Illuminate\Support\Collection;
17
 
17
 
18
 class ReportService
18
 class ReportService
19
 {
19
 {
46
     private function getCategoryGroupedAccounts(array $allCategories): Collection
46
     private function getCategoryGroupedAccounts(array $allCategories): Collection
47
     {
47
     {
48
         return Account::whereHas('journalEntries')
48
         return Account::whereHas('journalEntries')
49
-            ->select('id', 'name', 'currency_code', 'category', 'code')
49
+            ->select(['id', 'name', 'currency_code', 'category', 'code'])
50
             ->get()
50
             ->get()
51
             ->groupBy(fn (Account $account) => $account->category->getPluralLabel())
51
             ->groupBy(fn (Account $account) => $account->category->getPluralLabel())
52
             ->sortBy(static fn (Collection $groupedAccounts, string $key) => array_search($key, $allCategories, true));
52
             ->sortBy(static fn (Collection $groupedAccounts, string $key) => array_search($key, $allCategories, true));
74
         );
74
         );
75
     }
75
     }
76
 
76
 
77
-    public function buildTrialBalanceReport(string $startDate, string $endDate, array $columns = []): ReportDTO
78
-    {
79
-        $allCategories = $this->accountService->getAccountCategoryOrder();
80
-
81
-        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
82
-
83
-        $balanceFields = ['debit_balance', 'credit_balance'];
84
-
85
-        return $this->buildReport($allCategories, $categoryGroupedAccounts, function (Account $account) use ($startDate, $endDate) {
86
-            $endingBalance = $this->accountService->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
87
-
88
-            if ($endingBalance === 0) {
89
-                return [];
90
-            }
91
-
92
-            return $this->calculateTrialBalance($account->category, $endingBalance);
93
-        }, $balanceFields, $columns);
94
-    }
95
-
96
     public function buildAccountTransactionsReport(string $startDate, string $endDate, ?array $columns = null, ?string $accountId = 'all'): ReportDTO
77
     public function buildAccountTransactionsReport(string $startDate, string $endDate, ?array $columns = null, ?string $accountId = 'all'): ReportDTO
97
     {
78
     {
98
         $columns ??= [];
79
         $columns ??= [];
103
                 $query->whereHas('transaction', function (Builder $query) use ($startDate, $endDate) {
84
                 $query->whereHas('transaction', function (Builder $query) use ($startDate, $endDate) {
104
                     $query->whereBetween('posted_at', [$startDate, $endDate]);
85
                     $query->whereBetween('posted_at', [$startDate, $endDate]);
105
                 })
86
                 })
106
-                    ->with(['transaction' => function (Relation $query) {
107
-                        $query->select(['id', 'description', 'posted_at']);
108
-                    }])
87
+                    ->with('transaction:id,type,description,posted_at')
109
                     ->select(['account_id', 'transaction_id'])
88
                     ->select(['account_id', 'transaction_id'])
110
                     ->selectRaw('SUM(CASE WHEN type = "debit" THEN amount ELSE 0 END) AS total_debit')
89
                     ->selectRaw('SUM(CASE WHEN type = "debit" THEN amount ELSE 0 END) AS total_debit')
111
                     ->selectRaw('SUM(CASE WHEN type = "credit" THEN amount ELSE 0 END) AS total_credit')
90
                     ->selectRaw('SUM(CASE WHEN type = "credit" THEN amount ELSE 0 END) AS total_credit')
134
             $totalCredit = 0;
113
             $totalCredit = 0;
135
 
114
 
136
             $accountTransactions[] = new AccountTransactionDTO(
115
             $accountTransactions[] = new AccountTransactionDTO(
116
+                id: null,
137
                 date: 'Starting Balance',
117
                 date: 'Starting Balance',
138
                 description: '',
118
                 description: '',
139
                 debit: '',
119
                 debit: '',
140
                 credit: '',
120
                 credit: '',
141
                 balance: $startingBalance?->formatInDefaultCurrency() ?? 0,
121
                 balance: $startingBalance?->formatInDefaultCurrency() ?? 0,
122
+                type: null,
123
+                tableAction: null,
142
             );
124
             );
143
 
125
 
144
             foreach ($account->journalEntries as $journalEntry) {
126
             foreach ($account->journalEntries as $journalEntry) {
150
                 $currentBalance -= $journalEntry->total_credit;
132
                 $currentBalance -= $journalEntry->total_credit;
151
 
133
 
152
                 $accountTransactions[] = new AccountTransactionDTO(
134
                 $accountTransactions[] = new AccountTransactionDTO(
135
+                    id: $transaction->id,
153
                     date: $transaction->posted_at->format('Y-m-d'),
136
                     date: $transaction->posted_at->format('Y-m-d'),
154
                     description: $transaction->description ?? '',
137
                     description: $transaction->description ?? '',
155
                     debit: $journalEntry->total_debit ? money($journalEntry->total_debit, $defaultCurrency)->format() : '',
138
                     debit: $journalEntry->total_debit ? money($journalEntry->total_debit, $defaultCurrency)->format() : '',
156
                     credit: $journalEntry->total_credit ? money($journalEntry->total_credit, $defaultCurrency)->format() : '',
139
                     credit: $journalEntry->total_credit ? money($journalEntry->total_credit, $defaultCurrency)->format() : '',
157
                     balance: money($currentBalance, $defaultCurrency)->format(),
140
                     balance: money($currentBalance, $defaultCurrency)->format(),
141
+                    type: $transaction->type,
142
+                    tableAction: $transaction->type->isJournal() ? 'updateJournalTransaction' : 'updateTransaction',
158
                 );
143
                 );
159
             }
144
             }
160
 
145
 
161
             $balanceChange = $currentBalance - ($startingBalance?->getAmount() ?? 0);
146
             $balanceChange = $currentBalance - ($startingBalance?->getAmount() ?? 0);
162
 
147
 
163
             $accountTransactions[] = new AccountTransactionDTO(
148
             $accountTransactions[] = new AccountTransactionDTO(
149
+                id: null,
164
                 date: 'Totals and Ending Balance',
150
                 date: 'Totals and Ending Balance',
165
                 description: '',
151
                 description: '',
166
                 debit: money($totalDebit, $defaultCurrency)->format(),
152
                 debit: money($totalDebit, $defaultCurrency)->format(),
167
                 credit: money($totalCredit, $defaultCurrency)->format(),
153
                 credit: money($totalCredit, $defaultCurrency)->format(),
168
                 balance: money($currentBalance, $defaultCurrency)->format(),
154
                 balance: money($currentBalance, $defaultCurrency)->format(),
155
+                type: null,
156
+                tableAction: null,
169
             );
157
             );
170
 
158
 
171
             $accountTransactions[] = new AccountTransactionDTO(
159
             $accountTransactions[] = new AccountTransactionDTO(
160
+                id: null,
172
                 date: 'Balance Change',
161
                 date: 'Balance Change',
173
                 description: '',
162
                 description: '',
174
                 debit: '',
163
                 debit: '',
175
                 credit: '',
164
                 credit: '',
176
                 balance: money($balanceChange, $defaultCurrency)->format(),
165
                 balance: money($balanceChange, $defaultCurrency)->format(),
166
+                type: null,
167
+                tableAction: null,
177
             );
168
             );
178
 
169
 
179
             $reportCategories[] = [
170
             $reportCategories[] = [
186
         return new ReportDTO(categories: $reportCategories, fields: $columns);
177
         return new ReportDTO(categories: $reportCategories, fields: $columns);
187
     }
178
     }
188
 
179
 
189
-    private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $balanceFields, array $allFields, ?callable $initializeCategoryBalances = null): ReportDTO
180
+    private function buildReport(array $allCategories, Collection $categoryGroupedAccounts, callable $balanceCalculator, array $balanceFields, array $allFields, ?callable $initializeCategoryBalances = null, bool $includeRetainedEarnings = false, ?string $startDate = null): ReportDTO
190
     {
181
     {
191
         $accountCategories = [];
182
         $accountCategories = [];
192
         $reportTotalBalances = array_fill_keys($balanceFields, 0);
183
         $reportTotalBalances = array_fill_keys($balanceFields, 0);
221
                 $categoryAccounts[] = new AccountDTO(
212
                 $categoryAccounts[] = new AccountDTO(
222
                     $account->name,
213
                     $account->name,
223
                     $account->code,
214
                     $account->code,
215
+                    $account->id,
224
                     $formattedAccountBalances,
216
                     $formattedAccountBalances,
225
                 );
217
                 );
226
             }
218
             }
227
 
219
 
220
+            if ($includeRetainedEarnings && $categoryName === AccountCategory::Equity->getPluralLabel()) {
221
+                $retainedEarnings = $this->accountService->getRetainedEarnings($startDate);
222
+                $retainedEarningsAmount = $retainedEarnings->getAmount();
223
+
224
+                if ($retainedEarningsAmount >= 0) {
225
+                    $categorySummaryBalances['credit_balance'] += $retainedEarningsAmount;
226
+                    $categoryAccounts[] = new AccountDTO(
227
+                        'Retained Earnings',
228
+                        'RE',
229
+                        null,
230
+                        $this->formatBalances(['debit_balance' => 0, 'credit_balance' => $retainedEarningsAmount])
231
+                    );
232
+                } else {
233
+                    $categorySummaryBalances['debit_balance'] += abs($retainedEarningsAmount);
234
+                    $categoryAccounts[] = new AccountDTO(
235
+                        'Retained Earnings',
236
+                        'RE',
237
+                        null,
238
+                        $this->formatBalances(['debit_balance' => abs($retainedEarningsAmount), 'credit_balance' => 0])
239
+                    );
240
+                }
241
+            }
242
+
228
             foreach ($balanceFields as $field) {
243
             foreach ($balanceFields as $field) {
229
                 if (array_key_exists($field, $categorySummaryBalances)) {
244
                 if (array_key_exists($field, $categorySummaryBalances)) {
230
                     $reportTotalBalances[$field] += $categorySummaryBalances[$field];
245
                     $reportTotalBalances[$field] += $categorySummaryBalances[$field];
252
         }
267
         }
253
     }
268
     }
254
 
269
 
270
+    public function buildTrialBalanceReport(string $startDate, string $endDate, array $columns = []): ReportDTO
271
+    {
272
+        $allCategories = $this->accountService->getAccountCategoryOrder();
273
+
274
+        $categoryGroupedAccounts = $this->getCategoryGroupedAccounts($allCategories);
275
+
276
+        $balanceFields = ['debit_balance', 'credit_balance'];
277
+
278
+        return $this->buildReport($allCategories, $categoryGroupedAccounts, function (Account $account) use ($startDate, $endDate) {
279
+            $endingBalance = $this->accountService->getEndingBalance($account, $startDate, $endDate)?->getAmount() ?? 0;
280
+
281
+            if ($endingBalance === 0) {
282
+                return [];
283
+            }
284
+
285
+            return $this->calculateTrialBalance($account->category, $endingBalance);
286
+        }, $balanceFields, $columns, null, true, $startDate);
287
+    }
288
+
255
     private function calculateTrialBalance(AccountCategory $category, int $endingBalance): array
289
     private function calculateTrialBalance(AccountCategory $category, int $endingBalance): array
256
     {
290
     {
257
         if (in_array($category, [AccountCategory::Asset, AccountCategory::Expense], true)) {
291
         if (in_array($category, [AccountCategory::Asset, AccountCategory::Expense], true)) {

+ 4
- 1
app/Transformers/AccountBalanceReportTransformer.php View File

43
                 foreach ($this->getColumns() as $column) {
43
                 foreach ($this->getColumns() as $column) {
44
                     $row[] = match ($column->getName()) {
44
                     $row[] = match ($column->getName()) {
45
                         'account_code' => $account->accountCode,
45
                         'account_code' => $account->accountCode,
46
-                        'account_name' => $account->accountName,
46
+                        'account_name' => [
47
+                            'name' => $account->accountName,
48
+                            'id' => $account->accountId ?? null,
49
+                        ],
47
                         'starting_balance' => $account->balance->startingBalance ?? '',
50
                         'starting_balance' => $account->balance->startingBalance ?? '',
48
                         'debit_balance' => $account->balance->debitBalance,
51
                         'debit_balance' => $account->balance->debitBalance,
49
                         'credit_balance' => $account->balance->creditBalance,
52
                         'credit_balance' => $account->balance->creditBalance,

+ 5
- 1
app/Transformers/AccountTransactionReportTransformer.php View File

52
                 foreach ($this->getColumns() as $column) {
52
                 foreach ($this->getColumns() as $column) {
53
                     $row[] = match ($column->getName()) {
53
                     $row[] = match ($column->getName()) {
54
                         'date' => $transaction->date,
54
                         'date' => $transaction->date,
55
-                        'description' => $transaction->description,
55
+                        'description' => [
56
+                            'id' => $transaction->id,
57
+                            'description' => $transaction->description,
58
+                            'tableAction' => $transaction->tableAction,
59
+                        ],
56
                         'debit' => $transaction->debit,
60
                         'debit' => $transaction->debit,
57
                         'credit' => $transaction->credit,
61
                         'credit' => $transaction->credit,
58
                         'balance' => $transaction->balance,
62
                         'balance' => $transaction->balance,

+ 1
- 16
app/Transformers/BaseReportTransformer.php View File

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 Filament\Support\Enums\Alignment;
8
-use Livewire\Wireable;
9
 
8
 
10
-abstract class BaseReportTransformer implements ExportableReport, Wireable
9
+abstract class BaseReportTransformer implements ExportableReport
11
 {
10
 {
12
     protected ReportDTO $report;
11
     protected ReportDTO $report;
13
 
12
 
40
 
39
 
41
         return 'text-left';
40
         return 'text-left';
42
     }
41
     }
43
-
44
-    public function toLivewire(): array
45
-    {
46
-        return [
47
-            'report' => $this->report->toLivewire(),
48
-        ];
49
-    }
50
-
51
-    public static function fromLivewire($value): static
52
-    {
53
-        return new static(
54
-            ReportDTO::fromLivewire($value['report']),
55
-        );
56
-    }
57
 }
42
 }

+ 4
- 1
app/Transformers/TrialBalanceReportTransformer.php View File

43
                 foreach ($this->getColumns() as $column) {
43
                 foreach ($this->getColumns() as $column) {
44
                     $row[] = match ($column->getName()) {
44
                     $row[] = match ($column->getName()) {
45
                         'account_code' => $account->accountCode,
45
                         'account_code' => $account->accountCode,
46
-                        'account_name' => $account->accountName,
46
+                        'account_name' => [
47
+                            'name' => $account->accountName,
48
+                            'id' => $account->accountId ?? null,
49
+                        ],
47
                         'debit_balance' => $account->balance->debitBalance,
50
                         'debit_balance' => $account->balance->debitBalance,
48
                         'credit_balance' => $account->balance->creditBalance,
51
                         'credit_balance' => $account->balance->creditBalance,
49
                         default => '',
52
                         default => '',

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


+ 2
- 2
database/migrations/2024_01_01_234943_create_transactions_table.php View File

31
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
31
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
32
             $table->timestamps();
32
             $table->timestamps();
33
 
33
 
34
-            $table->index(['company_id', 'account_id', 'posted_at']);
35
-            $table->index(['company_id', 'bank_account_id', 'posted_at']);
34
+            $table->index(['account_id', 'posted_at']);
35
+            $table->index(['bank_account_id', 'posted_at']);
36
         });
36
         });
37
     }
37
     }
38
 
38
 

+ 2
- 2
database/migrations/2024_01_11_062614_create_journal_entries_table.php View File

23
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
23
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
24
             $table->timestamps();
24
             $table->timestamps();
25
 
25
 
26
-            $table->index(['company_id', 'account_id', 'type']);
27
-            $table->index(['company_id', 'account_id', 'transaction_id']);
26
+            $table->index(['account_id', 'type']);
27
+            $table->index(['account_id', 'transaction_id']);
28
         });
28
         });
29
     }
29
     }
30
 
30
 

+ 321
- 126
package-lock.json
File diff suppressed because it is too large
View File


+ 9
- 14
resources/css/filament/user/theme.css View File

8
     z-index: 1;
8
     z-index: 1;
9
 }
9
 }
10
 
10
 
11
-.fi-input-password-revealable::-ms-reveal {
12
-    display: none;
13
-}
14
-
15
 .fi-body::before {
11
 .fi-body::before {
16
     content: '';
12
     content: '';
17
-    position: absolute;
13
+    position: fixed;
18
     top: 0;
14
     top: 0;
19
     left: 0;
15
     left: 0;
20
     width: 100%;
16
     width: 100%;
21
     height: 100%;
17
     height: 100%;
22
-    background-image:
23
-        linear-gradient(99.6deg,
24
-            rgba(232, 233, 235, 1) 10.6%,
25
-            rgba(240, 241, 243, 1) 32.9%,
26
-            rgba(248, 249, 251, 0.7) 50%,
27
-            rgba(240, 241, 243, 1) 67.1%,
28
-            rgba(232, 233, 235, 1) 83.4%);
18
+    background-image: linear-gradient(99.6deg,
19
+    rgba(232, 233, 235, 1) 10.6%,
20
+    rgba(240, 241, 243, 1) 32.9%,
21
+    rgba(248, 249, 251, 0.7) 50%,
22
+    rgba(240, 241, 243, 1) 67.1%,
23
+    rgba(232, 233, 235, 1) 83.4%);
29
     pointer-events: none;
24
     pointer-events: none;
30
     z-index: -1;
25
     z-index: -1;
31
 }
26
 }
38
 
33
 
39
 :is(.dark .fi-body)::before {
34
 :is(.dark .fi-body)::before {
40
     content: '';
35
     content: '';
41
-    position: absolute;
36
+    position: fixed;
42
     top: 0;
37
     top: 0;
43
     right: 0;
38
     right: 0;
44
     background-image: radial-gradient(
39
     background-image: radial-gradient(
49
         rgba(var(--primary-900), 0.5) 45%,
44
         rgba(var(--primary-900), 0.5) 45%,
50
         rgba(var(--primary-950), 0.3) 60%,
45
         rgba(var(--primary-950), 0.3) 60%,
51
         rgba(var(--primary-950), 0.1) 75%,
46
         rgba(var(--primary-950), 0.1) 75%,
52
-        rgba(3,7,18,0) 100%
47
+        rgba(3, 7, 18, 0) 100%
53
     );
48
     );
54
     width: 100%;
49
     width: 100%;
55
     height: 100%;
50
     height: 100%;

+ 8
- 8
resources/views/components/company/reports/account-transactions-report-pdf.blade.php View File

35
             color: #374151;
35
             color: #374151;
36
         }
36
         }
37
 
37
 
38
+        .whitespace-normal {
39
+            white-space: normal;
40
+        }
41
+
42
+        .whitespace-nowrap {
43
+            white-space: nowrap;
44
+        }
45
+
38
         .title {
46
         .title {
39
             font-size: 1.5rem;
47
             font-size: 1.5rem;
40
         }
48
         }
61
             border-bottom: 1px solid #d1d5db; /* Gray border for all rows */
69
             border-bottom: 1px solid #d1d5db; /* Gray border for all rows */
62
         }
70
         }
63
 
71
 
64
-        .whitespace-normal {
65
-            white-space: normal;
66
-        }
67
-
68
-        .whitespace-nowrap {
69
-            white-space: nowrap;
70
-        }
71
-
72
         .category-header-row > td {
72
         .category-header-row > td {
73
             background-color: #f3f4f6; /* Gray background for category names */
73
             background-color: #f3f4f6; /* Gray background for category names */
74
             font-weight: 600;
74
             font-weight: 600;

+ 14
- 2
resources/views/components/company/reports/report-pdf.blade.php View File

35
             color: #374151;
35
             color: #374151;
36
         }
36
         }
37
 
37
 
38
+        .whitespace-normal {
39
+            white-space: normal;
40
+        }
41
+
42
+        .whitespace-nowrap {
43
+            white-space: nowrap;
44
+        }
45
+
38
         .title {
46
         .title {
39
             font-size: 1.5rem;
47
             font-size: 1.5rem;
40
         }
48
         }
109
         @foreach($category->data as $account)
117
         @foreach($category->data as $account)
110
             <tr>
118
             <tr>
111
                 @foreach($account as $index => $cell)
119
                 @foreach($account as $index => $cell)
112
-                    <td class="{{ $report->getAlignmentClass($index) }}">
113
-                        {{ $cell }}
120
+                    <td class="{{ $report->getAlignmentClass($index) }} {{ $index === 1 ? 'whitespace-normal' : 'whitespace-nowrap' }}">
121
+                        @if(isset($cell['id']) && isset($cell['name']))
122
+                            {{ $cell['name'] }}
123
+                        @else
124
+                            {{ $cell }}
125
+                        @endif
114
                     </td>
126
                     </td>
115
                 @endforeach
127
                 @endforeach
116
             </tr>
128
             </tr>

+ 77
- 54
resources/views/components/company/tables/reports/account-transactions.blade.php View File

1
 <table class="w-full table-auto divide-y divide-gray-200 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">
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) }}">
3
+    <tr class="bg-gray-50 dark:bg-white/5">
4
+        @foreach($report->getHeaders() as $index => $header)
5
+            <th wire:key="header-{{ $index }}"
6
+                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
                     <span class="text-sm font-semibold text-gray-950 dark:text-white">
7
                         {{ $header }}
8
                         {{ $header }}
8
                     </span>
9
                     </span>
9
-                </th>
10
-            @endforeach
11
-        </tr>
10
+            </th>
11
+        @endforeach
12
+    </tr>
12
     </thead>
13
     </thead>
13
     @foreach($report->getCategories() as $categoryIndex => $category)
14
     @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
-                        @foreach ($category->header as $headerRow)
20
-                            <div class="text-sm {{ $loop->first ? 'font-semibold text-gray-950 dark:text-white' : 'text-gray-500 dark:text-white/50' }}">
21
-                                @foreach ($headerRow as $headerValue)
22
-                                    @if (!empty($headerValue))
23
-                                        {{ $headerValue }}
24
-                                    @endif
25
-                                @endforeach
26
-                            </div>
27
-                        @endforeach
28
-                    </div>
29
-                </x-filament-tables::cell>
30
-            </tr>
31
-            <!-- Transactions Data -->
32
-            @foreach($category->data as $dataIndex => $transaction)
33
-                <tr wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}"
34
-                    @class([
35
-                        'bg-gray-50 dark:bg-white/5' => $loop->first || $loop->last || $loop->remaining === 1,
36
-                    ])
37
-                >
38
-                    @foreach($transaction as $cellIndex => $cell)
39
-                        <x-filament-tables::cell
40
-                            wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}-cell-{{ $cellIndex }}"
41
-                             @class([
42
-                                $report->getAlignmentClass($cellIndex),
43
-                                'whitespace-normal' => $cellIndex === 1,
15
+        <tbody wire:key="category-{{ $categoryIndex }}"
16
+               class="divide-y divide-gray-200 whitespace-nowrap dark:divide-white/5">
17
+        <!-- Category Header -->
18
+        <tr class="bg-gray-50 dark:bg-white/5">
19
+            <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}" class="text-left">
20
+                <div class="px-3 py-2">
21
+                    @foreach ($category->header as $headerRow)
22
+                        <div
23
+                            class="text-sm {{ $loop->first ? 'font-semibold text-gray-950 dark:text-white' : 'text-gray-500 dark:text-white/50' }}">
24
+                            @foreach ($headerRow as $headerValue)
25
+                                @if (!empty($headerValue))
26
+                                    {{ $headerValue }}
27
+                                @endif
28
+                            @endforeach
29
+                        </div>
30
+                    @endforeach
31
+                </div>
32
+            </x-filament-tables::cell>
33
+        </tr>
34
+        <!-- Transactions Data -->
35
+        @foreach($category->data as $dataIndex => $transaction)
36
+            <tr wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}"
37
+                @class([
38
+                    'bg-gray-50 dark:bg-white/5' => $loop->first || $loop->last || $loop->remaining === 1,
39
+                ])
40
+            >
41
+                @foreach($transaction as $cellIndex => $cell)
42
+                    <x-filament-tables::cell
43
+                        wire:key="category-{{ $categoryIndex }}-data-{{ $dataIndex }}-cell-{{ $cellIndex }}"
44
+                        @class([
45
+                           $report->getAlignmentClass($cellIndex),
46
+                           'whitespace-normal' => $cellIndex === 1,
47
+                       ])
48
+                    >
49
+                        <div
50
+                            @class([
51
+                                'px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white',
52
+                                'font-semibold' => $loop->parent->first || $loop->parent->last || $loop->parent->remaining === 1,
44
                             ])
53
                             ])
45
                         >
54
                         >
46
-                            <div
47
-                                @class([
48
-                                    'px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white',
49
-                                    'font-semibold' => $loop->parent->first || $loop->parent->last || $loop->parent->remaining === 1,
50
-                                ])
51
-                            >
55
+                            @if(is_array($cell) && isset($cell['description']))
56
+                                @if(isset($cell['id']) && $cell['tableAction'])
57
+                                    <x-filament::link
58
+                                        :href="\App\Filament\Company\Pages\Accounting\Transactions::getUrl(parameters: [
59
+                                            'tableAction' => $cell['tableAction'],
60
+                                            'tableActionRecord' => $cell['id'],
61
+                                        ])"
62
+                                        target="_blank"
63
+                                        color="primary"
64
+                                        icon="heroicon-o-arrow-top-right-on-square"
65
+                                        :icon-position="\Filament\Support\Enums\IconPosition::After"
66
+                                        icon-size="w-4 h-4 min-w-4 min-h-4"
67
+                                    >
68
+                                        {{ $cell['description'] }}
69
+                                    </x-filament::link>
70
+                                @else
71
+                                    {{ $cell['description'] }}
72
+                                @endif
73
+                            @else
52
                                 {{ $cell }}
74
                                 {{ $cell }}
53
-                            </div>
54
-                        </x-filament-tables::cell>
55
-                    @endforeach
56
-                </tr>
57
-            @endforeach
58
-            <!-- Spacer Row -->
59
-            @unless($loop->last)
60
-                <tr wire:key="category-{{ $categoryIndex }}-spacer">
61
-                    <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
62
-                        <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
75
+                            @endif
76
+                        </div>
63
                     </x-filament-tables::cell>
77
                     </x-filament-tables::cell>
64
-                </tr>
65
-            @endunless
78
+                @endforeach
79
+            </tr>
80
+        @endforeach
81
+        <!-- Spacer Row -->
82
+        @unless($loop->last)
83
+            <tr wire:key="category-{{ $categoryIndex }}-spacer">
84
+                <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
85
+                    <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
86
+                </x-filament-tables::cell>
87
+            </tr>
88
+        @endunless
66
         </tbody>
89
         </tbody>
67
     @endforeach
90
     @endforeach
68
 </table>
91
 </table>

+ 62
- 45
resources/views/components/company/tables/reports/detailed-report.blade.php View File

1
-<table class="w-full table-auto divide-y divide-gray-200 dark:divide-white/5">
1
+<table class="w-full table-auto divide-y divide-gray-200 dark:divide-white/5" x-data>
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">
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) }}">
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 class="divide-y divide-gray-200 whitespace-nowrap dark:divide-white/5">
3
         <tr class="bg-gray-50 dark:bg-white/5">
15
         <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">
16
+            @foreach($category->header as $headerIndex => $header)
17
+                <x-filament-tables::cell class="{{ $report->getAlignmentClass($headerIndex) }}">
18
+                    <div class="px-3 py-2 text-sm font-semibold text-gray-950 dark:text-white">
7
                         {{ $header }}
19
                         {{ $header }}
8
-                    </span>
9
-                </th>
20
+                    </div>
21
+                </x-filament-tables::cell>
10
             @endforeach
22
             @endforeach
11
         </tr>
23
         </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
-            <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">
24
+        @foreach($category->data as $dataIndex => $account)
25
+            <tr>
26
+                @foreach($account as $cellIndex => $cell)
27
+                    <x-filament-tables::cell class="{{ $report->getAlignmentClass($cellIndex) }}">
28
+                        <div class="px-3 py-4 text-sm leading-6 text-gray-950 dark:text-white">
29
+                            @if(is_array($cell) && isset($cell['name']))
30
+                                @if(isset($cell['id']))
31
+                                    <x-filament::link
32
+                                        color="primary"
33
+                                        target="_blank"
34
+                                        icon="heroicon-o-arrow-top-right-on-square"
35
+                                        :icon-position="\Filament\Support\Enums\IconPosition::After"
36
+                                        :icon-size="\Filament\Support\Enums\IconSize::Small"
37
+                                        href="{{ \App\Filament\Company\Pages\Reports\AccountTransactions::getUrl(['account_id' => $cell['id']]) }}"
38
+                                    >
39
+                                        {{ $cell['name'] }}
40
+                                    </x-filament::link>
41
+                                @else
42
+                                    {{ $cell['name'] }}
43
+                                @endif
44
+                            @else
29
                                 {{ $cell }}
45
                                 {{ $cell }}
30
-                            </div>
31
-                        </x-filament-tables::cell>
32
-                    @endforeach
33
-                </tr>
34
-            @endforeach
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">
39
-                            {{ $cell }}
46
+                            @endif
40
                         </div>
47
                         </div>
41
                     </x-filament-tables::cell>
48
                     </x-filament-tables::cell>
42
                 @endforeach
49
                 @endforeach
43
             </tr>
50
             </tr>
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>
47
-                </x-filament-tables::cell>
48
-            </tr>
49
-        </tbody>
50
-    @endforeach
51
-    <tfoot>
52
-        <tr class="bg-gray-50 dark:bg-white/5">
53
-            @foreach($report->getOverallTotals() as $index => $total)
54
-                <x-filament-tables::cell wire:key="footer-total-{{ $index }}" class="{{ $report->getAlignmentClass($index) }}">
51
+        @endforeach
52
+        <tr>
53
+            @foreach($category->summary as $summaryIndex => $cell)
54
+                <x-filament-tables::cell class="{{ $report->getAlignmentClass($summaryIndex) }}">
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
+                        {{ $cell }}
57
                     </div>
57
                     </div>
58
                 </x-filament-tables::cell>
58
                 </x-filament-tables::cell>
59
             @endforeach
59
             @endforeach
60
         </tr>
60
         </tr>
61
+        <tr>
62
+            <x-filament-tables::cell colspan="{{ count($report->getHeaders()) }}">
63
+                <div class="px-3 py-2 leading-6 invisible">Hidden Text</div>
64
+            </x-filament-tables::cell>
65
+        </tr>
66
+        </tbody>
67
+    @endforeach
68
+    <tfoot>
69
+    <tr class="bg-gray-50 dark:bg-white/5">
70
+        @foreach($report->getOverallTotals() as $index => $total)
71
+            <x-filament-tables::cell class="{{ $report->getAlignmentClass($index) }}">
72
+                <div class="px-3 py-2 text-sm leading-6 font-semibold text-gray-950 dark:text-white">
73
+                    {{ $total }}
74
+                </div>
75
+            </x-filament-tables::cell>
76
+        @endforeach
77
+    </tr>
61
     </tfoot>
78
     </tfoot>
62
 </table>
79
 </table>

+ 9
- 8
resources/views/filament/company/pages/reports/detailed-report.blade.php View File

14
                 </x-filament::button>
14
                 </x-filament::button>
15
             </div>
15
             </div>
16
         </form>
16
         </form>
17
-        <div class="relative divide-y divide-gray-200 overflow-x-auto dark:divide-white/10 dark:border-t-white/10 min-h-64">
18
-            <div wire:init="loadReportData" class="flex items-center justify-center w-full h-full absolute">
19
-                <div wire:loading wire:target="loadReportData">
20
-                    <x-filament::loading-indicator class="p-6 text-primary-700 dark:text-primary-300" />
21
-                </div>
22
-            </div>
23
-
17
+        <div wire:init="loadReportData"
18
+             class="relative divide-y divide-gray-200 overflow-x-auto dark:divide-white/10 dark:border-t-white/10 min-h-64">
24
             @if($this->reportLoaded)
19
             @if($this->reportLoaded)
25
                 <div wire:loading.remove wire:target="loadReportData">
20
                 <div wire:loading.remove wire:target="loadReportData">
26
                     @if($this->report)
21
                     @if($this->report)
27
-                        <x-company.tables.reports.detailed-report :report="$this->report" />
22
+                        <x-company.tables.reports.detailed-report :report="$this->report"/>
28
                     @endif
23
                     @endif
29
                 </div>
24
                 </div>
25
+            @else
26
+                <div class="absolute inset-0 flex items-center justify-center">
27
+                    <div wire:loading wire:target="loadReportData">
28
+                        <x-filament::loading-indicator class="p-6 text-primary-700 dark:text-primary-300"/>
29
+                    </div>
30
+                </div>
30
             @endif
31
             @endif
31
         </div>
32
         </div>
32
         <div class="es-table__footer-ctn border-t border-gray-200"></div>
33
         <div class="es-table__footer-ctn border-t border-gray-200"></div>

Loading…
Cancel
Save