浏览代码

refactor: navigation and double-entry accounting

3.x
wallo 1年前
父节点
当前提交
ae1a3ff004
共有 33 个文件被更改,包括 671 次插入394 次删除
  1. 0
    4
      app/Filament/Company/Pages/Accounting/AccountChart.php
  2. 1
    1
      app/Filament/Company/Pages/Reports.php
  3. 2
    5
      app/Filament/Company/Pages/Reports/AccountBalances.php
  4. 0
    4
      app/Filament/Company/Pages/Service/ConnectedAccount.php
  5. 0
    4
      app/Filament/Company/Pages/Service/LiveCurrency.php
  6. 0
    4
      app/Filament/Company/Pages/Setting/Appearance.php
  7. 0
    4
      app/Filament/Company/Pages/Setting/CompanyDefault.php
  8. 0
    4
      app/Filament/Company/Pages/Setting/CompanyProfile.php
  9. 0
    4
      app/Filament/Company/Pages/Setting/Invoice.php
  10. 0
    4
      app/Filament/Company/Pages/Setting/Localization.php
  11. 63
    60
      app/Filament/Company/Resources/Accounting/TransactionResource.php
  12. 0
    11
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/CreateTransaction.php
  13. 0
    19
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/EditTransaction.php
  14. 0
    19
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ListTransactions.php
  15. 39
    49
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ManageTransaction.php
  16. 0
    4
      app/Filament/Company/Resources/Banking/AccountResource.php
  17. 0
    4
      app/Filament/Company/Resources/Core/DepartmentResource.php
  18. 0
    5
      app/Filament/Company/Resources/Setting/CurrencyResource.php
  19. 0
    4
      app/Filament/Company/Resources/Setting/DiscountResource.php
  20. 0
    4
      app/Filament/Company/Resources/Setting/TaxResource.php
  21. 83
    0
      app/Jobs/ProcessTransactionImport.php
  22. 52
    0
      app/Listeners/ConfigureCompanyNavigation.php
  23. 9
    36
      app/Listeners/HandleTransactionImport.php
  24. 1
    2
      app/Models/Accounting/Transaction.php
  25. 4
    4
      app/Observers/TransactionObserver.php
  26. 2
    2
      app/Services/AccountService.php
  27. 6
    8
      app/Services/TransactionService.php
  28. 91
    91
      composer.lock
  29. 268
    0
      config/dompdf.php
  30. 1
    2
      database/migrations/2024_01_01_234943_create_transactions_table.php
  31. 22
    19
      package-lock.json
  32. 4
    13
      resources/views/components/company/reports/account-balances.blade.php
  33. 23
    0
      resources/views/filament/company/components/tables/actions/mark-as-reviewed.blade.php

+ 0
- 4
app/Filament/Company/Pages/Accounting/AccountChart.php 查看文件

@@ -26,12 +26,8 @@ use Livewire\Attributes\Url;
26 26
 
27 27
 class AccountChart extends Page
28 28
 {
29
-    protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-list';
30
-
31 29
     protected static ?string $title = 'Chart of Accounts';
32 30
 
33
-    protected static ?string $navigationGroup = 'Accounting';
34
-
35 31
     protected static ?string $slug = 'accounting/chart';
36 32
 
37 33
     protected static string $view = 'filament.company.pages.accounting.chart';

+ 1
- 1
app/Filament/Company/Pages/Reports.php 查看文件

@@ -11,7 +11,7 @@ use Filament\Support\Colors\Color;
11 11
 
12 12
 class Reports extends Page
13 13
 {
14
-    protected static ?string $navigationIcon = 'heroicon-o-document-text';
14
+    protected static ?string $navigationIcon = 'heroicon-o-document-chart-bar';
15 15
 
16 16
     protected static string $view = 'filament.company.pages.reports';
17 17
 

+ 2
- 5
app/Filament/Company/Pages/Reports/AccountBalances.php 查看文件

@@ -69,11 +69,7 @@ class AccountBalances extends Page
69 69
 
70 70
     public function loadAccountBalances(): void
71 71
     {
72
-        $startTime = microtime(true);
73 72
         $this->accountBalanceReport = $this->accountService->buildAccountBalanceReport($this->startDate, $this->endDate);
74
-        $endTime = microtime(true);
75
-        $executionTime = ($endTime - $startTime);
76
-        info('Account balance report loaded in ' . $executionTime . ' seconds');
77 73
     }
78 74
 
79 75
     protected function getHeaderActions(): array
@@ -109,7 +105,7 @@ class AccountBalances extends Page
109 105
             'accountBalanceReport' => $this->accountBalanceReport,
110 106
             'startDate' => Carbon::parse($this->startDate)->format('M d, Y'),
111 107
             'endDate' => Carbon::parse($this->endDate)->format('M d, Y'),
112
-        ])->setPaper('a4')->setOption(['defaultFont' => 'sans-serif', 'isPhpEnabled' => true]);
108
+        ])->setPaper('a4');
113 109
 
114 110
         return response()->streamDownload(function () use ($pdf) {
115 111
             echo $pdf->stream();
@@ -206,6 +202,7 @@ class AccountBalances extends Page
206 202
             'Y' => $this->processCalendarYear($param1),
207 203
             'Q' => $this->processCalendarQuarter($param1, $param2),
208 204
             'M' => $this->processMonth("{$param1}-{$param2}"),
205
+            'Custom' => null,
209 206
         };
210 207
     }
211 208
 

+ 0
- 4
app/Filament/Company/Pages/Service/ConnectedAccount.php 查看文件

@@ -10,12 +10,8 @@ use Illuminate\Contracts\Support\Htmlable;
10 10
 
11 11
 class ConnectedAccount extends Page
12 12
 {
13
-    protected static ?string $navigationIcon = 'heroicon-o-building-library';
14
-
15 13
     protected static ?string $title = 'Connected Accounts';
16 14
 
17
-    protected static ?string $navigationGroup = 'Services';
18
-
19 15
     protected static ?string $slug = 'services/connected-accounts';
20 16
 
21 17
     protected static string $view = 'filament.company.pages.service.connected-account';

+ 0
- 4
app/Filament/Company/Pages/Service/LiveCurrency.php 查看文件

@@ -12,12 +12,8 @@ use Livewire\Attributes\Url;
12 12
 
13 13
 class LiveCurrency extends Page
14 14
 {
15
-    protected static ?string $navigationIcon = 'icon-currency-exchange';
16
-
17 15
     protected static ?string $title = 'Live Currency';
18 16
 
19
-    protected static ?string $navigationGroup = 'Services';
20
-
21 17
     protected static ?string $slug = 'services/live-currency';
22 18
 
23 19
     protected static string $view = 'filament.company.pages.service.live-currency';

+ 0
- 4
app/Filament/Company/Pages/Setting/Appearance.php 查看文件

@@ -35,12 +35,8 @@ class Appearance extends Page
35 35
 {
36 36
     use InteractsWithFormActions;
37 37
 
38
-    protected static ?string $navigationIcon = 'heroicon-o-paint-brush';
39
-
40 38
     protected static ?string $title = 'Appearance';
41 39
 
42
-    protected static ?string $navigationGroup = 'Settings';
43
-
44 40
     protected static ?string $slug = 'settings/appearance';
45 41
 
46 42
     protected static string $view = 'filament.company.pages.setting.appearance';

+ 0
- 4
app/Filament/Company/Pages/Setting/CompanyDefault.php 查看文件

@@ -34,10 +34,6 @@ class CompanyDefault extends Page
34 34
 {
35 35
     use InteractsWithFormActions;
36 36
 
37
-    protected static ?string $navigationIcon = 'heroicon-o-adjustments-vertical';
38
-
39
-    protected static ?string $navigationGroup = 'Settings';
40
-
41 37
     protected static ?string $title = 'Default';
42 38
 
43 39
     protected static ?string $slug = 'settings/default';

+ 0
- 4
app/Filament/Company/Pages/Setting/CompanyProfile.php 查看文件

@@ -39,12 +39,8 @@ class CompanyProfile extends Page
39 39
 {
40 40
     use InteractsWithFormActions;
41 41
 
42
-    protected static ?string $navigationIcon = 'heroicon-o-building-office-2';
43
-
44 42
     protected static ?string $title = 'Company Profile';
45 43
 
46
-    protected static ?string $navigationGroup = 'Settings';
47
-
48 44
     protected static ?string $slug = 'settings/company-profile';
49 45
 
50 46
     protected static string $view = 'filament.company.pages.setting.company-profile';

+ 0
- 4
app/Filament/Company/Pages/Setting/Invoice.php 查看文件

@@ -43,12 +43,8 @@ class Invoice extends Page
43 43
 {
44 44
     use InteractsWithFormActions;
45 45
 
46
-    protected static ?string $navigationIcon = 'heroicon-o-document-duplicate';
47
-
48 46
     protected static ?string $title = 'Invoice';
49 47
 
50
-    protected static ?string $navigationGroup = 'Settings';
51
-
52 48
     protected static ?string $slug = 'settings/invoice';
53 49
 
54 50
     protected static string $view = 'filament.company.pages.setting.invoice';

+ 0
- 4
app/Filament/Company/Pages/Setting/Localization.php 查看文件

@@ -38,12 +38,8 @@ class Localization extends Page
38 38
 {
39 39
     use InteractsWithFormActions;
40 40
 
41
-    protected static ?string $navigationIcon = 'heroicon-o-language';
42
-
43 41
     protected static ?string $title = 'Localization';
44 42
 
45
-    protected static ?string $navigationGroup = 'Settings';
46
-
47 43
     protected static ?string $slug = 'settings/localization';
48 44
 
49 45
     protected static string $view = 'filament.company.pages.setting.localization';

+ 63
- 60
app/Filament/Company/Resources/Accounting/TransactionResource.php 查看文件

@@ -3,6 +3,7 @@
3 3
 namespace App\Filament\Company\Resources\Accounting;
4 4
 
5 5
 use App\Enums\Accounting\AccountCategory;
6
+use App\Enums\Accounting\AccountType;
6 7
 use App\Enums\DateFormat;
7 8
 use App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
8 9
 use App\Models\Accounting\Account;
@@ -12,6 +13,7 @@ use App\Models\Setting\Localization;
12 13
 use Filament\Forms;
13 14
 use Filament\Forms\Form;
14 15
 use Filament\Resources\Resource;
16
+use Filament\Support\Colors\Color;
15 17
 use Filament\Support\Enums\FontWeight;
16 18
 use Filament\Support\Enums\MaxWidth;
17 19
 use Filament\Tables;
@@ -23,7 +25,7 @@ class TransactionResource extends Resource
23 25
 {
24 26
     protected static ?string $model = Transaction::class;
25 27
 
26
-    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
28
+    protected static ?string $modelLabel = 'Transaction';
27 29
 
28 30
     public static function form(Form $form): Form
29 31
     {
@@ -32,60 +34,31 @@ class TransactionResource extends Resource
32 34
                 Forms\Components\DatePicker::make('posted_at')
33 35
                     ->label('Date')
34 36
                     ->required()
35
-                    ->displayFormat('Y-m-d')
36
-                    ->default(now()->format('Y-m-d')),
37
+                    ->displayFormat('Y-m-d'),
37 38
                 Forms\Components\TextInput::make('description')
38 39
                     ->label('Description'),
39 40
                 Forms\Components\Select::make('bank_account_id')
40 41
                     ->label('Account')
41
-                    ->options(fn () => static::getBankAccountOptions())
42
+                    ->options(static fn () => static::getBankAccountOptions())
42 43
                     ->live()
43 44
                     ->searchable()
44
-                    ->preload()
45 45
                     ->required(),
46
-                Forms\Components\Select::make('method')
46
+                Forms\Components\Select::make('type')
47 47
                     ->label('Type')
48 48
                     ->live()
49 49
                     ->options([
50 50
                         'deposit' => 'Deposit',
51 51
                         'withdrawal' => 'Withdrawal',
52 52
                     ])
53
-                    ->default('deposit')
54
-                    ->afterStateUpdated(static function (Forms\Set $set, $state) {
55
-                        if ($state === 'deposit') {
56
-                            $account = Account::where('category', AccountCategory::Revenue)
57
-                                ->where('name', 'Uncategorized Income')->first();
58
-
59
-                            if ($account->exists()) {
60
-                                $set('account_id', $account->id);
61
-                            }
62
-                        } else {
63
-                            $account = Account::where('category', AccountCategory::Expense)
64
-                                ->where('name', 'Uncategorized Expense')->first();
65
-
66
-                            if ($account->exists()) {
67
-                                $set('account_id', $account->id);
68
-                            }
69
-                        }
70
-                    })
71
-                    ->required(),
53
+                    ->required()
54
+                    ->afterStateUpdated(static fn (Forms\Set $set, $state) => $set('account_id', Pages\ManageTransaction::getUncategorizedAccountByType($state)?->id)),
72 55
                 Forms\Components\TextInput::make('amount')
73 56
                     ->label('Amount')
74
-                    ->money(static function (Forms\Get $get) {
75
-                        $bankAccount = $get('bank_account_id');
76
-                        $bankAccount = BankAccount::find($bankAccount);
77
-                        $account = $bankAccount->account ?? null;
78
-
79
-                        if ($account) {
80
-                            return $account->currency_code;
81
-                        }
82
-
83
-                        return 'USD';
84
-                    })
57
+                    ->money(static fn (Forms\Get $get) => BankAccount::find($get('bank_account_id'))?->account?->currency_code ?? 'USD')
85 58
                     ->required(),
86 59
                 Forms\Components\Select::make('account_id')
87 60
                     ->label('Category')
88
-                    ->options(static fn (Forms\Get $get) => static::getAccountOptions($get('method')))
61
+                    ->options(static fn (Forms\Get $get) => static::getAccountOptions($get('type')))
89 62
                     ->searchable()
90 63
                     ->preload()
91 64
                     ->required(),
@@ -109,41 +82,58 @@ class TransactionResource extends Resource
109 82
 
110 83
                         return Carbon::parse($state)->translatedFormat($dateFormat);
111 84
                     }),
85
+                Tables\Columns\TextColumn::make('description')
86
+                    ->limit(30)
87
+                    ->label('Description'),
112 88
                 Tables\Columns\TextColumn::make('bankAccount.account.name')
113 89
                     ->label('Account')
114 90
                     ->sortable(),
115
-                Tables\Columns\TextColumn::make('description')
116
-                    ->limit(50)
117
-                    ->label('Description'),
118 91
                 Tables\Columns\TextColumn::make('account.name')
119 92
                     ->label('Category'),
120 93
                 Tables\Columns\TextColumn::make('amount')
121 94
                     ->label('Amount')
122 95
                     ->sortable()
123
-                    ->weight(FontWeight::Medium)
124
-                    ->color(static fn (Transaction $record) => $record->type === 'expense' ? 'danger' : null)
96
+                    ->weight(static fn (Transaction $record) => $record->reviewed ? null : FontWeight::SemiBold)
97
+                    ->color(static fn (Transaction $record) => $record->type === 'deposit' ? Color::rgb('rgb(' . Color::Green[700] . ')') : null)
125 98
                     ->currency(static fn (Transaction $record) => $record->bankAccount->account->currency_code ?? 'USD', true),
126 99
             ])
100
+            ->recordClasses(static fn (Transaction $record) => $record->reviewed ? 'bg-primary-300/10' : null)
127 101
             ->defaultSort('posted_at', 'desc')
128 102
             ->filters([
129 103
                 //
130 104
             ])
131 105
             ->actions([
132
-                Tables\Actions\EditAction::make()
133
-                    ->modalWidth(MaxWidth::ThreeExtraLarge)
134
-                    ->stickyModalHeader()
135
-                    ->stickyModalFooter()
136
-                    ->mutateFormDataUsing(static function (array $data): array {
137
-                        $method = $data['method'];
138
-
139
-                        if ($method === 'deposit') {
140
-                            $data['type'] = 'income';
141
-                        } else {
142
-                            $data['type'] = 'expense';
143
-                        }
144
-
145
-                        return $data;
146
-                    }),
106
+                Tables\Actions\Action::make('markAsReviewed')
107
+                    ->label('Mark as Reviewed')
108
+                    ->view('filament.company.components.tables.actions.mark-as-reviewed')
109
+                    ->icon(static fn (Transaction $record) => $record->reviewed ? 'heroicon-s-check-circle' : 'heroicon-o-check-circle')
110
+                    ->color(static fn (Transaction $record, Tables\Actions\Action $action) => match (static::determineTransactionState($record, $action)) {
111
+                        'reviewed' => 'primary',
112
+                        'unreviewed' => Color::rgb('rgb(' . Color::Gray[600] . ')'),
113
+                        'uncategorized' => 'gray',
114
+                    })
115
+                    ->tooltip(static fn (Transaction $record, Tables\Actions\Action $action) => match (static::determineTransactionState($record, $action)) {
116
+                        'reviewed' => 'Reviewed',
117
+                        'unreviewed' => 'Mark as Reviewed',
118
+                        'uncategorized' => 'Categorize first to mark as reviewed',
119
+                    })
120
+                    ->disabled(static fn (Transaction $record) => in_array($record->account->type, [AccountType::UncategorizedRevenue, AccountType::UncategorizedExpense], true))
121
+                    ->action(fn (Transaction $record) => $record->update(['reviewed' => ! $record->reviewed])),
122
+                Tables\Actions\ActionGroup::make([
123
+                    Tables\Actions\EditAction::make()
124
+                        ->modalWidth(MaxWidth::ThreeExtraLarge)
125
+                        ->stickyModalHeader()
126
+                        ->stickyModalFooter(),
127
+                    Tables\Actions\DeleteAction::make(),
128
+                    Tables\Actions\ReplicateAction::make()
129
+                        ->excludeAttributes(['created_by', 'updated_by', 'created_at', 'updated_at'])
130
+                        ->modal(false)
131
+                        ->beforeReplicaSaved(static function (Transaction $replica) {
132
+                            $replica->description = '(Copy of) ' . $replica->description;
133
+                        }),
134
+                ])
135
+                    ->dropdownPlacement('bottom-start')
136
+                    ->dropdownWidth('max-w-fit'),
147 137
             ])
148 138
             ->bulkActions([
149 139
                 Tables\Actions\BulkActionGroup::make([
@@ -152,6 +142,19 @@ class TransactionResource extends Resource
152 142
             ]);
153 143
     }
154 144
 
145
+    protected static function determineTransactionState(Transaction $record, Tables\Actions\Action $action): string
146
+    {
147
+        if ($record->reviewed) {
148
+            return 'reviewed';
149
+        }
150
+
151
+        if ($record->reviewed === false && $action->isEnabled()) {
152
+            return 'unreviewed';
153
+        }
154
+
155
+        return 'uncategorized';
156
+    }
157
+
155 158
     public static function getRelations(): array
156 159
     {
157 160
         return [
@@ -175,9 +178,9 @@ class TransactionResource extends Resource
175 178
             ->toArray();
176 179
     }
177 180
 
178
-    public static function getAccountOptions(mixed $method)
181
+    public static function getAccountOptions(string $type)
179 182
     {
180
-        $excludedCategory = match ($method) {
183
+        $excludedCategory = match ($type) {
181 184
             'deposit' => AccountCategory::Expense,
182 185
             'withdrawal' => AccountCategory::Revenue,
183 186
         };
@@ -185,7 +188,7 @@ class TransactionResource extends Resource
185 188
         $accounts = Account::whereNot('category', $excludedCategory)->get();
186 189
 
187 190
         return $accounts->groupBy(fn (Account $account) => $account->category->getLabel())
188
-            ->map(fn (Collection $accounts, string $category) => $accounts->mapWithKeys(fn (Account $account) => [$account->id => $account->name]))
191
+            ->map(fn (Collection $accounts, string $category) => $accounts->mapWithKeys(static fn (Account $account) => [$account->id => $account->name]))
189 192
             ->toArray();
190 193
     }
191 194
 }

+ 0
- 11
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/CreateTransaction.php 查看文件

@@ -1,11 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Accounting\TransactionResource;
6
-use Filament\Resources\Pages\CreateRecord;
7
-
8
-class CreateTransaction extends CreateRecord
9
-{
10
-    protected static string $resource = TransactionResource::class;
11
-}

+ 0
- 19
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/EditTransaction.php 查看文件

@@ -1,19 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Accounting\TransactionResource;
6
-use Filament\Actions;
7
-use Filament\Resources\Pages\EditRecord;
8
-
9
-class EditTransaction extends EditRecord
10
-{
11
-    protected static string $resource = TransactionResource::class;
12
-
13
-    protected function getHeaderActions(): array
14
-    {
15
-        return [
16
-            Actions\DeleteAction::make(),
17
-        ];
18
-    }
19
-}

+ 0
- 19
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ListTransactions.php 查看文件

@@ -1,19 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Accounting\TransactionResource;
6
-use Filament\Actions;
7
-use Filament\Resources\Pages\ListRecords;
8
-
9
-class ListTransactions extends ListRecords
10
-{
11
-    protected static string $resource = TransactionResource::class;
12
-
13
-    protected function getHeaderActions(): array
14
-    {
15
-        return [
16
-            Actions\CreateAction::make(),
17
-        ];
18
-    }
19
-}

+ 39
- 49
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ManageTransaction.php 查看文件

@@ -17,56 +17,46 @@ class ManageTransaction extends ManageRecords
17 17
     protected function getHeaderActions(): array
18 18
     {
19 19
         return [
20
-            Actions\CreateAction::make('addIncome')
21
-                ->label('Add Income')
22
-                ->modalWidth(MaxWidth::ThreeExtraLarge)
23
-                ->stickyModalHeader()
24
-                ->stickyModalFooter()
25
-                ->button()
26
-                ->outlined()
27
-                ->fillForm(static fn (): array => [
28
-                    'method' => 'deposit',
29
-                    'posted_at' => now()->format('Y-m-d'),
30
-                    'bank_account_id' => BankAccount::first()->isEnabled() ? BankAccount::first()->id : null,
31
-                    'amount' => '0.00',
32
-                    'account_id' => Account::where('category', AccountCategory::Revenue)->where('name', 'Uncategorized Income')->first()->id,
33
-                ])
34
-                ->mutateFormDataUsing(function (array $data): array {
35
-                    $method = $data['method'];
36
-
37
-                    if ($method === 'deposit') {
38
-                        $data['type'] = 'income';
39
-                    } else {
40
-                        $data['type'] = 'expense';
41
-                    }
42
-
43
-                    return $data;
44
-                }),
45
-            Actions\CreateAction::make('addExpense')
46
-                ->label('Add Expense')
47
-                ->modalWidth(MaxWidth::ThreeExtraLarge)
48
-                ->stickyModalHeader()
49
-                ->stickyModalFooter()
50
-                ->button()
51
-                ->outlined()
52
-                ->fillForm(static fn (): array => [
53
-                    'method' => 'withdrawal',
54
-                    'posted_at' => now()->format('Y-m-d'),
55
-                    'bank_account_id' => BankAccount::first()->isEnabled() ? BankAccount::first()->id : null,
56
-                    'amount' => '0.00',
57
-                    'account_id' => Account::where('category', AccountCategory::Expense)->where('name', 'Uncategorized Expense')->first()->id,
58
-                ])
59
-                ->mutateFormDataUsing(function (array $data): array {
60
-                    $method = $data['method'];
20
+            $this->createAction(
21
+                name: 'addIncome',
22
+                label: 'Add Income',
23
+                type: 'deposit',
24
+            ),
25
+            $this->createAction(
26
+                name: 'addExpense',
27
+                label: 'Add Expense',
28
+                type: 'withdrawal',
29
+            ),
30
+        ];
31
+    }
61 32
 
62
-                    if ($method === 'deposit') {
63
-                        $data['type'] = 'income';
64
-                    } else {
65
-                        $data['type'] = 'expense';
66
-                    }
33
+    protected function createAction(string $name, string $label, string $type): Actions\CreateAction
34
+    {
35
+        return Actions\CreateAction::make($name)
36
+            ->label($label)
37
+            ->modalWidth(MaxWidth::ThreeExtraLarge)
38
+            ->stickyModalHeader()
39
+            ->stickyModalFooter()
40
+            ->button()
41
+            ->outlined()
42
+            ->fillForm(static fn (): array => [
43
+                'type' => $type,
44
+                'posted_at' => now()->format('Y-m-d'),
45
+                'bank_account_id' => BankAccount::where('enabled', true)->first()->id ?? null,
46
+                'amount' => '0.00',
47
+                'account_id' => static::getUncategorizedAccountByType($type)?->id,
48
+            ]);
49
+    }
67 50
 
68
-                    return $data;
69
-                }),
70
-        ];
51
+    public static function getUncategorizedAccountByType(string $type): ?Account
52
+    {
53
+        [$category, $accountName] = match ($type) {
54
+            'deposit' => [AccountCategory::Revenue, 'Uncategorized Income'],
55
+            'withdrawal' => [AccountCategory::Expense, 'Uncategorized Expense'],
56
+        };
57
+
58
+        return Account::where('category', $category)
59
+            ->where('name', $accountName)
60
+            ->first();
71 61
     }
72 62
 }

+ 0
- 4
app/Filament/Company/Resources/Banking/AccountResource.php 查看文件

@@ -33,10 +33,6 @@ class AccountResource extends Resource
33 33
 
34 34
     protected static ?string $modelLabel = 'Account';
35 35
 
36
-    protected static ?string $navigationIcon = 'heroicon-o-credit-card';
37
-
38
-    protected static ?string $navigationGroup = 'Banking';
39
-
40 36
     public static function getModelLabel(): string
41 37
     {
42 38
         $modelLabel = static::$modelLabel;

+ 0
- 4
app/Filament/Company/Resources/Core/DepartmentResource.php 查看文件

@@ -19,10 +19,6 @@ class DepartmentResource extends Resource
19 19
 
20 20
     protected static ?string $modelLabel = 'Department';
21 21
 
22
-    protected static ?string $navigationIcon = 'heroicon-o-square-3-stack-3d';
23
-
24
-    protected static ?string $navigationGroup = 'HR';
25
-
26 22
     protected static ?string $slug = 'hr/departments';
27 23
 
28 24
     public static function getModelLabel(): string

+ 0
- 5
app/Filament/Company/Resources/Setting/CurrencyResource.php 查看文件

@@ -4,7 +4,6 @@ namespace App\Filament\Company\Resources\Setting;
4 4
 
5 5
 use App\Facades\Forex;
6 6
 use App\Filament\Company\Resources\Setting\CurrencyResource\Pages;
7
-use App\Models\Banking\Account;
8 7
 use App\Models\Setting\Currency;
9 8
 use App\Models\Setting\Currency as CurrencyModel;
10 9
 use App\Traits\ChecksForeignKeyConstraints;
@@ -30,10 +29,6 @@ class CurrencyResource extends Resource
30 29
 
31 30
     protected static ?string $modelLabel = 'Currency';
32 31
 
33
-    protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
34
-
35
-    protected static ?string $navigationGroup = 'Settings';
36
-
37 32
     protected static ?string $slug = 'settings/currencies';
38 33
 
39 34
     public static function getModelLabel(): string

+ 0
- 4
app/Filament/Company/Resources/Setting/DiscountResource.php 查看文件

@@ -29,10 +29,6 @@ class DiscountResource extends Resource
29 29
 
30 30
     protected static ?string $modelLabel = 'Discount';
31 31
 
32
-    protected static ?string $navigationIcon = 'heroicon-o-tag';
33
-
34
-    protected static ?string $navigationGroup = 'Settings';
35
-
36 32
     protected static ?string $slug = 'settings/discounts';
37 33
 
38 34
     public static function getModelLabel(): string

+ 0
- 4
app/Filament/Company/Resources/Setting/TaxResource.php 查看文件

@@ -26,10 +26,6 @@ class TaxResource extends Resource
26 26
 
27 27
     protected static ?string $modelLabel = 'Tax';
28 28
 
29
-    protected static ?string $navigationIcon = 'heroicon-o-receipt-percent';
30
-
31
-    protected static ?string $navigationGroup = 'Settings';
32
-
33 29
     protected static ?string $slug = 'settings/taxes';
34 30
 
35 31
     public static function getModelLabel(): string

+ 83
- 0
app/Jobs/ProcessTransactionImport.php 查看文件

@@ -0,0 +1,83 @@
1
+<?php
2
+
3
+namespace App\Jobs;
4
+
5
+use App\Models\Accounting\Account;
6
+use App\Models\Banking\BankAccount;
7
+use App\Models\Banking\ConnectedBankAccount;
8
+use App\Models\Company;
9
+use App\Services\PlaidService;
10
+use App\Services\TransactionService;
11
+use Illuminate\Bus\Queueable;
12
+use Illuminate\Contracts\Queue\ShouldQueue;
13
+use Illuminate\Foundation\Bus\Dispatchable;
14
+use Illuminate\Queue\InteractsWithQueue;
15
+use Illuminate\Queue\SerializesModels;
16
+use Illuminate\Support\Carbon;
17
+
18
+class ProcessTransactionImport implements ShouldQueue
19
+{
20
+    use Dispatchable;
21
+    use InteractsWithQueue;
22
+    use Queueable;
23
+    use SerializesModels;
24
+
25
+    protected Company $company;
26
+
27
+    protected Account $account;
28
+
29
+    protected BankAccount $bankAccount;
30
+
31
+    protected ConnectedBankAccount $connectedBankAccount;
32
+
33
+    protected string $startDate;
34
+
35
+    /**
36
+     * Create a new job instance.
37
+     */
38
+    public function __construct(Company $company, Account $account, BankAccount $bankAccount, ConnectedBankAccount $connectedBankAccount, string $startDate)
39
+    {
40
+        $this->company = $company;
41
+        $this->account = $account;
42
+        $this->bankAccount = $bankAccount;
43
+        $this->connectedBankAccount = $connectedBankAccount;
44
+        $this->startDate = $startDate;
45
+    }
46
+
47
+    /**
48
+     * Execute the job.
49
+     */
50
+    public function handle(PlaidService $plaid, TransactionService $transactionService): void
51
+    {
52
+        $accessToken = $this->connectedBankAccount->access_token;
53
+        $endDate = Carbon::now()->toDateString();
54
+        $startDate = Carbon::parse($this->startDate)->toDateString();
55
+        $allTransactions = [];
56
+        $offset = 0;
57
+
58
+        $transactionsResponse = $plaid->getTransactions($accessToken, $startDate, $endDate, [
59
+            'account_ids' => [$this->connectedBankAccount->external_account_id],
60
+        ]);
61
+
62
+        $allTransactions = [...$allTransactions, ...$transactionsResponse->transactions];
63
+        $totalTransactions = $transactionsResponse->total_transactions;
64
+
65
+        while (count($allTransactions) < $totalTransactions) {
66
+            $offset += count($transactionsResponse->transactions);
67
+            $transactionsResponse = $plaid->getTransactions($accessToken, $startDate, $endDate, [
68
+                'account_ids' => [$this->connectedBankAccount->external_account_id],
69
+                'offset' => $offset,
70
+            ]);
71
+
72
+            $allTransactions = [...$allTransactions, ...$transactionsResponse->transactions];
73
+        }
74
+
75
+        if (count($allTransactions) > 0) {
76
+            $postedTransactions = array_filter($allTransactions, static fn ($transaction) => $transaction->pending === false);
77
+            $currentBalance = $transactionsResponse->accounts[0]->balances->current;
78
+
79
+            $transactionService->createStartingBalanceIfNeeded($this->company, $this->account, $this->bankAccount, $postedTransactions, $currentBalance, $startDate);
80
+            $transactionService->storeTransactions($this->company, $this->bankAccount, $postedTransactions);
81
+        }
82
+    }
83
+}

+ 52
- 0
app/Listeners/ConfigureCompanyNavigation.php 查看文件

@@ -3,6 +3,8 @@
3 3
 namespace App\Listeners;
4 4
 
5 5
 use App\Events\CompanyConfigured;
6
+use App\Filament\Company\Pages\Accounting\AccountChart;
7
+use App\Filament\Company\Pages\Reports;
6 8
 use App\Filament\Company\Pages\Service\ConnectedAccount;
7 9
 use App\Filament\Company\Pages\Service\LiveCurrency;
8 10
 use App\Filament\Company\Pages\Setting\Appearance;
@@ -10,6 +12,7 @@ use App\Filament\Company\Pages\Setting\CompanyDefault;
10 12
 use App\Filament\Company\Pages\Setting\CompanyProfile;
11 13
 use App\Filament\Company\Pages\Setting\Invoice;
12 14
 use App\Filament\Company\Pages\Setting\Localization;
15
+use App\Filament\Company\Resources\Accounting\TransactionResource;
13 16
 use App\Filament\Company\Resources\Banking\AccountResource;
14 17
 use App\Filament\Company\Resources\Core\DepartmentResource;
15 18
 use App\Filament\Company\Resources\Setting\CurrencyResource;
@@ -39,6 +42,10 @@ class ConfigureCompanyNavigation
39 42
             Filament::getPanel('company')->navigation(
40 43
                 $this->buildCompanyNavigation()
41 44
             );
45
+        } else {
46
+            Filament::getPanel('company')->navigation(
47
+                $this->buildCompanySidebarNavigation()
48
+            );
42 49
         }
43 50
 
44 51
         NavigationGroup::configureUsing(static function (NavigationGroup $group): void {
@@ -92,4 +99,49 @@ class ConfigureCompanyNavigation
92 99
                 ...DepartmentResource::getNavigationItems(),
93 100
             ]);
94 101
     }
102
+
103
+    /**
104
+     * Build the company sidebar navigation.
105
+     */
106
+    protected function buildCompanySidebarNavigation(): callable
107
+    {
108
+        return static function (NavigationBuilder $builder): NavigationBuilder {
109
+            return $builder
110
+                ->items(Dashboard::getNavigationItems())
111
+                ->groups([
112
+                    NavigationGroup::make('Accounting')
113
+                        ->icon('heroicon-o-clipboard-document-list')
114
+                        ->extraSidebarAttributes(['class' => 'es-sidebar-group'])
115
+                        ->items([
116
+                            ...AccountChart::getNavigationItems(),
117
+                            ...TransactionResource::getNavigationItems(),
118
+                        ]),
119
+                    NavigationGroup::make('Banking')
120
+                        ->icon('heroicon-o-building-library')
121
+                        ->items(AccountResource::getNavigationItems()),
122
+                    NavigationGroup::make('HR')
123
+                        ->icon('heroicon-o-user-group')
124
+                        ->items(DepartmentResource::getNavigationItems()),
125
+                    NavigationGroup::make('Services')
126
+                        ->icon('heroicon-o-wrench-screwdriver')
127
+                        ->items([
128
+                            ...ConnectedAccount::getNavigationItems(),
129
+                            ...LiveCurrency::getNavigationItems(),
130
+                        ]),
131
+                    NavigationGroup::make('Settings')
132
+                        ->icon('heroicon-o-cog-8-tooth')
133
+                        ->items([
134
+                            ...Appearance::getNavigationItems(),
135
+                            ...CompanyProfile::getNavigationItems(),
136
+                            ...CurrencyResource::getNavigationItems(),
137
+                            ...CompanyDefault::getNavigationItems(),
138
+                            ...DiscountResource::getNavigationItems(),
139
+                            ...Invoice::getNavigationItems(),
140
+                            ...Localization::getNavigationItems(),
141
+                            ...TaxResource::getNavigationItems(),
142
+                        ]),
143
+                ])
144
+                ->items(Reports::getNavigationItems());
145
+        };
146
+    }
95 147
 }

+ 9
- 36
app/Listeners/HandleTransactionImport.php 查看文件

@@ -3,32 +3,20 @@
3 3
 namespace App\Listeners;
4 4
 
5 5
 use App\Events\StartTransactionImport;
6
-use App\Models\Accounting\Account;
7
-use App\Models\Banking\BankAccount;
8
-use App\Models\Banking\ConnectedBankAccount;
9
-use App\Models\Company;
6
+use App\Jobs\ProcessTransactionImport;
10 7
 use App\Services\ConnectedBankAccountService;
11
-use App\Services\PlaidService;
12
-use App\Services\TransactionService;
13
-use Illuminate\Support\Carbon;
14 8
 use Illuminate\Support\Facades\DB;
15 9
 
16 10
 class HandleTransactionImport
17 11
 {
18
-    protected PlaidService $plaid;
19
-
20 12
     protected ConnectedBankAccountService $connectedBankAccountService;
21 13
 
22
-    protected TransactionService $transactionService;
23
-
24 14
     /**
25 15
      * Create the event listener.
26 16
      */
27
-    public function __construct(PlaidService $plaid, ConnectedBankAccountService $connectedBankAccountService, TransactionService $transactionService)
17
+    public function __construct(ConnectedBankAccountService $connectedBankAccountService)
28 18
     {
29
-        $this->plaid = $plaid;
30 19
         $this->connectedBankAccountService = $connectedBankAccountService;
31
-        $this->transactionService = $transactionService;
32 20
     }
33 21
 
34 22
     /**
@@ -48,8 +36,6 @@ class HandleTransactionImport
48 36
         $selectedBankAccountId = $event->selectedBankAccountId;
49 37
         $startDate = $event->startDate;
50 38
 
51
-        $accessToken = $connectedBankAccount->access_token;
52
-
53 39
         $bankAccount = $this->connectedBankAccountService->getOrProcessBankAccountForConnectedBankAccount($company, $connectedBankAccount, $selectedBankAccountId);
54 40
         $account = $this->connectedBankAccountService->getOrProcessAccountForConnectedBankAccount($bankAccount, $company, $connectedBankAccount);
55 41
 
@@ -58,25 +44,12 @@ class HandleTransactionImport
58 44
             'import_transactions' => true,
59 45
         ]);
60 46
 
61
-        $this->processTransactions($company, $account, $bankAccount, $connectedBankAccount, $startDate, $accessToken);
62
-    }
63
-
64
-    public function processTransactions(Company $company, Account $account, BankAccount $bankAccount, ConnectedBankAccount $connectedBankAccount, string $startDate, string $accessToken): void
65
-    {
66
-        $endDate = Carbon::now()->toDateString();
67
-        $startDate = Carbon::parse($startDate)->toDateString();
68
-
69
-        $transactionsResponse = $this->plaid->getTransactions($accessToken, $startDate, $endDate, [
70
-            'account_ids' => [$connectedBankAccount->external_account_id],
71
-        ]);
72
-
73
-        if (filled($transactionsResponse->transactions)) {
74
-            $postedTransactions = array_filter($transactionsResponse->transactions, static fn ($transaction) => $transaction->pending === false);
75
-            $transactions = array_reverse($postedTransactions);
76
-            $currentBalance = $transactionsResponse->accounts[0]->balances->current;
77
-
78
-            $this->transactionService->createStartingBalanceIfNeeded($company, $account, $bankAccount, $transactions, $currentBalance, $startDate);
79
-            $this->transactionService->storeTransactions($company, $bankAccount, $transactions);
80
-        }
47
+        ProcessTransactionImport::dispatch(
48
+            $company,
49
+            $account,
50
+            $bankAccount,
51
+            $connectedBankAccount,
52
+            $startDate,
53
+        )->onQueue('transactions');
81 54
     }
82 55
 }

+ 1
- 2
app/Models/Accounting/Transaction.php 查看文件

@@ -29,8 +29,7 @@ class Transaction extends Model
29 29
         'account_id', // Account from Chart of Accounts (Income/Expense accounts)
30 30
         'bank_account_id', // Cash or Bank Account
31 31
         'contact_id',
32
-        'type',
33
-        'method',
32
+        'type', // 'deposit', 'withdrawal', 'journal entry'
34 33
         'payment_channel',
35 34
         'description',
36 35
         'notes',

+ 4
- 4
app/Observers/TransactionObserver.php 查看文件

@@ -17,8 +17,8 @@ class TransactionObserver
17 17
         $chartAccount = $transaction->account;
18 18
         $bankAccount = $transaction->bankAccount->account;
19 19
 
20
-        $debitAccount = $transaction->type === 'expense' ? $chartAccount : $bankAccount;
21
-        $creditAccount = $transaction->type === 'expense' ? $bankAccount : $chartAccount;
20
+        $debitAccount = $transaction->type === 'withdrawal' ? $chartAccount : $bankAccount;
21
+        $creditAccount = $transaction->type === 'withdrawal' ? $bankAccount : $chartAccount;
22 22
 
23 23
         $this->createJournalEntries($transaction, $debitAccount, $creditAccount);
24 24
     }
@@ -63,8 +63,8 @@ class TransactionObserver
63 63
         $debitEntry = $journalEntries->where('type', 'debit')->first();
64 64
         $creditEntry = $journalEntries->where('type', 'credit')->first();
65 65
 
66
-        $debitAccount = $transaction->type === 'expense' ? $chartAccount : $bankAccount;
67
-        $creditAccount = $transaction->type === 'expense' ? $bankAccount : $chartAccount;
66
+        $debitAccount = $transaction->type === 'withdrawal' ? $chartAccount : $bankAccount;
67
+        $creditAccount = $transaction->type === 'withdrawal' ? $bankAccount : $chartAccount;
68 68
 
69 69
         $debitEntry?->update([
70 70
             'account_id' => $debitAccount->id,

+ 2
- 2
app/Services/AccountService.php 查看文件

@@ -39,8 +39,8 @@ class AccountService
39 39
 
40 40
     public function getNetMovement(Account $account, string $startDate, string $endDate): BalanceValue
41 41
     {
42
-        $debitBalance = $this->getDebitBalance($account, $startDate, $endDate)->getValue();
43
-        $creditBalance = $this->getCreditBalance($account, $startDate, $endDate)->getValue();
42
+        $debitBalance = $this->journalEntryRepository->sumDebitAmounts($account, $startDate, $endDate);
43
+        $creditBalance = $this->journalEntryRepository->sumCreditAmounts($account, $startDate, $endDate);
44 44
         $netMovement = $this->calculateNetMovementByCategory($account->category, $debitBalance, $creditBalance);
45 45
 
46 46
         return new BalanceValue($netMovement, $account->currency_code ?? 'USD');

+ 6
- 8
app/Services/TransactionService.php 查看文件

@@ -39,7 +39,7 @@ class TransactionService
39 39
 
40 40
     public function createStartingBalanceTransaction(Company $company, Account $account, BankAccount $bankAccount, float $startingBalance, string $startDate): void
41 41
     {
42
-        [$transactionType, $method] = $startingBalance >= 0 ? ['income', 'deposit'] : ['expense', 'withdrawal'];
42
+        $transactionType = $startingBalance >= 0 ? 'deposit' : 'withdrawal';
43 43
         $chartAccount = $account->where('category', AccountCategory::Equity)->where('name', 'Owner\'s Equity')->first();
44 44
         $postedAt = Carbon::parse($startDate)->subDay()->toDateTimeString();
45 45
 
@@ -49,7 +49,6 @@ class TransactionService
49 49
             'bank_account_id' => $bankAccount->id,
50 50
             'type' => $transactionType,
51 51
             'amount' => abs($startingBalance),
52
-            'method' => $method,
53 52
             'payment_channel' => 'other',
54 53
             'posted_at' => $postedAt,
55 54
             'description' => 'Starting Balance',
@@ -60,7 +59,7 @@ class TransactionService
60 59
 
61 60
     public function storeTransaction(Company $company, BankAccount $bankAccount, object $transaction): void
62 61
     {
63
-        [$transactionType, $method] = $transaction->amount < 0 ? ['income', 'deposit'] : ['expense', 'withdrawal'];
62
+        $transactionType = $transaction->amount < 0 ? 'deposit' : 'withdrawal';
64 63
         $paymentChannel = $transaction->payment_channel;
65 64
         $chartAccount = $this->getAccountFromTransaction($company, $transaction, $transactionType);
66 65
         $postedAt = $transaction->datetime ?? Carbon::parse($transaction->date)->toDateTimeString();
@@ -72,7 +71,6 @@ class TransactionService
72 71
             'bank_account_id' => $bankAccount->id,
73 72
             'type' => $transactionType,
74 73
             'amount' => abs($transaction->amount),
75
-            'method' => $method,
76 74
             'payment_channel' => $paymentChannel,
77 75
             'posted_at' => $postedAt,
78 76
             'description' => $description,
@@ -84,8 +82,8 @@ class TransactionService
84 82
     public function getAccountFromTransaction(Company $company, object $transaction, string $transactionType): Account
85 83
     {
86 84
         $accountCategory = match ($transactionType) {
87
-            'income' => AccountCategory::Revenue,
88
-            'expense' => AccountCategory::Expense,
85
+            'deposit' => AccountCategory::Revenue,
86
+            'withdrawal' => AccountCategory::Expense,
89 87
         };
90 88
 
91 89
         $accounts = $company->accounts()
@@ -132,8 +130,8 @@ class TransactionService
132 130
     public function getUncategorizedAccount(Company $company, string $transactionType): Account
133 131
     {
134 132
         [$type, $name] = match ($transactionType) {
135
-            'income' => [AccountType::UncategorizedRevenue, 'Uncategorized Income'],
136
-            'expense' => [AccountType::UncategorizedExpense, 'Uncategorized Expense'],
133
+            'deposit' => [AccountType::UncategorizedRevenue, 'Uncategorized Income'],
134
+            'withdrawal' => [AccountType::UncategorizedExpense, 'Uncategorized Expense'],
137 135
         };
138 136
 
139 137
         return $company->accounts()

+ 91
- 91
composer.lock 查看文件

@@ -293,16 +293,16 @@
293 293
         },
294 294
         {
295 295
             "name": "anourvalar/eloquent-serialize",
296
-            "version": "1.2.20",
296
+            "version": "1.2.21",
297 297
             "source": {
298 298
                 "type": "git",
299 299
                 "url": "https://github.com/AnourValar/eloquent-serialize.git",
300
-                "reference": "f8408e92768ff295990e3a1edb7e47950e008c63"
300
+                "reference": "39766b21c8de0dbbff8a9368c2579502fd1d248e"
301 301
             },
302 302
             "dist": {
303 303
                 "type": "zip",
304
-                "url": "https://api.github.com/repos/AnourValar/eloquent-serialize/zipball/f8408e92768ff295990e3a1edb7e47950e008c63",
305
-                "reference": "f8408e92768ff295990e3a1edb7e47950e008c63",
304
+                "url": "https://api.github.com/repos/AnourValar/eloquent-serialize/zipball/39766b21c8de0dbbff8a9368c2579502fd1d248e",
305
+                "reference": "39766b21c8de0dbbff8a9368c2579502fd1d248e",
306 306
                 "shasum": ""
307 307
             },
308 308
             "require": {
@@ -353,9 +353,9 @@
353 353
             ],
354 354
             "support": {
355 355
                 "issues": "https://github.com/AnourValar/eloquent-serialize/issues",
356
-                "source": "https://github.com/AnourValar/eloquent-serialize/tree/1.2.20"
356
+                "source": "https://github.com/AnourValar/eloquent-serialize/tree/1.2.21"
357 357
             },
358
-            "time": "2024-03-08T14:48:54+00:00"
358
+            "time": "2024-03-15T14:33:22+00:00"
359 359
         },
360 360
         {
361 361
             "name": "aws/aws-crt-php",
@@ -413,16 +413,16 @@
413 413
         },
414 414
         {
415 415
             "name": "aws/aws-sdk-php",
416
-            "version": "3.300.16",
416
+            "version": "3.301.1",
417 417
             "source": {
418 418
                 "type": "git",
419 419
                 "url": "https://github.com/aws/aws-sdk-php.git",
420
-                "reference": "d6afaeaa0c0e2c3be5f5223f72c0b5e2b2667b67"
420
+                "reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba"
421 421
             },
422 422
             "dist": {
423 423
                 "type": "zip",
424
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d6afaeaa0c0e2c3be5f5223f72c0b5e2b2667b67",
425
-                "reference": "d6afaeaa0c0e2c3be5f5223f72c0b5e2b2667b67",
424
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0a910d2b35e7087337cdf3569dc9b6ce232aafba",
425
+                "reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba",
426 426
                 "shasum": ""
427 427
             },
428 428
             "require": {
@@ -502,9 +502,9 @@
502 502
             "support": {
503 503
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
504 504
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
505
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.300.16"
505
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.301.1"
506 506
             },
507
-            "time": "2024-03-12T18:04:55+00:00"
507
+            "time": "2024-03-15T18:14:42+00:00"
508 508
         },
509 509
         {
510 510
             "name": "aws/aws-sdk-php-laravel",
@@ -585,16 +585,16 @@
585 585
         },
586 586
         {
587 587
             "name": "barryvdh/laravel-dompdf",
588
-            "version": "v2.1.0",
588
+            "version": "v2.1.1",
589 589
             "source": {
590 590
                 "type": "git",
591 591
                 "url": "https://github.com/barryvdh/laravel-dompdf.git",
592
-                "reference": "c8b8a8490e5f7348cf99054821fb248f103e7d24"
592
+                "reference": "cb37868365f9b937039d316727a1fced1e87b31c"
593 593
             },
594 594
             "dist": {
595 595
                 "type": "zip",
596
-                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/c8b8a8490e5f7348cf99054821fb248f103e7d24",
597
-                "reference": "c8b8a8490e5f7348cf99054821fb248f103e7d24",
596
+                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/cb37868365f9b937039d316727a1fced1e87b31c",
597
+                "reference": "cb37868365f9b937039d316727a1fced1e87b31c",
598 598
                 "shasum": ""
599 599
             },
600 600
             "require": {
@@ -646,7 +646,7 @@
646 646
             ],
647 647
             "support": {
648 648
                 "issues": "https://github.com/barryvdh/laravel-dompdf/issues",
649
-                "source": "https://github.com/barryvdh/laravel-dompdf/tree/v2.1.0"
649
+                "source": "https://github.com/barryvdh/laravel-dompdf/tree/v2.1.1"
650 650
             },
651 651
             "funding": [
652 652
                 {
@@ -658,7 +658,7 @@
658 658
                     "type": "github"
659 659
                 }
660 660
             ],
661
-            "time": "2024-03-04T08:18:20+00:00"
661
+            "time": "2024-03-15T12:48:39+00:00"
662 662
         },
663 663
         {
664 664
             "name": "bezhansalleh/filament-panel-switch",
@@ -1896,16 +1896,16 @@
1896 1896
         },
1897 1897
         {
1898 1898
             "name": "filament/actions",
1899
-            "version": "v3.2.47",
1899
+            "version": "v3.2.50",
1900 1900
             "source": {
1901 1901
                 "type": "git",
1902 1902
                 "url": "https://github.com/filamentphp/actions.git",
1903
-                "reference": "15f1f627b3a6d271e998228d591508c705491061"
1903
+                "reference": "07bb64870f6552e4e979894b6eea1404cad2d0f1"
1904 1904
             },
1905 1905
             "dist": {
1906 1906
                 "type": "zip",
1907
-                "url": "https://api.github.com/repos/filamentphp/actions/zipball/15f1f627b3a6d271e998228d591508c705491061",
1908
-                "reference": "15f1f627b3a6d271e998228d591508c705491061",
1907
+                "url": "https://api.github.com/repos/filamentphp/actions/zipball/07bb64870f6552e4e979894b6eea1404cad2d0f1",
1908
+                "reference": "07bb64870f6552e4e979894b6eea1404cad2d0f1",
1909 1909
                 "shasum": ""
1910 1910
             },
1911 1911
             "require": {
@@ -1945,20 +1945,20 @@
1945 1945
                 "issues": "https://github.com/filamentphp/filament/issues",
1946 1946
                 "source": "https://github.com/filamentphp/filament"
1947 1947
             },
1948
-            "time": "2024-03-11T11:21:19+00:00"
1948
+            "time": "2024-03-14T14:13:40+00:00"
1949 1949
         },
1950 1950
         {
1951 1951
             "name": "filament/filament",
1952
-            "version": "v3.2.47",
1952
+            "version": "v3.2.50",
1953 1953
             "source": {
1954 1954
                 "type": "git",
1955 1955
                 "url": "https://github.com/filamentphp/panels.git",
1956
-                "reference": "20a6939fb454b16fe48b205d1b815cdd6cc1446e"
1956
+                "reference": "39ca57471511129e854d138528cefad335c88b08"
1957 1957
             },
1958 1958
             "dist": {
1959 1959
                 "type": "zip",
1960
-                "url": "https://api.github.com/repos/filamentphp/panels/zipball/20a6939fb454b16fe48b205d1b815cdd6cc1446e",
1961
-                "reference": "20a6939fb454b16fe48b205d1b815cdd6cc1446e",
1960
+                "url": "https://api.github.com/repos/filamentphp/panels/zipball/39ca57471511129e854d138528cefad335c88b08",
1961
+                "reference": "39ca57471511129e854d138528cefad335c88b08",
1962 1962
                 "shasum": ""
1963 1963
             },
1964 1964
             "require": {
@@ -2010,20 +2010,20 @@
2010 2010
                 "issues": "https://github.com/filamentphp/filament/issues",
2011 2011
                 "source": "https://github.com/filamentphp/filament"
2012 2012
             },
2013
-            "time": "2024-03-11T11:21:25+00:00"
2013
+            "time": "2024-03-14T14:14:03+00:00"
2014 2014
         },
2015 2015
         {
2016 2016
             "name": "filament/forms",
2017
-            "version": "v3.2.47",
2017
+            "version": "v3.2.50",
2018 2018
             "source": {
2019 2019
                 "type": "git",
2020 2020
                 "url": "https://github.com/filamentphp/forms.git",
2021
-                "reference": "5ddef657a1127e081e5f03de8b1c432d65d4b9eb"
2021
+                "reference": "21515f37ee2de29f9753302112723a7b73162793"
2022 2022
             },
2023 2023
             "dist": {
2024 2024
                 "type": "zip",
2025
-                "url": "https://api.github.com/repos/filamentphp/forms/zipball/5ddef657a1127e081e5f03de8b1c432d65d4b9eb",
2026
-                "reference": "5ddef657a1127e081e5f03de8b1c432d65d4b9eb",
2025
+                "url": "https://api.github.com/repos/filamentphp/forms/zipball/21515f37ee2de29f9753302112723a7b73162793",
2026
+                "reference": "21515f37ee2de29f9753302112723a7b73162793",
2027 2027
                 "shasum": ""
2028 2028
             },
2029 2029
             "require": {
@@ -2066,20 +2066,20 @@
2066 2066
                 "issues": "https://github.com/filamentphp/filament/issues",
2067 2067
                 "source": "https://github.com/filamentphp/filament"
2068 2068
             },
2069
-            "time": "2024-03-11T11:21:19+00:00"
2069
+            "time": "2024-03-14T10:54:01+00:00"
2070 2070
         },
2071 2071
         {
2072 2072
             "name": "filament/infolists",
2073
-            "version": "v3.2.47",
2073
+            "version": "v3.2.50",
2074 2074
             "source": {
2075 2075
                 "type": "git",
2076 2076
                 "url": "https://github.com/filamentphp/infolists.git",
2077
-                "reference": "302e210bade862ba633fc7ccadb6d2c01aab8d36"
2077
+                "reference": "72217e2ae055bfea51feb3351e00f269b188796d"
2078 2078
             },
2079 2079
             "dist": {
2080 2080
                 "type": "zip",
2081
-                "url": "https://api.github.com/repos/filamentphp/infolists/zipball/302e210bade862ba633fc7ccadb6d2c01aab8d36",
2082
-                "reference": "302e210bade862ba633fc7ccadb6d2c01aab8d36",
2081
+                "url": "https://api.github.com/repos/filamentphp/infolists/zipball/72217e2ae055bfea51feb3351e00f269b188796d",
2082
+                "reference": "72217e2ae055bfea51feb3351e00f269b188796d",
2083 2083
                 "shasum": ""
2084 2084
             },
2085 2085
             "require": {
@@ -2117,20 +2117,20 @@
2117 2117
                 "issues": "https://github.com/filamentphp/filament/issues",
2118 2118
                 "source": "https://github.com/filamentphp/filament"
2119 2119
             },
2120
-            "time": "2024-03-11T11:21:22+00:00"
2120
+            "time": "2024-03-14T10:54:02+00:00"
2121 2121
         },
2122 2122
         {
2123 2123
             "name": "filament/notifications",
2124
-            "version": "v3.2.47",
2124
+            "version": "v3.2.50",
2125 2125
             "source": {
2126 2126
                 "type": "git",
2127 2127
                 "url": "https://github.com/filamentphp/notifications.git",
2128
-                "reference": "93c1d72b12ff1bed520d082a1292ab25a566a8d8"
2128
+                "reference": "a25fe75eeebc61d4a6e2bfa88e9e501a5f8020bd"
2129 2129
             },
2130 2130
             "dist": {
2131 2131
                 "type": "zip",
2132
-                "url": "https://api.github.com/repos/filamentphp/notifications/zipball/93c1d72b12ff1bed520d082a1292ab25a566a8d8",
2133
-                "reference": "93c1d72b12ff1bed520d082a1292ab25a566a8d8",
2132
+                "url": "https://api.github.com/repos/filamentphp/notifications/zipball/a25fe75eeebc61d4a6e2bfa88e9e501a5f8020bd",
2133
+                "reference": "a25fe75eeebc61d4a6e2bfa88e9e501a5f8020bd",
2134 2134
                 "shasum": ""
2135 2135
             },
2136 2136
             "require": {
@@ -2169,11 +2169,11 @@
2169 2169
                 "issues": "https://github.com/filamentphp/filament/issues",
2170 2170
                 "source": "https://github.com/filamentphp/filament"
2171 2171
             },
2172
-            "time": "2024-02-29T12:30:04+00:00"
2172
+            "time": "2024-03-14T10:54:06+00:00"
2173 2173
         },
2174 2174
         {
2175 2175
             "name": "filament/spatie-laravel-tags-plugin",
2176
-            "version": "v3.2.47",
2176
+            "version": "v3.2.50",
2177 2177
             "source": {
2178 2178
                 "type": "git",
2179 2179
                 "url": "https://github.com/filamentphp/spatie-laravel-tags-plugin.git",
@@ -2210,16 +2210,16 @@
2210 2210
         },
2211 2211
         {
2212 2212
             "name": "filament/support",
2213
-            "version": "v3.2.47",
2213
+            "version": "v3.2.50",
2214 2214
             "source": {
2215 2215
                 "type": "git",
2216 2216
                 "url": "https://github.com/filamentphp/support.git",
2217
-                "reference": "811a57072a6e8677e42d1b745291def5f26a8d62"
2217
+                "reference": "f10144d4758d7873ee8d471530a4455a1105ca53"
2218 2218
             },
2219 2219
             "dist": {
2220 2220
                 "type": "zip",
2221
-                "url": "https://api.github.com/repos/filamentphp/support/zipball/811a57072a6e8677e42d1b745291def5f26a8d62",
2222
-                "reference": "811a57072a6e8677e42d1b745291def5f26a8d62",
2221
+                "url": "https://api.github.com/repos/filamentphp/support/zipball/f10144d4758d7873ee8d471530a4455a1105ca53",
2222
+                "reference": "f10144d4758d7873ee8d471530a4455a1105ca53",
2223 2223
                 "shasum": ""
2224 2224
             },
2225 2225
             "require": {
@@ -2229,7 +2229,7 @@
2229 2229
                 "illuminate/contracts": "^10.45|^11.0",
2230 2230
                 "illuminate/support": "^10.45|^11.0",
2231 2231
                 "illuminate/view": "^10.45|^11.0",
2232
-                "livewire/livewire": "^3.2.3",
2232
+                "livewire/livewire": "^3.4.9",
2233 2233
                 "php": "^8.1",
2234 2234
                 "ryangjchandler/blade-capture-directive": "^0.2|^0.3|^1.0",
2235 2235
                 "spatie/color": "^1.5",
@@ -2263,20 +2263,20 @@
2263 2263
                 "issues": "https://github.com/filamentphp/filament/issues",
2264 2264
                 "source": "https://github.com/filamentphp/filament"
2265 2265
             },
2266
-            "time": "2024-03-08T12:54:38+00:00"
2266
+            "time": "2024-03-14T14:14:06+00:00"
2267 2267
         },
2268 2268
         {
2269 2269
             "name": "filament/tables",
2270
-            "version": "v3.2.47",
2270
+            "version": "v3.2.50",
2271 2271
             "source": {
2272 2272
                 "type": "git",
2273 2273
                 "url": "https://github.com/filamentphp/tables.git",
2274
-                "reference": "68900e0443ad84f027a06e19429bd47672c7733f"
2274
+                "reference": "260fa164f4f5c792da40c0a3fa8bb274b64dbf56"
2275 2275
             },
2276 2276
             "dist": {
2277 2277
                 "type": "zip",
2278
-                "url": "https://api.github.com/repos/filamentphp/tables/zipball/68900e0443ad84f027a06e19429bd47672c7733f",
2279
-                "reference": "68900e0443ad84f027a06e19429bd47672c7733f",
2278
+                "url": "https://api.github.com/repos/filamentphp/tables/zipball/260fa164f4f5c792da40c0a3fa8bb274b64dbf56",
2279
+                "reference": "260fa164f4f5c792da40c0a3fa8bb274b64dbf56",
2280 2280
                 "shasum": ""
2281 2281
             },
2282 2282
             "require": {
@@ -2316,20 +2316,20 @@
2316 2316
                 "issues": "https://github.com/filamentphp/filament/issues",
2317 2317
                 "source": "https://github.com/filamentphp/filament"
2318 2318
             },
2319
-            "time": "2024-03-11T11:21:42+00:00"
2319
+            "time": "2024-03-14T10:54:32+00:00"
2320 2320
         },
2321 2321
         {
2322 2322
             "name": "filament/widgets",
2323
-            "version": "v3.2.47",
2323
+            "version": "v3.2.50",
2324 2324
             "source": {
2325 2325
                 "type": "git",
2326 2326
                 "url": "https://github.com/filamentphp/widgets.git",
2327
-                "reference": "dc074f0f78ae2ba6d176249b00e05f10d4a939a3"
2327
+                "reference": "38a011b9a556a2786028eb80aa135e171569d2af"
2328 2328
             },
2329 2329
             "dist": {
2330 2330
                 "type": "zip",
2331
-                "url": "https://api.github.com/repos/filamentphp/widgets/zipball/dc074f0f78ae2ba6d176249b00e05f10d4a939a3",
2332
-                "reference": "dc074f0f78ae2ba6d176249b00e05f10d4a939a3",
2331
+                "url": "https://api.github.com/repos/filamentphp/widgets/zipball/38a011b9a556a2786028eb80aa135e171569d2af",
2332
+                "reference": "38a011b9a556a2786028eb80aa135e171569d2af",
2333 2333
                 "shasum": ""
2334 2334
             },
2335 2335
             "require": {
@@ -2360,7 +2360,7 @@
2360 2360
                 "issues": "https://github.com/filamentphp/filament/issues",
2361 2361
                 "source": "https://github.com/filamentphp/filament"
2362 2362
             },
2363
-            "time": "2024-03-11T11:21:45+00:00"
2363
+            "time": "2024-03-14T10:54:33+00:00"
2364 2364
         },
2365 2365
         {
2366 2366
             "name": "fruitcake/php-cors",
@@ -2497,21 +2497,21 @@
2497 2497
         },
2498 2498
         {
2499 2499
             "name": "guava/filament-clusters",
2500
-            "version": "1.1.0",
2500
+            "version": "1.2.0",
2501 2501
             "source": {
2502 2502
                 "type": "git",
2503 2503
                 "url": "https://github.com/GuavaCZ/filament-clusters.git",
2504
-                "reference": "4801324f633e4db0532ee0a1d64ed57c8afcfe4a"
2504
+                "reference": "b9bd5a413e8f392653c06e77acb19c20b6a26924"
2505 2505
             },
2506 2506
             "dist": {
2507 2507
                 "type": "zip",
2508
-                "url": "https://api.github.com/repos/GuavaCZ/filament-clusters/zipball/4801324f633e4db0532ee0a1d64ed57c8afcfe4a",
2509
-                "reference": "4801324f633e4db0532ee0a1d64ed57c8afcfe4a",
2508
+                "url": "https://api.github.com/repos/GuavaCZ/filament-clusters/zipball/b9bd5a413e8f392653c06e77acb19c20b6a26924",
2509
+                "reference": "b9bd5a413e8f392653c06e77acb19c20b6a26924",
2510 2510
                 "shasum": ""
2511 2511
             },
2512 2512
             "require": {
2513 2513
                 "filament/filament": "^3.0",
2514
-                "illuminate/contracts": "^10.0",
2514
+                "illuminate/contracts": "^10.0 | ^11.0",
2515 2515
                 "php": "^8.1",
2516 2516
                 "spatie/laravel-package-tools": "^1.14.0"
2517 2517
             },
@@ -2560,7 +2560,7 @@
2560 2560
             ],
2561 2561
             "support": {
2562 2562
                 "issues": "https://github.com/GuavaCZ/filament-clusters/issues",
2563
-                "source": "https://github.com/GuavaCZ/filament-clusters/tree/1.1.0"
2563
+                "source": "https://github.com/GuavaCZ/filament-clusters/tree/1.2.0"
2564 2564
             },
2565 2565
             "funding": [
2566 2566
                 {
@@ -2568,7 +2568,7 @@
2568 2568
                     "type": "github"
2569 2569
                 }
2570 2570
             ],
2571
-            "time": "2023-10-18T12:19:40+00:00"
2571
+            "time": "2024-03-15T12:43:01+00:00"
2572 2572
         },
2573 2573
         {
2574 2574
             "name": "guzzlehttp/guzzle",
@@ -3045,16 +3045,16 @@
3045 3045
         },
3046 3046
         {
3047 3047
             "name": "laravel/framework",
3048
-            "version": "v10.48.2",
3048
+            "version": "v10.48.3",
3049 3049
             "source": {
3050 3050
                 "type": "git",
3051 3051
                 "url": "https://github.com/laravel/framework.git",
3052
-                "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3"
3052
+                "reference": "5791c052b41c6b593556adc687076bfbdd13c501"
3053 3053
             },
3054 3054
             "dist": {
3055 3055
                 "type": "zip",
3056
-                "url": "https://api.github.com/repos/laravel/framework/zipball/32a8bb151d748b579c3794dea53b56bd40dfbbd3",
3057
-                "reference": "32a8bb151d748b579c3794dea53b56bd40dfbbd3",
3056
+                "url": "https://api.github.com/repos/laravel/framework/zipball/5791c052b41c6b593556adc687076bfbdd13c501",
3057
+                "reference": "5791c052b41c6b593556adc687076bfbdd13c501",
3058 3058
                 "shasum": ""
3059 3059
             },
3060 3060
             "require": {
@@ -3248,7 +3248,7 @@
3248 3248
                 "issues": "https://github.com/laravel/framework/issues",
3249 3249
                 "source": "https://github.com/laravel/framework"
3250 3250
             },
3251
-            "time": "2024-03-12T16:35:43+00:00"
3251
+            "time": "2024-03-15T10:17:07+00:00"
3252 3252
         },
3253 3253
         {
3254 3254
             "name": "laravel/prompts",
@@ -4304,16 +4304,16 @@
4304 4304
         },
4305 4305
         {
4306 4306
             "name": "livewire/livewire",
4307
-            "version": "v3.4.8",
4307
+            "version": "v3.4.9",
4308 4308
             "source": {
4309 4309
                 "type": "git",
4310 4310
                 "url": "https://github.com/livewire/livewire.git",
4311
-                "reference": "0335b8f022ac535fc3cf29fd8a8fcb275353a470"
4311
+                "reference": "c65b3f0798ab2c9338213ede3588c3cdf4e6fcc0"
4312 4312
             },
4313 4313
             "dist": {
4314 4314
                 "type": "zip",
4315
-                "url": "https://api.github.com/repos/livewire/livewire/zipball/0335b8f022ac535fc3cf29fd8a8fcb275353a470",
4316
-                "reference": "0335b8f022ac535fc3cf29fd8a8fcb275353a470",
4315
+                "url": "https://api.github.com/repos/livewire/livewire/zipball/c65b3f0798ab2c9338213ede3588c3cdf4e6fcc0",
4316
+                "reference": "c65b3f0798ab2c9338213ede3588c3cdf4e6fcc0",
4317 4317
                 "shasum": ""
4318 4318
             },
4319 4319
             "require": {
@@ -4367,7 +4367,7 @@
4367 4367
             "description": "A front-end framework for Laravel.",
4368 4368
             "support": {
4369 4369
                 "issues": "https://github.com/livewire/livewire/issues",
4370
-                "source": "https://github.com/livewire/livewire/tree/v3.4.8"
4370
+                "source": "https://github.com/livewire/livewire/tree/v3.4.9"
4371 4371
             },
4372 4372
             "funding": [
4373 4373
                 {
@@ -4375,7 +4375,7 @@
4375 4375
                     "type": "github"
4376 4376
                 }
4377 4377
             ],
4378
-            "time": "2024-03-08T13:01:50+00:00"
4378
+            "time": "2024-03-14T14:03:32+00:00"
4379 4379
         },
4380 4380
         {
4381 4381
             "name": "masterminds/html5",
@@ -5272,16 +5272,16 @@
5272 5272
         },
5273 5273
         {
5274 5274
             "name": "phenx/php-svg-lib",
5275
-            "version": "0.5.2",
5275
+            "version": "0.5.3",
5276 5276
             "source": {
5277 5277
                 "type": "git",
5278 5278
                 "url": "https://github.com/dompdf/php-svg-lib.git",
5279
-                "reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa"
5279
+                "reference": "0e46722c154726a5f9ac218197ccc28adba16fcf"
5280 5280
             },
5281 5281
             "dist": {
5282 5282
                 "type": "zip",
5283
-                "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/732faa9fb4309221e2bd9b2fda5de44f947133aa",
5284
-                "reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa",
5283
+                "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/0e46722c154726a5f9ac218197ccc28adba16fcf",
5284
+                "reference": "0e46722c154726a5f9ac218197ccc28adba16fcf",
5285 5285
                 "shasum": ""
5286 5286
             },
5287 5287
             "require": {
@@ -5300,7 +5300,7 @@
5300 5300
             },
5301 5301
             "notification-url": "https://packagist.org/downloads/",
5302 5302
             "license": [
5303
-                "LGPL-3.0"
5303
+                "LGPL-3.0-or-later"
5304 5304
             ],
5305 5305
             "authors": [
5306 5306
                 {
@@ -5312,9 +5312,9 @@
5312 5312
             "homepage": "https://github.com/PhenX/php-svg-lib",
5313 5313
             "support": {
5314 5314
                 "issues": "https://github.com/dompdf/php-svg-lib/issues",
5315
-                "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.2"
5315
+                "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.3"
5316 5316
             },
5317
-            "time": "2024-02-07T12:49:40+00:00"
5317
+            "time": "2024-02-23T20:39:24+00:00"
5318 5318
         },
5319 5319
         {
5320 5320
             "name": "phpoption/phpoption",
@@ -5854,16 +5854,16 @@
5854 5854
         },
5855 5855
         {
5856 5856
             "name": "psy/psysh",
5857
-            "version": "v0.12.0",
5857
+            "version": "v0.12.1",
5858 5858
             "source": {
5859 5859
                 "type": "git",
5860 5860
                 "url": "https://github.com/bobthecow/psysh.git",
5861
-                "reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d"
5861
+                "reference": "39621c73e0754328252f032c6997b983afc50431"
5862 5862
             },
5863 5863
             "dist": {
5864 5864
                 "type": "zip",
5865
-                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/750bf031a48fd07c673dbe3f11f72362ea306d0d",
5866
-                "reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d",
5865
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/39621c73e0754328252f032c6997b983afc50431",
5866
+                "reference": "39621c73e0754328252f032c6997b983afc50431",
5867 5867
                 "shasum": ""
5868 5868
             },
5869 5869
             "require": {
@@ -5927,9 +5927,9 @@
5927 5927
             ],
5928 5928
             "support": {
5929 5929
                 "issues": "https://github.com/bobthecow/psysh/issues",
5930
-                "source": "https://github.com/bobthecow/psysh/tree/v0.12.0"
5930
+                "source": "https://github.com/bobthecow/psysh/tree/v0.12.1"
5931 5931
             },
5932
-            "time": "2023-12-20T15:28:09+00:00"
5932
+            "time": "2024-03-15T03:22:57+00:00"
5933 5933
         },
5934 5934
         {
5935 5935
             "name": "ralouphie/getallheaders",

+ 268
- 0
config/dompdf.php 查看文件

@@ -0,0 +1,268 @@
1
+<?php
2
+
3
+return [
4
+
5
+    /*
6
+    |--------------------------------------------------------------------------
7
+    | Settings
8
+    |--------------------------------------------------------------------------
9
+    |
10
+    | Set some default values. It is possible to add all defines that can be set
11
+    | in dompdf_config.inc.php. You can also override the entire config file.
12
+    |
13
+    */
14
+    'show_warnings' => false,   // Throw an Exception on warnings from dompdf
15
+
16
+    'public_path' => null,  // Override the public path if needed
17
+
18
+    /*
19
+     * Dejavu Sans font is missing glyphs for converted entities, turn it off if you need to show € and £.
20
+     */
21
+    'convert_entities' => true,
22
+
23
+    'options' => [
24
+        /**
25
+         * The location of the DOMPDF font directory
26
+         *
27
+         * The location of the directory where DOMPDF will store fonts and font metrics
28
+         * Note: This directory must exist and be writable by the webserver process.
29
+         * *Please note the trailing slash.*
30
+         *
31
+         * Notes regarding fonts:
32
+         * Additional .afm font metrics can be added by executing load_font.php from command line.
33
+         *
34
+         * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
35
+         * be embedded in the pdf file or the PDF may not display correctly. This can significantly
36
+         * increase file size unless font subsetting is enabled. Before embedding a font please
37
+         * review your rights under the font license.
38
+         *
39
+         * Any font specification in the source HTML is translated to the closest font available
40
+         * in the font directory.
41
+         *
42
+         * The pdf standard "Base 14 fonts" are:
43
+         * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
44
+         * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
45
+         * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
46
+         * Symbol, ZapfDingbats.
47
+         */
48
+        'font_dir' => storage_path('fonts'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
49
+
50
+        /**
51
+         * The location of the DOMPDF font cache directory
52
+         *
53
+         * This directory contains the cached font metrics for the fonts used by DOMPDF.
54
+         * This directory can be the same as DOMPDF_FONT_DIR
55
+         *
56
+         * Note: This directory must exist and be writable by the webserver process.
57
+         */
58
+        'font_cache' => storage_path('fonts'),
59
+
60
+        /**
61
+         * The location of a temporary directory.
62
+         *
63
+         * The directory specified must be writeable by the webserver process.
64
+         * The temporary directory is required to download remote images and when
65
+         * using the PDFLib back end.
66
+         */
67
+        'temp_dir' => sys_get_temp_dir(),
68
+
69
+        /**
70
+         * ==== IMPORTANT ====
71
+         *
72
+         * dompdf's "chroot": Prevents dompdf from accessing system files or other
73
+         * files on the webserver.  All local files opened by dompdf must be in a
74
+         * subdirectory of this directory.  DO NOT set it to '/' since this could
75
+         * allow an attacker to use dompdf to read any files on the server.  This
76
+         * should be an absolute path.
77
+         * This is only checked on command line call by dompdf.php, but not by
78
+         * direct class use like:
79
+         * $dompdf = new DOMPDF();  $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
80
+         */
81
+        'chroot' => realpath(base_path()),
82
+
83
+        /**
84
+         * Protocol whitelist
85
+         *
86
+         * Protocols and PHP wrappers allowed in URIs, and the validation rules
87
+         * that determine if a resource may be loaded. Full support is not guaranteed
88
+         * for the protocols/wrappers specified
89
+         * by this array.
90
+         *
91
+         * @var array
92
+         */
93
+        'allowed_protocols' => [
94
+            'file://' => ['rules' => []],
95
+            'http://' => ['rules' => []],
96
+            'https://' => ['rules' => []],
97
+        ],
98
+
99
+
100
+        'log_output_file' => null,
101
+
102
+        /**
103
+         * Whether to enable font subsetting or not.
104
+         */
105
+        'enable_font_subsetting' => true,
106
+
107
+        /**
108
+         * The PDF rendering backend to use
109
+         *
110
+         * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
111
+         * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
112
+         * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link * Canvas_Factory} ultimately determines which rendering class to instantiate
113
+         * based on this setting.
114
+         *
115
+         * Both PDFLib & CPDF rendering backends provide sufficient rendering
116
+         * capabilities for dompdf, however additional features (e.g. object,
117
+         * image and font support, etc.) differ between backends.  Please see
118
+         * {@link PDFLib_Adapter} for more information on the PDFLib backend
119
+         * and {@link CPDF_Adapter} and lib/class.pdf.php for more information
120
+         * on CPDF. Also see the documentation for each backend at the links
121
+         * below.
122
+         *
123
+         * The GD rendering backend is a little different than PDFLib and
124
+         * CPDF. Several features of CPDF and PDFLib are not supported or do
125
+         * not make any sense when creating image files.  For example,
126
+         * multiple pages are not supported, nor are PDF 'objects'.  Have a
127
+         * look at {@link GD_Adapter} for more information.  GD support is
128
+         * experimental, so use it at your own risk.
129
+         *
130
+         * @link http://www.pdflib.com
131
+         * @link http://www.ros.co.nz/pdf
132
+         * @link http://www.php.net/image
133
+         */
134
+        'pdf_backend' => 'CPDF',
135
+
136
+        /**
137
+         * PDFlib license key
138
+         *
139
+         * If you are using a licensed, commercial version of PDFlib, specify
140
+         * your license key here.  If you are using PDFlib-Lite or are evaluating
141
+         * the commercial version of PDFlib, comment out this setting.
142
+         *
143
+         * @link http://www.pdflib.com
144
+         *
145
+         * If pdflib present in web server and auto or selected explicitly above,
146
+         * a real license code must exist!
147
+         */
148
+        //"DOMPDF_PDFLIB_LICENSE" => "your license key here",
149
+
150
+        /**
151
+         * html target media view which should be rendered into pdf.
152
+         * List of types and parsing rules for future extensions:
153
+         * http://www.w3.org/TR/REC-html40/types.html
154
+         *   screen, tty, tv, projection, handheld, print, braille, aural, all
155
+         * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
156
+         * Note, even though the generated pdf file is intended for print output,
157
+         * the desired content might be different (e.g. screen or projection view of html file).
158
+         * Therefore allow specification of content here.
159
+         */
160
+        'default_media_type' => 'screen',
161
+
162
+        /**
163
+         * The default paper size.
164
+         *
165
+         * North America standard is "letter"; other countries generally "a4"
166
+         *
167
+         * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.)
168
+         */
169
+        'default_paper_size' => 'a4',
170
+
171
+        /**
172
+         * The default paper orientation.
173
+         *
174
+         * The orientation of the page (portrait or landscape).
175
+         */
176
+        'default_paper_orientation' => 'portrait',
177
+
178
+        /**
179
+         * The default font family
180
+         *
181
+         * Used if no suitable fonts can be found. This must exist in the font folder.
182
+         */
183
+        'default_font' => 'sans-serif',
184
+
185
+        /**
186
+         * Image DPI setting
187
+         *
188
+         * This setting determines the default DPI setting for images and fonts.  The
189
+         * DPI may be overridden for inline images by explicitly setting the
190
+         * image's width & height style attributes (i.e. if the image's native
191
+         * width is 600 pixels and you specify the image's width as 72 points,
192
+         * the image will have a DPI of 600 in the rendered PDF.  The DPI of
193
+         * background images can not be overridden and is controlled entirely
194
+         * via this parameter.
195
+         *
196
+         * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
197
+         * If a size in html is given as px (or without unit as image size),
198
+         * this tells the corresponding size in pt.
199
+         * This adjusts the relative sizes to be similar to the rendering of the
200
+         * html page in a reference browser.
201
+         *
202
+         * In pdf, always 1 pt = 1/72 inch
203
+         *
204
+         * Rendering resolution of various browsers in px per inch:
205
+         * Windows Firefox and Internet Explorer:
206
+         *   SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
207
+         * Linux Firefox:
208
+         *   about:config *resolution: Default:96
209
+         *   (xorg screen dimension in mm and Desktop font dpi settings are ignored)
210
+         *
211
+         * Take care about extra font/image zoom factor of browser.
212
+         *
213
+         * In images, <img> size in pixel attribute, img css style, are overriding
214
+         * the real image dimension in px for rendering.
215
+         */
216
+        'dpi' => 96,
217
+
218
+        /**
219
+         * Enable inline PHP
220
+         *
221
+         * If this setting is set to true then DOMPDF will automatically evaluate
222
+         * inline PHP contained within <script type="text/php"> ... </script> tags.
223
+         *
224
+         * Enabling this for documents you do not trust (e.g. arbitrary remote html
225
+         * pages) is a security risk.  Set this option to false if you wish to process
226
+         * untrusted documents.
227
+         */
228
+        'enable_php' => true,
229
+
230
+        /**
231
+         * Enable inline Javascript
232
+         *
233
+         * If this setting is set to true then DOMPDF will automatically insert
234
+         * JavaScript code contained within <script type="text/javascript"> ... </script> tags.
235
+         */
236
+        'enable_javascript' => true,
237
+
238
+        /**
239
+         * Enable remote file access
240
+         *
241
+         * If this setting is set to true, DOMPDF will access remote sites for
242
+         * images and CSS files as required.
243
+         * This is required for part of test case www/test/image_variants.html through www/examples.php
244
+         *
245
+         * Attention!
246
+         * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
247
+         * allowing remote access to dompdf.php or on allowing remote html code to be passed to
248
+         * $dompdf = new DOMPDF(, $dompdf->load_html(...,
249
+         * This allows anonymous users to download legally doubtful internet content which on
250
+         * tracing back appears to being downloaded by your server, or allows malicious php code
251
+         * in remote html pages to be executed by your server with your account privileges.
252
+         */
253
+        'enable_remote' => true,
254
+
255
+        /**
256
+         * A ratio applied to the fonts height to be more like browsers' line height
257
+         */
258
+        'font_height_ratio' => 1.1,
259
+
260
+        /**
261
+         * Use the HTML5 Lib parser
262
+         *
263
+         * @deprecated This feature is now always on in dompdf 2.x
264
+         */
265
+        'enable_html5_parser' => true,
266
+    ],
267
+
268
+];

+ 1
- 2
database/migrations/2024_01_01_234943_create_transactions_table.php 查看文件

@@ -17,8 +17,7 @@ return new class extends Migration
17 17
             $table->foreignId('account_id')->nullable()->constrained()->nullOnDelete();
18 18
             $table->foreignId('bank_account_id')->nullable()->constrained()->nullOnDelete();
19 19
             $table->foreignId('contact_id')->nullable()->constrained()->nullOnDelete();
20
-            $table->string('type'); // income, expense, other
21
-            $table->string('method'); // deposit, withdrawal
20
+            $table->string('type'); // deposit, withdrawal, journal entry
22 21
             $table->string('payment_channel')->nullable(); // online, in store, other
23 22
             $table->string('description')->nullable();
24 23
             $table->text('notes')->nullable();

+ 22
- 19
package-lock.json 查看文件

@@ -609,12 +609,12 @@
609 609
             }
610 610
         },
611 611
         "node_modules/axios": {
612
-            "version": "1.6.7",
613
-            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
614
-            "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
612
+            "version": "1.6.8",
613
+            "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
614
+            "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
615 615
             "dev": true,
616 616
             "dependencies": {
617
-                "follow-redirects": "^1.15.4",
617
+                "follow-redirects": "^1.15.6",
618 618
                 "form-data": "^4.0.0",
619 619
                 "proxy-from-env": "^1.1.0"
620 620
             }
@@ -626,12 +626,15 @@
626 626
             "dev": true
627 627
         },
628 628
         "node_modules/binary-extensions": {
629
-            "version": "2.2.0",
630
-            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
631
-            "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
629
+            "version": "2.3.0",
630
+            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
631
+            "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
632 632
             "dev": true,
633 633
             "engines": {
634 634
                 "node": ">=8"
635
+            },
636
+            "funding": {
637
+                "url": "https://github.com/sponsors/sindresorhus"
635 638
             }
636 639
         },
637 640
         "node_modules/brace-expansion": {
@@ -845,9 +848,9 @@
845 848
             "dev": true
846 849
         },
847 850
         "node_modules/electron-to-chromium": {
848
-            "version": "1.4.701",
849
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.701.tgz",
850
-            "integrity": "sha512-K3WPQ36bUOtXg/1+69bFlFOvdSm0/0bGqmsfPDLRXLanoKXdA+pIWuf/VbA9b+2CwBFuONgl4NEz4OEm+OJOKA==",
851
+            "version": "1.4.708",
852
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.708.tgz",
853
+            "integrity": "sha512-iWgEEvREL4GTXXHKohhh33+6Y8XkPI5eHihDmm8zUk5Zo7HICEW+wI/j5kJ2tbuNUCXJ/sNXa03ajW635DiJXA==",
851 854
             "dev": true
852 855
         },
853 856
         "node_modules/emoji-regex": {
@@ -952,9 +955,9 @@
952 955
             }
953 956
         },
954 957
         "node_modules/follow-redirects": {
955
-            "version": "1.15.5",
956
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
957
-            "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
958
+            "version": "1.15.6",
959
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
960
+            "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
958 961
             "dev": true,
959 962
             "funding": [
960 963
                 {
@@ -1582,9 +1585,9 @@
1582 1585
             }
1583 1586
         },
1584 1587
         "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
1585
-            "version": "6.0.15",
1586
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
1587
-            "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
1588
+            "version": "6.0.16",
1589
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
1590
+            "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
1588 1591
             "dev": true,
1589 1592
             "dependencies": {
1590 1593
                 "cssesc": "^3.0.0",
@@ -1936,9 +1939,9 @@
1936 1939
             }
1937 1940
         },
1938 1941
         "node_modules/tailwindcss/node_modules/postcss-selector-parser": {
1939
-            "version": "6.0.15",
1940
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz",
1941
-            "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==",
1942
+            "version": "6.0.16",
1943
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz",
1944
+            "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==",
1942 1945
             "dev": true,
1943 1946
             "dependencies": {
1944 1947
                 "cssesc": "^3.0.0",

+ 4
- 13
resources/views/components/company/reports/account-balances.blade.php 查看文件

@@ -1,11 +1,13 @@
1 1
 <!DOCTYPE html>
2 2
 <html lang="en">
3 3
 <head>
4
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
4
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
+    <meta name="viewport" content="width=device-width, initial-scale=1">
5 6
     <title>Account Balances</title>
6 7
     <style>
7 8
         @page {
8
-            margin: 0.25in 0.25in 1in 0.25in;
9
+            size: A4;
10
+            margin: 8.5mm 8.5mm 30mm 8.5mm;
9 11
         }
10 12
 
11 13
         .header {
@@ -122,15 +124,4 @@
122 124
             </tr>
123 125
         </tfoot>
124 126
     </table>
125
-    <script type="text/php">
126
-        if (isset($pdf)) {
127
-            $font = $fontMetrics->getFont("Inter, sans-serif", "normal");
128
-            $size = 8;
129
-
130
-            $pageText = "Page {PAGE_NUM} of {PAGE_COUNT}";
131
-            $x = 533;
132
-            $y = 820;
133
-            $pdf->page_text($x, $y, $pageText, $font, $size);
134
-        }
135
-    </script>
136 127
 </body>

+ 23
- 0
resources/views/filament/company/components/tables/actions/mark-as-reviewed.blade.php 查看文件

@@ -0,0 +1,23 @@
1
+@php
2
+    $isDisabled = $action->isDisabled();
3
+@endphp
4
+
5
+<span
6
+    x-data="{}"
7
+    x-tooltip="{
8
+        content: @js($action->getTooltip()),
9
+        theme: $store.theme === 'dark' ? 'light' : 'dark',
10
+        placement: 'left',
11
+    }"
12
+>
13
+    <x-filament::icon-button
14
+        @class([
15
+            'disabled:cursor-not-allowed disabled:pointer-events-auto disabled:hover:text-gray-400',
16
+        ])
17
+        :icon="$action->getIcon()"
18
+        :color="$action->getColor()"
19
+        :disabled="$isDisabled"
20
+        :size="$action->getIconSize()"
21
+        :wire:click="$action->getLivewireClickHandler()"
22
+    />
23
+</span>

正在加载...
取消
保存