Kaynağa Gözat

wip Invoice Payments

3.x
Andrew Wallo 10 ay önce
ebeveyn
işleme
c6cb2784ca

+ 27
- 0
app/Enums/Accounting/PaymentMethod.php Dosyayı Görüntüle

@@ -0,0 +1,27 @@
1
+<?php
2
+
3
+namespace App\Enums\Accounting;
4
+
5
+use Filament\Support\Contracts\HasLabel;
6
+
7
+enum PaymentMethod: string implements HasLabel
8
+{
9
+    case BankPayment = 'bank_payment';
10
+    case Cash = 'cash';
11
+    case Check = 'check';
12
+    case CreditCard = 'credit_card';
13
+    case PayPal = 'paypal';
14
+    case Other = 'other';
15
+
16
+    public function getLabel(): ?string
17
+    {
18
+        return match ($this) {
19
+            self::BankPayment => 'Bank Payment',
20
+            self::Cash => 'Cash',
21
+            self::Check => 'Check',
22
+            self::CreditCard => 'Credit Card',
23
+            self::PayPal => 'PayPal',
24
+            self::Other => 'Other',
25
+        };
26
+    }
27
+}

+ 1
- 1
app/Filament/Company/Pages/Accounting/Transactions.php Dosyayı Görüntüle

@@ -296,7 +296,7 @@ class Transactions extends Page implements HasTable
296 296
                         }
297 297
                     )
298 298
                     ->sortable()
299
-                    ->currency(static fn (Transaction $transaction) => $transaction->bankAccount->account->currency_code ?? CurrencyAccessor::getDefaultCurrency(), true),
299
+                    ->currency(static fn (Transaction $transaction) => $transaction->bankAccount?->account->currency_code ?? CurrencyAccessor::getDefaultCurrency(), true),
300 300
             ])
301 301
             ->recordClasses(static fn (Transaction $transaction) => $transaction->reviewed ? 'bg-primary-300/10' : null)
302 302
             ->defaultSort('posted_at', 'desc')

+ 64
- 45
app/Filament/Company/Resources/Sales/InvoiceResource.php Dosyayı Görüntüle

@@ -4,8 +4,10 @@ namespace App\Filament\Company\Resources\Sales;
4 4
 
5 5
 use App\Enums\Accounting\InvoiceStatus;
6 6
 use App\Enums\Accounting\JournalEntryType;
7
+use App\Enums\Accounting\PaymentMethod;
7 8
 use App\Enums\Accounting\TransactionType;
8 9
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
10
+use App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
9 11
 use App\Models\Accounting\Account;
10 12
 use App\Models\Accounting\Adjustment;
11 13
 use App\Models\Accounting\DocumentLineItem;
@@ -17,10 +19,12 @@ use App\Utilities\Currency\CurrencyConverter;
17 19
 use Awcodes\TableRepeater\Components\TableRepeater;
18 20
 use Awcodes\TableRepeater\Header;
19 21
 use Carbon\CarbonInterface;
22
+use Closure;
20 23
 use Filament\Forms;
21 24
 use Filament\Forms\Components\FileUpload;
22 25
 use Filament\Forms\Form;
23 26
 use Filament\Resources\Resource;
27
+use Filament\Support\Enums\Alignment;
24 28
 use Filament\Support\Enums\MaxWidth;
25 29
 use Filament\Tables;
26 30
 use Filament\Tables\Table;
@@ -459,6 +463,7 @@ class InvoiceResource extends Resource
459 463
                             $replica->date = now();
460 464
                             $replica->due_date = now()->addDays($original->company->defaultInvoice->payment_terms->getDays());
461 465
                         })
466
+                        ->databaseTransaction()
462 467
                         ->after(function (Invoice $original, Invoice $replica) {
463 468
                             $original->lineItems->each(function (DocumentLineItem $lineItem) use ($replica) {
464 469
                                 $replicaLineItem = $lineItem->replicate([
@@ -489,7 +494,9 @@ class InvoiceResource extends Resource
489 494
                         ->visible(function (Invoice $record) {
490 495
                             return $record->isDraft();
491 496
                         })
492
-                        ->action(function (Invoice $record) {
497
+                        ->databaseTransaction()
498
+                        ->successNotificationTitle('Invoice Approved')
499
+                        ->action(function (Invoice $record, Tables\Actions\Action $action) {
493 500
                             $transaction = $record->transactions()->create([
494 501
                                 'type' => TransactionType::Journal,
495 502
                                 'posted_at' => now(),
@@ -499,7 +506,7 @@ class InvoiceResource extends Resource
499 506
 
500 507
                             $transaction->journalEntries()->create([
501 508
                                 'type' => JournalEntryType::Debit,
502
-                                'account_id' => Account::where('name', 'Accounts Receivable')->first()->id,
509
+                                'account_id' => Account::getAccountsReceivableAccount()->id,
503 510
                                 'amount' => $record->total,
504 511
                                 'description' => $transaction->description,
505 512
                             ]);
@@ -525,6 +532,8 @@ class InvoiceResource extends Resource
525 532
                             $record->updateQuietly([
526 533
                                 'status' => InvoiceStatus::Unsent,
527 534
                             ]);
535
+
536
+                            $action->success();
528 537
                         }),
529 538
                     Tables\Actions\Action::make('markAsSent')
530 539
                         ->label('Mark as Sent')
@@ -532,13 +541,19 @@ class InvoiceResource extends Resource
532 541
                         ->visible(function (Invoice $record) {
533 542
                             return $record->status === InvoiceStatus::Unsent;
534 543
                         })
535
-                        ->action(function (Invoice $record) {
544
+                        ->successNotificationTitle('Invoice Sent')
545
+                        ->action(function (Invoice $record, Tables\Actions\Action $action) {
536 546
                             $record->updateQuietly([
537 547
                                 'status' => InvoiceStatus::Sent,
538 548
                             ]);
549
+
550
+                            $action->success();
539 551
                         }),
540 552
                     Tables\Actions\Action::make('recordPayment')
541
-                        ->label('Record Payment')
553
+                        ->label(fn (Invoice $record) => $record->status === InvoiceStatus::Overpaid ? 'Refund Overpayment' : 'Record Payment')
554
+                        ->stickyModalHeader()
555
+                        ->stickyModalFooter()
556
+                        ->modalFooterActionsAlignment(Alignment::End)
542 557
                         ->modalWidth(MaxWidth::TwoExtraLarge)
543 558
                         ->icon('heroicon-o-credit-card')
544 559
                         ->visible(function (Invoice $record) {
@@ -547,28 +562,58 @@ class InvoiceResource extends Resource
547 562
                         ->mountUsing(function (Invoice $record, Form $form) {
548 563
                             $form->fill([
549 564
                                 'posted_at' => now(),
550
-                                'amount' => $record->amount_due,
565
+                                'amount' => $record->status === InvoiceStatus::Overpaid ? ltrim($record->amount_due, '-') : $record->amount_due,
551 566
                             ]);
552 567
                         })
568
+                        ->databaseTransaction()
569
+                        ->successNotificationTitle('Payment Recorded')
553 570
                         ->form([
554 571
                             Forms\Components\DatePicker::make('posted_at')
555
-                                ->label('Payment Date'),
572
+                                ->label('Date'),
556 573
                             Forms\Components\TextInput::make('amount')
557 574
                                 ->label('Amount')
558 575
                                 ->required()
559
-                                ->money(),
576
+                                ->money()
577
+                                ->live(onBlur: true)
578
+                                ->helperText(function (Invoice $record, $state) {
579
+                                    if (! CurrencyConverter::isValidAmount($state)) {
580
+                                        return null;
581
+                                    }
582
+
583
+                                    $amountDue = $record->getRawOriginal('amount_due');
584
+
585
+                                    $amount = CurrencyConverter::convertToCents($state);
586
+
587
+                                    if ($amount <= 0) {
588
+                                        return 'Please enter a valid positive amount';
589
+                                    }
590
+
591
+                                    if ($record->status === InvoiceStatus::Overpaid) {
592
+                                        $newAmountDue = $amountDue + $amount;
593
+                                    } else {
594
+                                        $newAmountDue = $amountDue - $amount;
595
+                                    }
596
+
597
+                                    return match (true) {
598
+                                        $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue),
599
+                                        $newAmountDue === 0 => 'Invoice will be fully paid',
600
+                                        default => 'Invoice will be overpaid by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue)),
601
+                                    };
602
+                                })
603
+                                ->rules([
604
+                                    static fn (): Closure => static function (string $attribute, $value, Closure $fail) {
605
+                                        if (! CurrencyConverter::isValidAmount($value)) {
606
+                                            $fail('Please enter a valid amount');
607
+                                        }
608
+                                    },
609
+                                ]),
560 610
                             Forms\Components\Select::make('payment_method')
561 611
                                 ->label('Payment Method')
562
-                                ->options([
563
-                                    'bank_payment' => 'Bank Payment',
564
-                                    'cash' => 'Cash',
565
-                                    'check' => 'Check',
566
-                                    'credit_card' => 'Credit Card',
567
-                                    'paypal' => 'PayPal',
568
-                                    'other' => 'Other',
569
-                                ]),
612
+                                ->required()
613
+                                ->options(PaymentMethod::class),
570 614
                             Forms\Components\Select::make('bank_account_id')
571 615
                                 ->label('Account')
616
+                                ->required()
572 617
                                 ->options(BankAccount::query()
573 618
                                     ->get()
574 619
                                     ->pluck('account.name', 'id'))
@@ -576,36 +621,10 @@ class InvoiceResource extends Resource
576 621
                             Forms\Components\Textarea::make('notes')
577 622
                                 ->label('Notes'),
578 623
                         ])
579
-                        ->action(function (Invoice $record, array $data) {
580
-                            $payment = $record->transactions()->create([
581
-                                'type' => TransactionType::Deposit,
582
-                                'is_payment' => true,
583
-                                'posted_at' => $data['posted_at'],
584
-                                'amount' => $data['amount'],
585
-                                'payment_method' => $data['payment_method'],
586
-                                'bank_account_id' => $data['bank_account_id'],
587
-                                'account_id' => Account::where('name', 'Accounts Receivable')->first()?->id,
588
-                                'description' => 'Payment for Invoice #' . $record->invoice_number,
589
-                                'notes' => $data['notes'] ?? null,
590
-                            ]);
591
-
592
-                            $amountPaid = $record->getRawOriginal('amount_paid');
593
-                            $paymentAmount = $payment->getRawOriginal('amount');
594
-                            $totalAmount = $record->getRawOriginal('total');
595
-
596
-                            $newAmountPaid = $amountPaid + $paymentAmount;
597
-
598
-                            $record->amount_paid = CurrencyConverter::convertCentsToFloat($newAmountPaid);
599
-
600
-                            if ($newAmountPaid > $totalAmount) {
601
-                                $record->status = InvoiceStatus::Overpaid;
602
-                            } elseif ($newAmountPaid === $totalAmount) {
603
-                                $record->status = InvoiceStatus::Paid;
604
-                            } else {
605
-                                $record->status = InvoiceStatus::Partial;
606
-                            }
624
+                        ->action(function (Invoice $record, Tables\Actions\Action $action, array $data) {
625
+                            $record->recordPayment($data);
607 626
 
608
-                            $record->save();
627
+                            $action->success();
609 628
                         }),
610 629
                 ]),
611 630
             ])
@@ -619,7 +638,7 @@ class InvoiceResource extends Resource
619 638
     public static function getRelations(): array
620 639
     {
621 640
         return [
622
-            //
641
+            RelationManagers\PaymentsRelationManager::class,
623 642
         ];
624 643
     }
625 644
 

+ 15
- 44
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php Dosyayı Görüntüle

@@ -5,45 +5,44 @@ namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
5 5
 use App\Enums\Accounting\InvoiceStatus;
6 6
 use App\Filament\Company\Resources\Sales\ClientResource;
7 7
 use App\Filament\Company\Resources\Sales\InvoiceResource;
8
+use App\Models\Accounting\Invoice;
8 9
 use Carbon\CarbonInterface;
9
-use Filament\Infolists\Components\Actions\Action;
10
-use Filament\Infolists\Components\Grid;
10
+use Filament\Infolists\Components\Section;
11 11
 use Filament\Infolists\Components\TextEntry;
12
-use Filament\Infolists\Components\ViewEntry;
13 12
 use Filament\Infolists\Infolist;
14 13
 use Filament\Resources\Pages\ViewRecord;
15 14
 use Filament\Support\Enums\FontWeight;
16
-use Filament\Support\Enums\MaxWidth;
17 15
 use Illuminate\Support\Carbon;
18 16
 
19 17
 class ViewInvoice extends ViewRecord
20 18
 {
21 19
     protected static string $resource = InvoiceResource::class;
22 20
 
21
+    protected $listeners = [
22
+        'refresh' => '$refresh',
23
+    ];
24
+
23 25
     public function infolist(Infolist $infolist): Infolist
24 26
     {
25 27
         return $infolist
26
-            ->columns(1)
27 28
             ->schema([
28
-                Grid::make(5)
29
+                Section::make('Invoice Details')
30
+                    ->columns(3)
29 31
                     ->schema([
30 32
                         TextEntry::make('invoice_number')
31
-                            ->label('Invoice #')
32
-                            ->size(TextEntry\TextEntrySize::Large),
33
+                            ->label('Invoice #'),
33 34
                         TextEntry::make('status')
34
-                            ->badge()
35
-                            ->size(TextEntry\TextEntrySize::Large),
35
+                            ->badge(),
36 36
                         TextEntry::make('client.name')
37
+                            ->label('Client')
37 38
                             ->color('primary')
38 39
                             ->weight(FontWeight::SemiBold)
39
-                            ->size(TextEntry\TextEntrySize::Large)
40
-                            ->url(fn ($record) => ClientResource::getUrl('edit', ['record' => $record->client_id]), true),
40
+                            ->url(fn (Invoice $record) => ClientResource::getUrl('edit', ['record' => $record->client_id])),
41 41
                         TextEntry::make('amount_due')
42 42
                             ->label('Amount Due')
43
-                            ->money()
44
-                            ->size(TextEntry\TextEntrySize::Large),
43
+                            ->money(),
45 44
                         TextEntry::make('due_date')
46
-                            ->label('Due')
45
+                            ->label('Due Date')
47 46
                             ->formatStateUsing(function (TextEntry $entry, mixed $state) {
48 47
                                 if (blank($state)) {
49 48
                                     return null;
@@ -59,30 +58,7 @@ class ViewInvoice extends ViewRecord
59 58
                                 return $date->diffForHumans([
60 59
                                     'options' => CarbonInterface::ONE_DAY_WORDS,
61 60
                                 ]);
62
-                            })
63
-                            ->size(TextEntry\TextEntrySize::Large),
64
-                    ]),
65
-                ViewEntry::make('create')
66
-                    ->view('filament.infolists.invoice-create-step')
67
-                    ->registerActions([
68
-                        Action::make('approveDraft')
69
-                            ->label('Approve Draft')
70
-                            ->action('approveDraft')
71
-                            ->visible(fn ($record) => $record->status === InvoiceStatus::Draft),
72
-                        Action::make('edit')
73
-                            ->label('Edit')
74
-                            ->outlined()
75
-                            ->url(fn ($record) => InvoiceResource::getUrl('edit', ['record' => $record->id]), true),
76
-                        Action::make('markAsSent')
77
-                            ->label('Mark as Sent')
78
-                            ->outlined()
79
-                            ->action('markAsSent'),
80
-                        Action::make('sendInvoice')
81
-                            ->label('Send Invoice')
82
-                            ->action('sendInvoice'),
83
-                        Action::make('recordPayment')
84
-                            ->label('Record Payment')
85
-                            ->action('recordPayment'),
61
+                            }),
86 62
                     ]),
87 63
             ]);
88 64
     }
@@ -100,9 +76,4 @@ class ViewInvoice extends ViewRecord
100 76
             'status' => InvoiceStatus::Sent,
101 77
         ]);
102 78
     }
103
-
104
-    public function getMaxContentWidth(): MaxWidth | string | null
105
-    {
106
-        return MaxWidth::FiveExtraLarge;
107
-    }
108 79
 }

+ 187
- 0
app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php Dosyayı Görüntüle

@@ -0,0 +1,187 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
4
+
5
+use App\Enums\Accounting\InvoiceStatus;
6
+use App\Enums\Accounting\PaymentMethod;
7
+use App\Enums\Accounting\TransactionType;
8
+use App\Filament\Company\Resources\Sales\InvoiceResource\Pages\ViewInvoice;
9
+use App\Models\Accounting\Invoice;
10
+use App\Models\Accounting\Transaction;
11
+use App\Models\Banking\BankAccount;
12
+use App\Utilities\Currency\CurrencyAccessor;
13
+use App\Utilities\Currency\CurrencyConverter;
14
+use Closure;
15
+use Filament\Forms;
16
+use Filament\Forms\Form;
17
+use Filament\Resources\RelationManagers\RelationManager;
18
+use Filament\Support\Colors\Color;
19
+use Filament\Support\Enums\FontWeight;
20
+use Filament\Support\Enums\MaxWidth;
21
+use Filament\Tables;
22
+use Filament\Tables\Table;
23
+use Illuminate\Database\Eloquent\Model;
24
+
25
+class PaymentsRelationManager extends RelationManager
26
+{
27
+    protected static string $relationship = 'payments';
28
+
29
+    protected static ?string $modelLabel = 'Payment';
30
+
31
+    protected $listeners = [
32
+        'refresh' => '$refresh',
33
+    ];
34
+
35
+    public function isReadOnly(): bool
36
+    {
37
+        return false;
38
+    }
39
+
40
+    public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
41
+    {
42
+        return $ownerRecord->status !== InvoiceStatus::Draft && $pageClass === ViewInvoice::class;
43
+    }
44
+
45
+    public function form(Form $form): Form
46
+    {
47
+        return $form
48
+            ->columns(1)
49
+            ->schema([
50
+                Forms\Components\DatePicker::make('posted_at')
51
+                    ->label('Date'),
52
+                Forms\Components\TextInput::make('amount')
53
+                    ->label('Amount')
54
+                    ->required()
55
+                    ->money()
56
+                    ->live(onBlur: true)
57
+                    ->helperText(function (RelationManager $livewire, $state, ?Transaction $record) {
58
+                        if (! CurrencyConverter::isValidAmount($state)) {
59
+                            return null;
60
+                        }
61
+
62
+                        /** @var Invoice $ownerRecord */
63
+                        $ownerRecord = $livewire->getOwnerRecord();
64
+
65
+                        $amountDue = $ownerRecord->getRawOriginal('amount_due');
66
+
67
+                        $amount = CurrencyConverter::convertToCents($state);
68
+
69
+                        if ($amount <= 0) {
70
+                            return 'Please enter a valid positive amount';
71
+                        }
72
+
73
+                        $currentPaymentAmount = $record?->getRawOriginal('amount') ?? 0;
74
+
75
+                        if ($ownerRecord->status === InvoiceStatus::Overpaid) {
76
+                            $newAmountDue = $amountDue + $amount - $currentPaymentAmount;
77
+                        } else {
78
+                            $newAmountDue = $amountDue - $amount + $currentPaymentAmount;
79
+                        }
80
+
81
+                        return match (true) {
82
+                            $newAmountDue > 0 => 'Amount due after payment will be ' . CurrencyConverter::formatCentsToMoney($newAmountDue),
83
+                            $newAmountDue === 0 => 'Invoice will be fully paid',
84
+                            default => 'Invoice will be overpaid by ' . CurrencyConverter::formatCentsToMoney(abs($newAmountDue)),
85
+                        };
86
+                    })
87
+                    ->rules([
88
+                        static fn (): Closure => static function (string $attribute, $value, Closure $fail) {
89
+                            if (! CurrencyConverter::isValidAmount($value)) {
90
+                                $fail('Please enter a valid amount');
91
+                            }
92
+                        },
93
+                    ]),
94
+                Forms\Components\Select::make('payment_method')
95
+                    ->label('Payment Method')
96
+                    ->required()
97
+                    ->options(PaymentMethod::class),
98
+                Forms\Components\Select::make('bank_account_id')
99
+                    ->label('Account')
100
+                    ->required()
101
+                    ->options(BankAccount::query()
102
+                        ->get()
103
+                        ->pluck('account.name', 'id'))
104
+                    ->searchable(),
105
+                Forms\Components\Textarea::make('notes')
106
+                    ->label('Notes'),
107
+            ]);
108
+    }
109
+
110
+    public function table(Table $table): Table
111
+    {
112
+        return $table
113
+            ->recordTitleAttribute('description')
114
+            ->columns([
115
+                Tables\Columns\TextColumn::make('posted_at')
116
+                    ->label('Date')
117
+                    ->sortable()
118
+                    ->defaultDateFormat(),
119
+                Tables\Columns\TextColumn::make('type')
120
+                    ->label('Type')
121
+                    ->sortable()
122
+                    ->toggleable(isToggledHiddenByDefault: true),
123
+                Tables\Columns\TextColumn::make('description')
124
+                    ->label('Description')
125
+                    ->limit(30)
126
+                    ->toggleable(),
127
+                Tables\Columns\TextColumn::make('bankAccount.account.name')
128
+                    ->label('Account')
129
+                    ->toggleable(),
130
+                Tables\Columns\TextColumn::make('amount')
131
+                    ->label('Amount')
132
+                    ->weight(static fn (Transaction $transaction) => $transaction->reviewed ? null : FontWeight::SemiBold)
133
+                    ->color(
134
+                        static fn (Transaction $transaction) => match ($transaction->type) {
135
+                            TransactionType::Deposit => Color::rgb('rgb(' . Color::Green[700] . ')'),
136
+                            TransactionType::Journal => 'primary',
137
+                            default => null,
138
+                        }
139
+                    )
140
+                    ->sortable()
141
+                    ->currency(static fn (Transaction $transaction) => $transaction->bankAccount?->account->currency_code ?? CurrencyAccessor::getDefaultCurrency(), true),
142
+            ])
143
+            ->filters([
144
+                //
145
+            ])
146
+            ->headerActions([
147
+                Tables\Actions\CreateAction::make()
148
+                    ->label(fn () => $this->getOwnerRecord()->status === InvoiceStatus::Overpaid ? 'Refund Overpayment' : 'Record Payment')
149
+                    ->modalHeading(fn (Tables\Actions\CreateAction $action) => $action->getLabel())
150
+                    ->modalWidth(MaxWidth::TwoExtraLarge)
151
+                    ->visible(function () {
152
+                        return $this->getOwnerRecord()->canRecordPayment();
153
+                    })
154
+                    ->mountUsing(function (Form $form) {
155
+                        $record = $this->getOwnerRecord();
156
+                        $form->fill([
157
+                            'posted_at' => now(),
158
+                            'amount' => $record->status === InvoiceStatus::Overpaid ? ltrim($record->amount_due, '-') : $record->amount_due,
159
+                        ]);
160
+                    })
161
+                    ->databaseTransaction()
162
+                    ->successNotificationTitle('Payment Recorded')
163
+                    ->action(function (Tables\Actions\CreateAction $action, array $data) {
164
+                        /** @var Invoice $record */
165
+                        $record = $this->getOwnerRecord();
166
+
167
+                        $record->recordPayment($data);
168
+
169
+                        $action->success();
170
+
171
+                        $this->dispatch('refresh');
172
+                    }),
173
+            ])
174
+            ->actions([
175
+                Tables\Actions\EditAction::make()
176
+                    ->modalWidth(MaxWidth::TwoExtraLarge)
177
+                    ->after(fn () => $this->dispatch('refresh')),
178
+                Tables\Actions\DeleteAction::make()
179
+                    ->after(fn () => $this->dispatch('refresh')),
180
+            ])
181
+            ->bulkActions([
182
+                Tables\Actions\BulkActionGroup::make([
183
+                    Tables\Actions\DeleteBulkAction::make(),
184
+                ]),
185
+            ]);
186
+    }
187
+}

+ 5
- 0
app/Models/Accounting/Account.php Dosyayı Görüntüle

@@ -123,6 +123,11 @@ class Account extends Model
123 123
         return $this->hasMany(JournalEntry::class, 'account_id');
124 124
     }
125 125
 
126
+    public static function getAccountsReceivableAccount(): self
127
+    {
128
+        return self::where('name', 'Accounts Receivable')->firstOrFail();
129
+    }
130
+
126 131
     protected static function newFactory(): Factory
127 132
     {
128 133
         return AccountFactory::new();

+ 1
- 1
app/Models/Accounting/Bill.php Dosyayı Görüntüle

@@ -85,7 +85,7 @@ class Bill extends Model
85 85
     public function approvalTransaction(): MorphOne
86 86
     {
87 87
         return $this->morphOne(Transaction::class, 'transactionable')
88
-            ->where('type', TransactionType::Approval);
88
+            ->where('type', TransactionType::Journal);
89 89
     }
90 90
 
91 91
     public static function getNextDocumentNumber(): string

+ 30
- 2
app/Models/Accounting/Invoice.php Dosyayı Görüntüle

@@ -8,12 +8,15 @@ use App\Concerns\CompanyOwned;
8 8
 use App\Enums\Accounting\InvoiceStatus;
9 9
 use App\Enums\Accounting\TransactionType;
10 10
 use App\Models\Common\Client;
11
+use App\Observers\InvoiceObserver;
12
+use Illuminate\Database\Eloquent\Attributes\ObservedBy;
11 13
 use Illuminate\Database\Eloquent\Factories\HasFactory;
12 14
 use Illuminate\Database\Eloquent\Model;
13 15
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
14 16
 use Illuminate\Database\Eloquent\Relations\MorphMany;
15 17
 use Illuminate\Database\Eloquent\Relations\MorphOne;
16 18
 
19
+#[ObservedBy(InvoiceObserver::class)]
17 20
 class Invoice extends Model
18 21
 {
19 22
     use Blamable;
@@ -90,7 +93,7 @@ class Invoice extends Model
90 93
     public function approvalTransaction(): MorphOne
91 94
     {
92 95
         return $this->morphOne(Transaction::class, 'transactionable')
93
-            ->where('type', TransactionType::Approval);
96
+            ->where('type', TransactionType::Journal);
94 97
     }
95 98
 
96 99
     public function isDraft(): bool
@@ -103,7 +106,6 @@ class Invoice extends Model
103 106
         return ! in_array($this->status, [
104 107
             InvoiceStatus::Draft,
105 108
             InvoiceStatus::Paid,
106
-            InvoiceStatus::Overpaid,
107 109
             InvoiceStatus::Void,
108 110
         ]);
109 111
     }
@@ -140,4 +142,30 @@ class Invoice extends Model
140 142
             next: $numberNext
141 143
         );
142 144
     }
145
+
146
+    public function recordPayment(array $data): void
147
+    {
148
+        $isRefund = $this->status === InvoiceStatus::Overpaid;
149
+
150
+        if ($isRefund) {
151
+            $transactionType = TransactionType::Withdrawal;
152
+            $transactionDescription = 'Refund for Overpayment on Invoice #' . $this->invoice_number;
153
+        } else {
154
+            $transactionType = TransactionType::Deposit;
155
+            $transactionDescription = 'Payment for Invoice #' . $this->invoice_number;
156
+        }
157
+
158
+        // Create transaction
159
+        $this->transactions()->create([
160
+            'type' => $transactionType,
161
+            'is_payment' => true,
162
+            'posted_at' => $data['posted_at'],
163
+            'amount' => $data['amount'],
164
+            'payment_method' => $data['payment_method'],
165
+            'bank_account_id' => $data['bank_account_id'],
166
+            'account_id' => Account::getAccountsReceivableAccount()->id,
167
+            'description' => $transactionDescription,
168
+            'notes' => $data['notes'] ?? null,
169
+        ]);
170
+    }
143 171
 }

+ 2
- 0
app/Models/Accounting/Transaction.php Dosyayı Görüntüle

@@ -5,6 +5,7 @@ namespace App\Models\Accounting;
5 5
 use App\Casts\TransactionAmountCast;
6 6
 use App\Concerns\Blamable;
7 7
 use App\Concerns\CompanyOwned;
8
+use App\Enums\Accounting\PaymentMethod;
8 9
 use App\Enums\Accounting\TransactionType;
9 10
 use App\Models\Banking\BankAccount;
10 11
 use App\Models\Common\Contact;
@@ -48,6 +49,7 @@ class Transaction extends Model
48 49
 
49 50
     protected $casts = [
50 51
         'type' => TransactionType::class,
52
+        'payment_method' => PaymentMethod::class,
51 53
         'amount' => TransactionAmountCast::class,
52 54
         'pending' => 'boolean',
53 55
         'reviewed' => 'boolean',

+ 0
- 5
app/Models/Banking/BankAccount.php Dosyayı Görüntüle

@@ -66,11 +66,6 @@ class BankAccount extends Model
66 66
         return $this->belongsTo(Institution::class, 'institution_id');
67 67
     }
68 68
 
69
-    public function payments(): HasMany
70
-    {
71
-        return $this->hasMany(Payment::class, 'bank_account_id');
72
-    }
73
-
74 69
     public function transactions(): HasMany
75 70
     {
76 71
         return $this->hasMany(Transaction::class, 'bank_account_id');

+ 0
- 44
app/Models/Banking/Payment.php Dosyayı Görüntüle

@@ -1,44 +0,0 @@
1
-<?php
2
-
3
-namespace App\Models\Banking;
4
-
5
-use App\Casts\MoneyCast;
6
-use App\Concerns\Blamable;
7
-use App\Concerns\CompanyOwned;
8
-use Illuminate\Database\Eloquent\Factories\HasFactory;
9
-use Illuminate\Database\Eloquent\Model;
10
-use Illuminate\Database\Eloquent\Relations\BelongsTo;
11
-use Illuminate\Database\Eloquent\Relations\MorphTo;
12
-
13
-class Payment extends Model
14
-{
15
-    use Blamable;
16
-    use CompanyOwned;
17
-    use HasFactory;
18
-
19
-    protected $fillable = [
20
-        'company_id',
21
-        'date',
22
-        'amount',
23
-        'payment_method',
24
-        'bank_account_id',
25
-        'notes',
26
-        'created_by',
27
-        'updated_by',
28
-    ];
29
-
30
-    protected $casts = [
31
-        'date' => 'date',
32
-        'amount' => MoneyCast::class,
33
-    ];
34
-
35
-    public function payable(): MorphTo
36
-    {
37
-        return $this->morphTo();
38
-    }
39
-
40
-    public function bankAccount(): BelongsTo
41
-    {
42
-        return $this->belongsTo(BankAccount::class);
43
-    }
44
-}

+ 0
- 5
app/Models/Company.php Dosyayı Görüntüle

@@ -152,11 +152,6 @@ class Company extends FilamentCompaniesCompany implements HasAvatar
152 152
         return $this->hasOne(Localization::class, 'company_id');
153 153
     }
154 154
 
155
-    public function payments(): HasMany
156
-    {
157
-        return $this->hasMany(Banking\Payment::class, 'company_id');
158
-    }
159
-
160 155
     public function profile(): HasOne
161 156
     {
162 157
         return $this->hasOne(CompanyProfile::class, 'company_id');

+ 64
- 0
app/Observers/InvoiceObserver.php Dosyayı Görüntüle

@@ -0,0 +1,64 @@
1
+<?php
2
+
3
+namespace App\Observers;
4
+
5
+use App\Models\Accounting\DocumentLineItem;
6
+use App\Models\Accounting\Invoice;
7
+use App\Models\Accounting\Transaction;
8
+use Illuminate\Support\Facades\DB;
9
+
10
+class InvoiceObserver
11
+{
12
+    /**
13
+     * Handle the Invoice "created" event.
14
+     */
15
+    public function created(Invoice $invoice): void
16
+    {
17
+        //
18
+    }
19
+
20
+    /**
21
+     * Handle the Invoice "updated" event.
22
+     */
23
+    public function updated(Invoice $invoice): void
24
+    {
25
+        //
26
+    }
27
+
28
+    public function deleting(Invoice $invoice): void
29
+    {
30
+        DB::transaction(function () use ($invoice) {
31
+            $invoice->lineItems()->each(function (DocumentLineItem $lineItem) {
32
+                $lineItem->delete();
33
+            });
34
+
35
+            $invoice->transactions()->each(function (Transaction $transaction) {
36
+                $transaction->delete();
37
+            });
38
+        });
39
+    }
40
+
41
+    /**
42
+     * Handle the Invoice "deleted" event.
43
+     */
44
+    public function deleted(Invoice $invoice): void
45
+    {
46
+        //
47
+    }
48
+
49
+    /**
50
+     * Handle the Invoice "restored" event.
51
+     */
52
+    public function restored(Invoice $invoice): void
53
+    {
54
+        //
55
+    }
56
+
57
+    /**
58
+     * Handle the Invoice "force deleted" event.
59
+     */
60
+    public function forceDeleted(Invoice $invoice): void
61
+    {
62
+        //
63
+    }
64
+}

+ 68
- 1
app/Observers/TransactionObserver.php Dosyayı Görüntüle

@@ -2,8 +2,13 @@
2 2
 
3 3
 namespace App\Observers;
4 4
 
5
+use App\Enums\Accounting\InvoiceStatus;
6
+use App\Models\Accounting\Invoice;
5 7
 use App\Models\Accounting\Transaction;
6 8
 use App\Services\TransactionService;
9
+use App\Utilities\Currency\CurrencyConverter;
10
+use Illuminate\Database\Eloquent\Builder;
11
+use Illuminate\Support\Facades\DB;
7 12
 
8 13
 class TransactionObserver
9 14
 {
@@ -27,6 +32,16 @@ class TransactionObserver
27 32
     public function created(Transaction $transaction): void
28 33
     {
29 34
         $this->transactionService->createJournalEntries($transaction);
35
+
36
+        if (! $transaction->is_payment) {
37
+            return;
38
+        }
39
+
40
+        $invoice = $transaction->transactionable;
41
+
42
+        if ($invoice instanceof Invoice) {
43
+            $this->updateInvoiceTotals($invoice);
44
+        }
30 45
     }
31 46
 
32 47
     /**
@@ -37,6 +52,16 @@ class TransactionObserver
37 52
         $transaction->refresh(); // DO NOT REMOVE
38 53
 
39 54
         $this->transactionService->updateJournalEntries($transaction);
55
+
56
+        if (! $transaction->is_payment) {
57
+            return;
58
+        }
59
+
60
+        $invoice = $transaction->transactionable;
61
+
62
+        if ($invoice instanceof Invoice) {
63
+            $this->updateInvoiceTotals($invoice);
64
+        }
40 65
     }
41 66
 
42 67
     /**
@@ -44,6 +69,48 @@ class TransactionObserver
44 69
      */
45 70
     public function deleting(Transaction $transaction): void
46 71
     {
47
-        $this->transactionService->deleteJournalEntries($transaction);
72
+        DB::transaction(function () use ($transaction) {
73
+            $this->transactionService->deleteJournalEntries($transaction);
74
+
75
+            if (! $transaction->is_payment) {
76
+                return;
77
+            }
78
+
79
+            $invoice = $transaction->transactionable;
80
+
81
+            if ($invoice instanceof Invoice) {
82
+                $this->updateInvoiceTotals($invoice, $transaction);
83
+            }
84
+        });
85
+    }
86
+
87
+    public function deleted(Transaction $transaction): void
88
+    {
89
+        //
90
+    }
91
+
92
+    protected function updateInvoiceTotals(Invoice $invoice, ?Transaction $excludedTransaction = null): void
93
+    {
94
+        $depositTotal = (int) $invoice->deposits()
95
+            ->when($excludedTransaction, fn (Builder $query) => $query->whereKeyNot($excludedTransaction->getKey()))
96
+            ->sum('amount');
97
+
98
+        $withdrawalTotal = (int) $invoice->withdrawals()
99
+            ->when($excludedTransaction, fn (Builder $query) => $query->whereKeyNot($excludedTransaction->getKey()))
100
+            ->sum('amount');
101
+
102
+        $totalPaid = $depositTotal - $withdrawalTotal;
103
+
104
+        $invoiceTotal = (int) $invoice->getRawOriginal('total');
105
+
106
+        $invoice->updateQuietly([
107
+            'amount_paid' => CurrencyConverter::convertCentsToFloat($totalPaid),
108
+            'status' => match (true) {
109
+                $totalPaid > $invoiceTotal => InvoiceStatus::Overpaid,
110
+                $totalPaid === $invoiceTotal => InvoiceStatus::Paid,
111
+                $totalPaid === 0 => InvoiceStatus::Sent,
112
+                default => InvoiceStatus::Partial,
113
+            },
114
+        ]);
48 115
     }
49 116
 }

+ 2
- 0
app/Providers/FilamentCompaniesServiceProvider.php Dosyayı Görüntüle

@@ -256,8 +256,10 @@ class FilamentCompaniesServiceProvider extends PanelProvider
256 256
 
257 257
         Actions\CreateAction::configureUsing(static fn (Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
258 258
         Actions\EditAction::configureUsing(static fn (Actions\EditAction $action) => FilamentComponentConfigurator::configureActionModals($action));
259
+        Actions\DeleteAction::configureUsing(static fn (Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));
259 260
         Tables\Actions\EditAction::configureUsing(static fn (Tables\Actions\EditAction $action) => FilamentComponentConfigurator::configureActionModals($action));
260 261
         Tables\Actions\CreateAction::configureUsing(static fn (Tables\Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
262
+        Tables\Actions\DeleteAction::configureUsing(static fn (Tables\Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));
261 263
         Forms\Components\DateTimePicker::configureUsing(static function (Forms\Components\DateTimePicker $component) {
262 264
             $component->native(false);
263 265
         });

+ 6
- 1
app/Support/FilamentComponentConfigurator.php Dosyayı Görüntüle

@@ -15,8 +15,13 @@ class FilamentComponentConfigurator
15 15
             ->stickyModalFooter()
16 16
             ->modalFooterActionsAlignment(Alignment::End);
17 17
 
18
-        if ($action instanceof CreateAction) {
18
+        if ($action instanceof CreateAction || $action instanceof \Filament\Tables\Actions\CreateAction) {
19 19
             $action->createAnother(false);
20 20
         }
21 21
     }
22
+
23
+    public static function configureDeleteAction(MountableAction $action): void
24
+    {
25
+        $action->databaseTransaction();
26
+    }
22 27
 }

+ 17
- 0
app/Utilities/Currency/CurrencyConverter.php Dosyayı Görüntüle

@@ -63,6 +63,23 @@ class CurrencyConverter
63 63
         return money($amount, $currency)->getValue();
64 64
     }
65 65
 
66
+    public static function isValidAmount(?string $amount, ?string $currency = null): bool
67
+    {
68
+        $currency ??= CurrencyAccessor::getDefaultCurrency();
69
+
70
+        if (blank($amount)) {
71
+            return false;
72
+        }
73
+
74
+        try {
75
+            money($amount, $currency);
76
+        } catch (\Exception $e) {
77
+            return false;
78
+        }
79
+
80
+        return true;
81
+    }
82
+
66 83
     public static function handleCurrencyChange(Set $set, $state): void
67 84
     {
68 85
         $currency = currency($state);

+ 0
- 23
database/factories/Banking/PaymentFactory.php Dosyayı Görüntüle

@@ -1,23 +0,0 @@
1
-<?php
2
-
3
-namespace Database\Factories\Banking;
4
-
5
-use Illuminate\Database\Eloquent\Factories\Factory;
6
-
7
-/**
8
- * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Banking\Payment>
9
- */
10
-class PaymentFactory extends Factory
11
-{
12
-    /**
13
-     * Define the model's default state.
14
-     *
15
-     * @return array<string, mixed>
16
-     */
17
-    public function definition(): array
18
-    {
19
-        return [
20
-            //
21
-        ];
22
-    }
23
-}

+ 0
- 36
database/migrations/2024_11_13_215818_create_payments_table.php Dosyayı Görüntüle

@@ -1,36 +0,0 @@
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::create('payments', function (Blueprint $table) {
15
-            $table->id();
16
-            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
-            $table->morphs('payable');
18
-            $table->date('date');
19
-            $table->integer('amount');
20
-            $table->string('payment_method');
21
-            $table->foreignId('bank_account_id')->nullable()->constrained()->nullOnDelete();
22
-            $table->text('notes')->nullable();
23
-            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
24
-            $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
25
-            $table->timestamps();
26
-        });
27
-    }
28
-
29
-    /**
30
-     * Reverse the migrations.
31
-     */
32
-    public function down(): void
33
-    {
34
-        Schema::dropIfExists('payments');
35
-    }
36
-};

+ 3
- 3
package-lock.json Dosyayı Görüntüle

@@ -10,9 +10,9 @@
10 10
                 "autoprefixer": "^10.4.20",
11 11
                 "axios": "^1.7.7",
12 12
                 "laravel-vite-plugin": "^1.0",
13
-                "postcss": "^8.4.47",
14
-                "postcss-nesting": "^13.0.0",
15
-                "tailwindcss": "^3.4.13",
13
+                "postcss": "^8.4.49",
14
+                "postcss-nesting": "^13.0.1",
15
+                "tailwindcss": "^3.4.15",
16 16
                 "vite": "^5.4"
17 17
             }
18 18
         },

+ 3
- 3
package.json Dosyayı Görüntüle

@@ -11,9 +11,9 @@
11 11
         "autoprefixer": "^10.4.20",
12 12
         "axios": "^1.7.7",
13 13
         "laravel-vite-plugin": "^1.0",
14
-        "postcss": "^8.4.47",
15
-        "postcss-nesting": "^13.0.0",
16
-        "tailwindcss": "^3.4.13",
14
+        "postcss": "^8.4.49",
15
+        "postcss-nesting": "^13.0.1",
16
+        "tailwindcss": "^3.4.15",
17 17
         "vite": "^5.4"
18 18
     }
19 19
 }

+ 2
- 5
resources/css/filament/company/tailwind.config.js Dosyayı Görüntüle

@@ -4,16 +4,13 @@ export default {
4 4
     presets: [preset],
5 5
     content: [
6 6
         './app/Filament/Company/**/*.php',
7
-        './resources/views/filament/company/**/*.blade.php',
8
-        './resources/views/filament/forms/**/*.blade.php',
9
-        './resources/views/filament/infolists/**/*.blade.php',
7
+        './resources/views/filament/**/*.blade.php',
10 8
         './resources/views/livewire/company/**/*.blade.php',
11 9
         './resources/views/components/**/*.blade.php',
10
+        './resources/views/vendor/**/*.blade.php',
12 11
         './vendor/filament/**/*.blade.php',
13 12
         './vendor/andrewdwallo/filament-companies/resources/views/**/*.blade.php',
14 13
         './vendor/andrewdwallo/filament-selectify/resources/views/**/*.blade.php',
15
-        './resources/views/vendor/**/*.blade.php',
16
-        './vendor/bezhansalleh/filament-panel-switch/resources/views/panel-switch-menu.blade.php',
17 14
         './vendor/awcodes/filament-table-repeater/resources/**/*.blade.php',
18 15
         './vendor/jaocero/radio-deck/resources/views/**/*.blade.php',
19 16
     ],

+ 3
- 3
resources/css/filament/company/theme.css Dosyayı Görüntüle

@@ -1,8 +1,8 @@
1
-@import '../../../../vendor/filament/filament/resources/css/theme.css';
1
+@import '/vendor/filament/filament/resources/css/theme.css';
2 2
 @import 'tooltip.css';
3
-@import '../../../../vendor/awcodes/filament-table-repeater/resources/css/plugin.css';
3
+@import '/vendor/awcodes/filament-table-repeater/resources/css/plugin.css';
4 4
 
5
-@config './tailwind.config.js';
5
+@config 'tailwind.config.js';
6 6
 
7 7
 .fi-fo-repeater.uncontained .fi-fo-repeater-item {
8 8
     @apply divide-y-0 rounded-none bg-none shadow-none ring-0 ring-gray-950/5 dark:divide-white/10 dark:bg-white/5 dark:ring-white/10;

+ 0
- 89
resources/views/filament/infolists/invoice-create-step.blade.php Dosyayı Görüntüle

@@ -1,89 +0,0 @@
1
-@use('App\Utilities\Currency\CurrencyConverter')
2
-
3
-<div class="space-y-1">
4
-    <!-- Create Section -->
5
-    <x-filament::section>
6
-        <div class="flex justify-between items-start">
7
-            <!-- Left section -->
8
-            <div class="flex items-center space-x-3">
9
-                <!-- Icon -->
10
-                <x-filament::icon
11
-                    icon="heroicon-o-document-text"
12
-                    class="h-8 w-8 text-primary-600 dark:text-primary-500 flex-shrink-0 mr-4"
13
-                />
14
-                <!-- Text content -->
15
-                <div>
16
-                    <h1 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Create</h1>
17
-                    <p class="text-sm text-gray-600 dark:text-gray-400">Created: {{ $getRecord()->created_at->diffForHumans() }}</p>
18
-                </div>
19
-            </div>
20
-
21
-            <!-- Right section -->
22
-            <div class="flex flex-row items-center space-x-2">
23
-                @if($getRecord()->status->value === 'draft')
24
-                    {{ $getAction('approveDraft') }}
25
-                @endif
26
-                {{ $getAction('edit') }}
27
-            </div>
28
-        </div>
29
-    </x-filament::section>
30
-
31
-    <div class="border-l-4 h-8 border-solid border-gray-300 dark:border-gray-700 ms-8"></div>
32
-
33
-    <!-- Send Section -->
34
-    <x-filament::section :class="$getRecord()->status->value === 'draft' ? 'opacity-50 pointer-events-none' : ''">
35
-        <div class="flex justify-between items-start">
36
-            <!-- Left section -->
37
-            <div class="flex items-center space-x-3">
38
-                <!-- Icon -->
39
-                <x-filament::icon
40
-                    icon="heroicon-o-paper-airplane"
41
-                    class="h-8 w-8 text-primary-600 dark:text-primary-500 flex-shrink-0 mr-4"
42
-                />
43
-                <!-- Text content -->
44
-                <div>
45
-                    <h1 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Send</h1>
46
-                    <p class="text-sm text-gray-600 dark:text-gray-400">Last Sent: just a moment ago</p>
47
-                </div>
48
-            </div>
49
-
50
-            <!-- Right section -->
51
-            @if($getRecord()->status->value !== 'draft')
52
-                <div class="flex flex-row items-center space-x-2">
53
-                    @if($getRecord()->status->value !== 'sent')
54
-                        {{ $getAction('markAsSent') }}
55
-                    @endif
56
-                    {{ $getAction('sendInvoice') }}
57
-                </div>
58
-            @endif
59
-        </div>
60
-    </x-filament::section>
61
-
62
-    <div class="border-l-4 h-8 border-solid border-gray-300 dark:border-gray-700 ms-8"></div>
63
-
64
-    <!-- Manage Payments Section -->
65
-    <x-filament::section :class="$getRecord()->status->value === 'draft' ? 'opacity-50 pointer-events-none' : ''">
66
-        <div class="flex justify-between items-start">
67
-            <!-- Left section -->
68
-            <div class="flex items-center space-x-3">
69
-                <!-- Icon -->
70
-                <x-filament::icon
71
-                    icon="heroicon-o-credit-card"
72
-                    class="h-8 w-8 text-primary-600 dark:text-primary-500 flex-shrink-0 mr-4"
73
-                />
74
-                <!-- Text content -->
75
-                <div>
76
-                    <h1 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Manage Payments</h1>
77
-                    <p class="text-sm text-gray-600 dark:text-gray-400">Amount Due: {{ CurrencyConverter::formatToMoney($getRecord()->amount_due) }}</p>
78
-                </div>
79
-            </div>
80
-
81
-            <!-- Right section -->
82
-            @if($getRecord()->status->value !== 'draft')
83
-                <div class="flex flex-row items-center space-x-2">
84
-                    {{ $getAction('recordPayment') }}
85
-                </div>
86
-            @endif
87
-        </div>
88
-    </x-filament::section>
89
-</div>

Loading…
İptal
Kaydet