Andrew Wallo 4 mēnešus atpakaļ
vecāks
revīzija
c6c467b036
21 mainītis faili ar 212 papildinājumiem un 83 dzēšanām
  1. 2
    3
      app/Concerns/HasTransactionAction.php
  2. 6
    0
      app/Filament/Actions/CreateTransactionAction.php
  3. 1
    1
      app/Filament/Company/Pages/Reports/BaseReportPage.php
  4. 37
    15
      app/Filament/Company/Resources/Accounting/TransactionResource.php
  5. 16
    17
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ListTransactions.php
  6. 1
    25
      app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ViewTransaction.php
  7. 1
    1
      app/Filament/Company/Resources/Purchases/BillResource/Pages/ViewBill.php
  8. 1
    1
      app/Filament/Company/Resources/Purchases/VendorResource/Pages/ViewVendor.php
  9. 1
    1
      app/Filament/Company/Resources/Sales/ClientResource/Pages/ViewClient.php
  10. 1
    1
      app/Filament/Company/Resources/Sales/EstimateResource/Pages/ViewEstimate.php
  11. 1
    2
      app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php
  12. 1
    1
      app/Filament/Company/Resources/Sales/RecurringInvoiceResource/Pages/ViewRecurringInvoice.php
  13. 4
    0
      app/Filament/Tables/Actions/EditTransactionAction.php
  14. 75
    7
      app/Models/Accounting/Transaction.php
  15. 6
    0
      app/Models/Common/Client.php
  16. 7
    0
      app/Models/Common/Vendor.php
  17. 14
    0
      app/Observers/TransactionObserver.php
  18. 3
    4
      app/Providers/Filament/CompanyPanelProvider.php
  19. 1
    1
      app/Services/ReportService.php
  20. 30
    0
      database/migrations/2025_05_17_194711_add_payeeable_to_transactions_table.php
  21. 3
    3
      tests/Feature/Accounting/TransactionTest.php

+ 2
- 3
app/Concerns/HasTransactionAction.php Parādīt failu

@@ -114,9 +114,8 @@ trait HasTransactionAction
114 114
                     ->required(),
115 115
                 Forms\Components\Select::make('account_id')
116 116
                     ->label('Category')
117
-                    ->options(fn (Forms\Get $get, ?Transaction $transaction) => Transaction::getChartAccountOptions(type: TransactionType::parse($get('type')), nominalAccountsOnly: true, currentAccountId: $transaction?->account_id))
117
+                    ->options(fn (Forms\Get $get, ?Transaction $transaction) => Transaction::getTransactionAccountOptions(type: TransactionType::parse($get('type')), currentAccountId: $transaction?->account_id))
118 118
                     ->searchable()
119
-                    ->preload()
120 119
                     ->required(),
121 120
                 Forms\Components\Textarea::make('notes')
122 121
                     ->label('Notes')
@@ -341,7 +340,7 @@ trait HasTransactionAction
341 340
                 ->label('Description'),
342 341
             Forms\Components\Select::make('account_id')
343 342
                 ->label('Account')
344
-                ->options(fn (?JournalEntry $journalEntry): array => Transaction::getChartAccountOptions(currentAccountId: $journalEntry?->account_id))
343
+                ->options(fn (?JournalEntry $journalEntry): array => Transaction::getJournalAccountOptions(currentAccountId: $journalEntry?->account_id))
345 344
                 ->softRequired()
346 345
                 ->searchable(),
347 346
             Forms\Components\TextInput::make('amount')

+ 6
- 0
app/Filament/Actions/CreateTransactionAction.php Parādīt failu

@@ -18,6 +18,12 @@ class CreateTransactionAction extends CreateAction
18 18
     {
19 19
         parent::setUp();
20 20
 
21
+        $this->label(null);
22
+
23
+        $this->groupedIcon(null);
24
+
25
+        $this->slideOver();
26
+
21 27
         $this->modalWidth(function (): MaxWidth {
22 28
             return match ($this->transactionType) {
23 29
                 TransactionType::Journal => MaxWidth::Screen,

+ 1
- 1
app/Filament/Company/Pages/Reports/BaseReportPage.php Parādīt failu

@@ -235,7 +235,7 @@ abstract class BaseReportPage extends Page
235 235
                 ->outlined()
236 236
                 ->dropdownWidth('max-w-[7rem]')
237 237
                 ->dropdownPlacement('bottom-end')
238
-                ->icon('heroicon-c-chevron-down')
238
+                ->icon('heroicon-m-chevron-down')
239 239
                 ->iconSize(IconSize::Small)
240 240
                 ->iconPosition(IconPosition::After),
241 241
         ];

+ 37
- 15
app/Filament/Company/Resources/Accounting/TransactionResource.php Parādīt failu

@@ -9,6 +9,8 @@ use App\Filament\Tables\Actions\EditTransactionAction;
9 9
 use App\Filament\Tables\Actions\ReplicateBulkAction;
10 10
 use App\Models\Accounting\JournalEntry;
11 11
 use App\Models\Accounting\Transaction;
12
+use App\Models\Common\Client;
13
+use App\Models\Common\Vendor;
12 14
 use Exception;
13 15
 use Filament\Forms\Components\DatePicker;
14 16
 use Filament\Forms\Components\Grid;
@@ -29,14 +31,8 @@ class TransactionResource extends Resource
29 31
 {
30 32
     protected static ?string $model = Transaction::class;
31 33
 
32
-    protected static ?string $navigationGroup = 'Accounting';
33
-
34
-    protected static ?string $navigationIcon = 'heroicon-o-banknotes';
35
-
36 34
     protected static ?string $recordTitleAttribute = 'description';
37 35
 
38
-    protected static bool $isGloballySearchable = false;
39
-
40 36
     public static function form(Form $form): Form
41 37
     {
42 38
         return $form
@@ -51,6 +47,7 @@ class TransactionResource extends Resource
51 47
                     'account',
52 48
                     'bankAccount.account',
53 49
                     'journalEntries.account',
50
+                    'payeeable',
54 51
                 ])
55 52
                     ->where(function (Builder $query) {
56 53
                         $query->whereNull('transactionable_id')
@@ -69,13 +66,20 @@ class TransactionResource extends Resource
69 66
                 Tables\Columns\TextColumn::make('description')
70 67
                     ->label('Description')
71 68
                     ->limit(50)
69
+                    ->searchable()
72 70
                     ->toggleable(),
71
+                Tables\Columns\TextColumn::make('payeeable.name')
72
+                    ->label('Payee')
73
+                    ->searchable()
74
+                    ->toggleable(isToggledHiddenByDefault: true),
73 75
                 Tables\Columns\TextColumn::make('bankAccount.account.name')
74 76
                     ->label('Account')
77
+                    ->searchable()
75 78
                     ->toggleable(),
76 79
                 Tables\Columns\TextColumn::make('account.name')
77 80
                     ->label('Category')
78 81
                     ->prefix(static fn (Transaction $transaction) => $transaction->type->isTransfer() ? 'Transfer to ' : null)
82
+                    ->searchable()
79 83
                     ->toggleable()
80 84
                     ->state(static fn (Transaction $transaction) => $transaction->account->name ?? 'Journal Entry'),
81 85
                 Tables\Columns\TextColumn::make('amount')
@@ -91,26 +95,45 @@ class TransactionResource extends Resource
91 95
                     ->sortable()
92 96
                     ->currency(static fn (Transaction $transaction) => $transaction->bankAccount?->account->currency_code),
93 97
             ])
94
-            ->recordClasses(static fn (Transaction $transaction) => $transaction->reviewed ? 'bg-primary-300/10' : null)
95 98
             ->defaultSort('posted_at', 'desc')
96 99
             ->filters([
97 100
                 Tables\Filters\SelectFilter::make('bank_account_id')
98 101
                     ->label('Account')
99 102
                     ->searchable()
100
-                    ->options(fn () => Transaction::getBankAccountOptions(false)),
103
+                    ->options(static fn () => Transaction::getBankAccountOptions(false)),
101 104
                 Tables\Filters\SelectFilter::make('account_id')
102 105
                     ->label('Category')
103 106
                     ->multiple()
104
-                    ->options(fn () => Transaction::getChartAccountOptions(nominalAccountsOnly: false)),
107
+                    ->options(static fn () => Transaction::getChartAccountOptions()),
105 108
                 Tables\Filters\TernaryFilter::make('reviewed')
106 109
                     ->label('Status')
107
-                    ->native(false)
108 110
                     ->trueLabel('Reviewed')
109 111
                     ->falseLabel('Not Reviewed'),
110 112
                 Tables\Filters\SelectFilter::make('type')
111 113
                     ->label('Type')
112
-                    ->native(false)
113 114
                     ->options(TransactionType::class),
115
+                Tables\Filters\TernaryFilter::make('is_payment')
116
+                    ->label('Payment')
117
+                    ->default(false),
118
+                Tables\Filters\SelectFilter::make('payee')
119
+                    ->label('Payee')
120
+                    ->options(static fn () => Transaction::getPayeeOptions())
121
+                    ->searchable()
122
+                    ->query(function (Builder $query, array $data): Builder {
123
+                        if (empty($data['value'])) {
124
+                            return $query;
125
+                        }
126
+
127
+                        $id = (int) $data['value'];
128
+
129
+                        if ($id < 0) {
130
+                            return $query->where('payeeable_type', Vendor::class)
131
+                                ->where('payeeable_id', abs($id));
132
+                        } else {
133
+                            return $query->where('payeeable_type', Client::class)
134
+                                ->where('payeeable_id', $id);
135
+                        }
136
+                    }),
114 137
                 static::buildDateRangeFilter('posted_at', 'Posted', true),
115 138
                 static::buildDateRangeFilter('updated_at', 'Last modified'),
116 139
             ])
@@ -121,6 +144,8 @@ class TransactionResource extends Resource
121 144
                         $filters['account_id'],
122 145
                         $filters['reviewed'],
123 146
                         $filters['type'],
147
+                        $filters['is_payment'],
148
+                        $filters['payee'],
124 149
                     ])
125 150
                     ->columnSpanFull()
126 151
                     ->extraAttributes(['class' => 'border-b border-gray-200 dark:border-white/10 pb-8']),
@@ -148,14 +173,11 @@ class TransactionResource extends Resource
148 173
                 Tables\Actions\ActionGroup::make([
149 174
                     Tables\Actions\ActionGroup::make([
150 175
                         EditTransactionAction::make('editTransaction')
151
-                            ->label('Edit transaction')
152 176
                             ->visible(static fn (Transaction $transaction) => $transaction->type->isStandard() && ! $transaction->transactionable_id),
153 177
                         EditTransactionAction::make('editTransfer')
154
-                            ->label('Edit transfer')
155 178
                             ->type(TransactionType::Transfer)
156 179
                             ->visible(static fn (Transaction $transaction) => $transaction->type->isTransfer()),
157
-                        EditTransactionAction::make('editJournalTransaction')
158
-                            ->label('Edit journal transaction')
180
+                        EditTransactionAction::make('editJournalEntry')
159 181
                             ->type(TransactionType::Journal)
160 182
                             ->visible(static fn (Transaction $transaction) => $transaction->type->isJournal() && ! $transaction->transactionable_id),
161 183
                         Tables\Actions\ReplicateAction::make()

+ 16
- 17
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ListTransactions.php Parādīt failu

@@ -11,7 +11,6 @@ use App\Services\PlaidService;
11 11
 use Filament\Actions;
12 12
 use Filament\Resources\Pages\ListRecords;
13 13
 use Filament\Support\Enums\IconPosition;
14
-use Filament\Support\Enums\IconSize;
15 14
 use Filament\Support\Enums\MaxWidth;
16 15
 
17 16
 class ListTransactions extends ListRecords
@@ -28,20 +27,22 @@ class ListTransactions extends ListRecords
28 27
     protected function getHeaderActions(): array
29 28
     {
30 29
         return [
31
-            CreateTransactionAction::make('addIncome')
32
-                ->label('Add income')
33
-                ->type(TransactionType::Deposit),
34
-            CreateTransactionAction::make('addExpense')
35
-                ->label('Add expense')
36
-                ->type(TransactionType::Withdrawal),
37
-            CreateTransactionAction::make('addTransfer')
38
-                ->label('Add transfer')
39
-                ->type(TransactionType::Transfer),
40 30
             Actions\ActionGroup::make([
41
-                CreateTransactionAction::make('addJournalTransaction')
42
-                    ->label('Add journal transaction')
43
-                    ->type(TransactionType::Journal)
44
-                    ->groupedIcon(null),
31
+                CreateTransactionAction::make('addDeposit')
32
+                    ->type(TransactionType::Deposit),
33
+                CreateTransactionAction::make('addWithdrawal')
34
+                    ->type(TransactionType::Withdrawal),
35
+                CreateTransactionAction::make('addTransfer')
36
+                    ->type(TransactionType::Transfer),
37
+                CreateTransactionAction::make('addJournalEntry')
38
+                    ->type(TransactionType::Journal),
39
+            ])
40
+                ->label('Add transaction')
41
+                ->button()
42
+                ->dropdownPlacement('bottom-end')
43
+                ->icon('heroicon-m-chevron-down')
44
+                ->iconPosition(IconPosition::After),
45
+            Actions\ActionGroup::make([
45 46
                 Actions\Action::make('connectBank')
46 47
                     ->label('Connect your bank')
47 48
                     ->visible(app(PlaidService::class)->isEnabled())
@@ -50,10 +51,8 @@ class ListTransactions extends ListRecords
50 51
                 ->label('More')
51 52
                 ->button()
52 53
                 ->outlined()
53
-                ->dropdownWidth('max-w-fit')
54 54
                 ->dropdownPlacement('bottom-end')
55
-                ->icon('heroicon-c-chevron-down')
56
-                ->iconSize(IconSize::Small)
55
+                ->icon('heroicon-m-chevron-down')
57 56
                 ->iconPosition(IconPosition::After),
58 57
         ];
59 58
     }

+ 1
- 25
app/Filament/Company/Resources/Accounting/TransactionResource/Pages/ViewTransaction.php Parādīt failu

@@ -7,8 +7,6 @@ use App\Filament\Company\Resources\Accounting\TransactionResource;
7 7
 use App\Models\Accounting\JournalEntry;
8 8
 use App\Models\Accounting\Transaction;
9 9
 use Filament\Actions;
10
-use Filament\Infolists\Components\Grid;
11
-use Filament\Infolists\Components\RepeatableEntry;
12 10
 use Filament\Infolists\Components\Section;
13 11
 use Filament\Infolists\Components\TextEntry;
14 12
 use Filament\Infolists\Infolist;
@@ -57,7 +55,7 @@ class ViewTransaction extends ViewRecord
57 55
                 ->button()
58 56
                 ->outlined()
59 57
                 ->dropdownPlacement('bottom-end')
60
-                ->icon('heroicon-c-chevron-down')
58
+                ->icon('heroicon-m-chevron-down')
61 59
                 ->iconSize(IconSize::Small)
62 60
                 ->iconPosition(IconPosition::After),
63 61
         ];
@@ -103,28 +101,6 @@ class ViewTransaction extends ViewRecord
103 101
                             ->label('Notes')
104 102
                             ->columnSpanFull(),
105 103
                     ]),
106
-                Section::make('Journal Entries')
107
-                    ->visible(fn (Transaction $record) => $record->type->isJournal())
108
-                    ->schema([
109
-                        RepeatableEntry::make('journalEntries')
110
-                            ->hiddenLabel()
111
-                            ->schema([
112
-                                Grid::make(4)
113
-                                    ->schema([
114
-                                        TextEntry::make('type')
115
-                                            ->label('Type')
116
-                                            ->badge(),
117
-                                        TextEntry::make('description')
118
-                                            ->label('Description'),
119
-                                        TextEntry::make('account.name')
120
-                                            ->label('Account'),
121
-                                        TextEntry::make('amount')
122
-                                            ->label('Amount')
123
-                                            ->currency(fn (JournalEntry $record) => $record->transaction->bankAccount?->account->currency_code)
124
-                                            ->alignEnd(),
125
-                                    ]),
126
-                            ]),
127
-                    ]),
128 104
             ]);
129 105
     }
130 106
 

+ 1
- 1
app/Filament/Company/Resources/Purchases/BillResource/Pages/ViewBill.php Parādīt failu

@@ -38,7 +38,7 @@ class ViewBill extends ViewRecord
38 38
                 ->button()
39 39
                 ->outlined()
40 40
                 ->dropdownPlacement('bottom-end')
41
-                ->icon('heroicon-c-chevron-down')
41
+                ->icon('heroicon-m-chevron-down')
42 42
                 ->iconSize(IconSize::Small)
43 43
                 ->iconPosition(IconPosition::After),
44 44
         ];

+ 1
- 1
app/Filament/Company/Resources/Purchases/VendorResource/Pages/ViewVendor.php Parādīt failu

@@ -51,7 +51,7 @@ class ViewVendor extends ViewRecord
51 51
                 ->button()
52 52
                 ->outlined()
53 53
                 ->dropdownPlacement('bottom-end')
54
-                ->icon('heroicon-c-chevron-down')
54
+                ->icon('heroicon-m-chevron-down')
55 55
                 ->iconSize(IconSize::Small)
56 56
                 ->iconPosition(IconPosition::After),
57 57
         ];

+ 1
- 1
app/Filament/Company/Resources/Sales/ClientResource/Pages/ViewClient.php Parādīt failu

@@ -64,7 +64,7 @@ class ViewClient extends ViewRecord
64 64
                 ->button()
65 65
                 ->outlined()
66 66
                 ->dropdownPlacement('bottom-end')
67
-                ->icon('heroicon-c-chevron-down')
67
+                ->icon('heroicon-m-chevron-down')
68 68
                 ->iconSize(IconSize::Small)
69 69
                 ->iconPosition(IconPosition::After),
70 70
         ];

+ 1
- 1
app/Filament/Company/Resources/Sales/EstimateResource/Pages/ViewEstimate.php Parādīt failu

@@ -49,7 +49,7 @@ class ViewEstimate extends ViewRecord
49 49
                 ->button()
50 50
                 ->outlined()
51 51
                 ->dropdownPlacement('bottom-end')
52
-                ->icon('heroicon-c-chevron-down')
52
+                ->icon('heroicon-m-chevron-down')
53 53
                 ->iconSize(IconSize::Small)
54 54
                 ->iconPosition(IconPosition::After),
55 55
         ];

+ 1
- 2
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php Parādīt failu

@@ -17,7 +17,6 @@ use Filament\Resources\Pages\ViewRecord;
17 17
 use Filament\Support\Enums\FontWeight;
18 18
 use Filament\Support\Enums\IconPosition;
19 19
 use Filament\Support\Enums\IconSize;
20
-use Filament\Support\Enums\MaxWidth;
21 20
 use Illuminate\Support\HtmlString;
22 21
 
23 22
 class ViewInvoice extends ViewRecord
@@ -47,7 +46,7 @@ class ViewInvoice extends ViewRecord
47 46
                 ->button()
48 47
                 ->outlined()
49 48
                 ->dropdownPlacement('bottom-end')
50
-                ->icon('heroicon-c-chevron-down')
49
+                ->icon('heroicon-m-chevron-down')
51 50
                 ->iconSize(IconSize::Small)
52 51
                 ->iconPosition(IconPosition::After),
53 52
         ];

+ 1
- 1
app/Filament/Company/Resources/Sales/RecurringInvoiceResource/Pages/ViewRecurringInvoice.php Parādīt failu

@@ -44,7 +44,7 @@ class ViewRecurringInvoice extends ViewRecord
44 44
                 ->button()
45 45
                 ->outlined()
46 46
                 ->dropdownPlacement('bottom-end')
47
-                ->icon('heroicon-c-chevron-down')
47
+                ->icon('heroicon-m-chevron-down')
48 48
                 ->iconSize(IconSize::Small)
49 49
                 ->iconPosition(IconPosition::After),
50 50
         ];

+ 4
- 0
app/Filament/Tables/Actions/EditTransactionAction.php Parādīt failu

@@ -18,6 +18,10 @@ class EditTransactionAction extends EditAction
18 18
     {
19 19
         parent::setUp();
20 20
 
21
+        $this->label(null);
22
+
23
+        $this->slideOver();
24
+
21 25
         $this->modalWidth(function (): MaxWidth {
22 26
             return match ($this->transactionType) {
23 27
                 TransactionType::Journal => MaxWidth::Screen,

+ 75
- 7
app/Models/Accounting/Transaction.php Parādīt failu

@@ -6,10 +6,13 @@ use App\Casts\TransactionAmountCast;
6 6
 use App\Concerns\Blamable;
7 7
 use App\Concerns\CompanyOwned;
8 8
 use App\Enums\Accounting\AccountCategory;
9
+use App\Enums\Accounting\AccountType;
9 10
 use App\Enums\Accounting\PaymentMethod;
10 11
 use App\Enums\Accounting\TransactionType;
11 12
 use App\Models\Banking\BankAccount;
13
+use App\Models\Common\Client;
12 14
 use App\Models\Common\Contact;
15
+use App\Models\Common\Vendor;
13 16
 use App\Observers\TransactionObserver;
14 17
 use Database\Factories\Accounting\TransactionFactory;
15 18
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
@@ -86,6 +89,11 @@ class Transaction extends Model
86 89
         return $this->morphTo();
87 90
     }
88 91
 
92
+    public function payeeable(): MorphTo
93
+    {
94
+        return $this->morphTo();
95
+    }
96
+
89 97
     public function isUncategorized(): bool
90 98
     {
91 99
         return $this->journalEntries->contains(fn (JournalEntry $entry) => $entry->account->isUncategorized());
@@ -137,19 +145,60 @@ class Transaction extends Model
137 145
             ->toArray();
138 146
     }
139 147
 
140
-    public static function getChartAccountOptions(?TransactionType $type = null, ?bool $nominalAccountsOnly = null, ?int $currentAccountId = null): array
148
+    public static function getChartAccountOptions(): array
141 149
     {
142
-        $nominalAccountsOnly ??= false;
150
+        return Account::query()
151
+            ->select(['id', 'name', 'category'])
152
+            ->get()
153
+            ->groupBy(fn (Account $account) => $account->category->getPluralLabel())
154
+            ->map(fn (Collection $accounts, string $category) => $accounts->pluck('name', 'id'))
155
+            ->toArray();
156
+    }
143 157
 
144
-        $excludedCategory = match ($type) {
145
-            TransactionType::Deposit => AccountCategory::Expense,
146
-            TransactionType::Withdrawal => AccountCategory::Revenue,
158
+    public static function getTransactionAccountOptions(
159
+        TransactionType $type,
160
+        ?int $currentAccountId = null
161
+    ): array {
162
+        $associatedAccountTypes = match ($type) {
163
+            TransactionType::Deposit => [
164
+                AccountType::OperatingRevenue,     // Sales, service income
165
+                AccountType::NonOperatingRevenue,  // Interest, dividends received
166
+                AccountType::CurrentLiability,     // Loans received
167
+                AccountType::NonCurrentLiability,  // Long-term financing
168
+                AccountType::Equity,               // Owner contributions
169
+                AccountType::ContraExpense,        // Refunds of expenses
170
+                AccountType::UncategorizedRevenue,
171
+            ],
172
+            TransactionType::Withdrawal => [
173
+                AccountType::OperatingExpense,     // Regular business expenses
174
+                AccountType::NonOperatingExpense,  // Interest paid, etc.
175
+                AccountType::CurrentLiability,     // Loan payments
176
+                AccountType::NonCurrentLiability,  // Long-term debt payments
177
+                AccountType::Equity,               // Owner withdrawals
178
+                AccountType::ContraRevenue,        // Customer refunds, discounts
179
+                AccountType::UncategorizedExpense,
180
+            ],
147 181
             default => null,
148 182
         };
149 183
 
150 184
         return Account::query()
151
-            ->when($nominalAccountsOnly, fn (Builder $query) => $query->doesntHave('bankAccount'))
152
-            ->when($excludedCategory, fn (Builder $query) => $query->whereNot('category', $excludedCategory))
185
+            ->doesntHave('adjustment')
186
+            ->doesntHave('bankAccount')
187
+            ->when($associatedAccountTypes, fn (Builder $query) => $query->whereIn('type', $associatedAccountTypes))
188
+            ->where(function (Builder $query) use ($currentAccountId) {
189
+                $query->where('archived', false)
190
+                    ->orWhere('id', $currentAccountId);
191
+            })
192
+            ->get()
193
+            ->groupBy(fn (Account $account) => $account->category->getPluralLabel())
194
+            ->map(fn (Collection $accounts, string $category) => $accounts->pluck('name', 'id'))
195
+            ->toArray();
196
+    }
197
+
198
+    public static function getJournalAccountOptions(
199
+        ?int $currentAccountId = null
200
+    ): array {
201
+        return Account::query()
153 202
             ->where(function (Builder $query) use ($currentAccountId) {
154 203
                 $query->where('archived', false)
155 204
                     ->orWhere('id', $currentAccountId);
@@ -173,6 +222,25 @@ class Transaction extends Model
173 222
             ->first();
174 223
     }
175 224
 
225
+    public static function getPayeeOptions(): array
226
+    {
227
+        $clients = Client::query()
228
+            ->orderBy('name')
229
+            ->pluck('name', 'id')
230
+            ->toArray();
231
+
232
+        $vendors = Vendor::query()
233
+            ->orderBy('name')
234
+            ->pluck('name', 'id')
235
+            ->mapWithKeys(fn ($name, $id) => [-$id => $name])
236
+            ->toArray();
237
+
238
+        return [
239
+            'Clients' => $clients,
240
+            'Vendors' => $vendors,
241
+        ];
242
+    }
243
+
176 244
     protected static function newFactory(): Factory
177 245
     {
178 246
         return TransactionFactory::new();

+ 6
- 0
app/Models/Common/Client.php Parādīt failu

@@ -8,6 +8,7 @@ use App\Enums\Common\AddressType;
8 8
 use App\Models\Accounting\Estimate;
9 9
 use App\Models\Accounting\Invoice;
10 10
 use App\Models\Accounting\RecurringInvoice;
11
+use App\Models\Accounting\Transaction;
11 12
 use App\Models\Setting\Currency;
12 13
 use Illuminate\Database\Eloquent\Factories\HasFactory;
13 14
 use Illuminate\Database\Eloquent\Model;
@@ -215,6 +216,11 @@ class Client extends Model
215 216
         return $this;
216 217
     }
217 218
 
219
+    public function transactions(): MorphMany
220
+    {
221
+        return $this->morphMany(Transaction::class, 'payeeable');
222
+    }
223
+
218 224
     public function contacts(): MorphMany
219 225
     {
220 226
         return $this->morphMany(Contact::class, 'contactable');

+ 7
- 0
app/Models/Common/Vendor.php Parādīt failu

@@ -7,11 +7,13 @@ use App\Concerns\CompanyOwned;
7 7
 use App\Enums\Common\ContractorType;
8 8
 use App\Enums\Common\VendorType;
9 9
 use App\Models\Accounting\Bill;
10
+use App\Models\Accounting\Transaction;
10 11
 use App\Models\Setting\Currency;
11 12
 use Illuminate\Database\Eloquent\Factories\HasFactory;
12 13
 use Illuminate\Database\Eloquent\Model;
13 14
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
14 15
 use Illuminate\Database\Eloquent\Relations\HasMany;
16
+use Illuminate\Database\Eloquent\Relations\MorphMany;
15 17
 use Illuminate\Database\Eloquent\Relations\MorphOne;
16 18
 
17 19
 class Vendor extends Model
@@ -112,6 +114,11 @@ class Vendor extends Model
112 114
         return $this->hasMany(Bill::class);
113 115
     }
114 116
 
117
+    public function transactions(): MorphMany
118
+    {
119
+        return $this->morphMany(Transaction::class, 'payeeable');
120
+    }
121
+
115 122
     public function currency(): BelongsTo
116 123
     {
117 124
         return $this->belongsTo(Currency::class, 'currency_code', 'code');

+ 14
- 0
app/Observers/TransactionObserver.php Parādīt failu

@@ -7,6 +7,8 @@ use App\Enums\Accounting\InvoiceStatus;
7 7
 use App\Models\Accounting\Bill;
8 8
 use App\Models\Accounting\Invoice;
9 9
 use App\Models\Accounting\Transaction;
10
+use App\Models\Common\Client;
11
+use App\Models\Common\Vendor;
10 12
 use App\Services\TransactionService;
11 13
 use App\Utilities\Currency\CurrencyConverter;
12 14
 use Illuminate\Database\Eloquent\Builder;
@@ -26,6 +28,18 @@ class TransactionObserver
26 28
         if ($transaction->type->isTransfer() && $transaction->description === null) {
27 29
             $transaction->description = 'Account Transfer';
28 30
         }
31
+
32
+        if ($transaction->transactionable && ! $transaction->payeeable_id) {
33
+            $document = $transaction->transactionable;
34
+
35
+            if ($document instanceof Invoice) {
36
+                $transaction->payeeable_id = $document->client_id;
37
+                $transaction->payeeable_type = Client::class;
38
+            } elseif ($document instanceof Bill) {
39
+                $transaction->payeeable_id = $document->vendor_id;
40
+                $transaction->payeeable_type = Vendor::class;
41
+            }
42
+        }
29 43
     }
30 44
 
31 45
     /**

+ 3
- 4
app/Providers/Filament/CompanyPanelProvider.php Parādīt failu

@@ -92,7 +92,7 @@ class CompanyPanelProvider extends PanelProvider
92 92
                     ->passwordReset();
93 93
             })
94 94
             ->tenantMenu(false)
95
-            ->plugin(
95
+            ->plugins([
96 96
                 FilamentCompanies::make()
97 97
                     ->userPanel('user')
98 98
                     ->switchCurrentCompany()
@@ -114,8 +114,6 @@ class CompanyPanelProvider extends PanelProvider
114 114
                         providers: [Provider::Github],
115 115
                         features: [Feature::RememberSession, Feature::ProviderAvatars],
116 116
                     ),
117
-            )
118
-            ->plugin(
119 117
                 PanelShiftDropdown::make()
120 118
                     ->logoutItem()
121 119
                     ->companySettings()
@@ -123,7 +121,7 @@ class CompanyPanelProvider extends PanelProvider
123 121
                         return $builder
124 122
                             ->items(Account::getNavigationItems());
125 123
                     }),
126
-            )
124
+            ])
127 125
             ->colors([
128 126
                 'primary' => Color::Indigo,
129 127
             ])
@@ -173,6 +171,7 @@ class CompanyPanelProvider extends PanelProvider
173 171
                             ]),
174 172
                     ]);
175 173
             })
174
+            ->globalSearch(false)
176 175
             ->sidebarCollapsibleOnDesktop()
177 176
             ->viteTheme('resources/css/filament/company/theme.css')
178 177
             ->brandLogo(static fn () => view('components.icons.logo'))

+ 1
- 1
app/Services/ReportService.php Parādīt failu

@@ -282,7 +282,7 @@ class ReportService
282 282
             return [
283 283
                 'type' => 'transaction',
284 284
                 'action' => match ($transaction->type) {
285
-                    TransactionType::Journal => 'editJournalTransaction',
285
+                    TransactionType::Journal => 'editJournalEntry',
286 286
                     TransactionType::Transfer => 'editTransfer',
287 287
                     default => 'editTransaction',
288 288
                 },

+ 30
- 0
database/migrations/2025_05_17_194711_add_payeeable_to_transactions_table.php Parādīt failu

@@ -0,0 +1,30 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::table('transactions', function (Blueprint $table) {
15
+            $table->after('transactionable_id', function (Blueprint $table) {
16
+                $table->nullableMorphs('payeeable');
17
+            });
18
+        });
19
+    }
20
+
21
+    /**
22
+     * Reverse the migrations.
23
+     */
24
+    public function down(): void
25
+    {
26
+        Schema::table('transactions', function (Blueprint $table) {
27
+            $table->dropMorphs('payeeable');
28
+        });
29
+    }
30
+};

+ 3
- 3
tests/Feature/Accounting/TransactionTest.php Parādīt failu

@@ -297,7 +297,7 @@ it('can add a journal transaction', function () {
297 297
     $undoRepeaterFake = JournalEntryRepeater::fake();
298 298
 
299 299
     livewire(ListTransactions::class)
300
-        ->mountAction('addJournalTransaction')
300
+        ->mountAction('addJournalEntry')
301 301
         ->assertActionDataSet([
302 302
             'posted_at' => today(),
303 303
             'journalEntries' => [
@@ -378,7 +378,7 @@ it('does not show Edit Transfer or Edit Journal Transaction for deposit or withd
378 378
 
379 379
     livewire(ListTransactions::class)
380 380
         ->assertTableActionHidden('editTransfer', $transaction)
381
-        ->assertTableActionHidden('editJournalTransaction', $transaction);
381
+        ->assertTableActionHidden('editJournalEntry', $transaction);
382 382
 })->with([
383 383
     TransactionType::Deposit,
384 384
     TransactionType::Withdrawal,
@@ -422,7 +422,7 @@ it('does not show Edit Transaction or Edit Journal Transaction for transfer tran
422 422
 
423 423
     livewire(ListTransactions::class)
424 424
         ->assertTableActionHidden('editTransaction', $transaction)
425
-        ->assertTableActionHidden('editJournalTransaction', $transaction);
425
+        ->assertTableActionHidden('editJournalEntry', $transaction);
426 426
 });
427 427
 
428 428
 it('replicates a transaction with correct journal entries', function () {

Notiek ielāde…
Atcelt
Saglabāt