Andrew Wallo 10 months ago
parent
commit
f4923a17be
21 changed files with 390 additions and 354 deletions
  1. 1
    1
      app/DTO/AccountTransactionDTO.php
  2. 0
    2
      app/Enums/Accounting/TransactionType.php
  3. 7
    13
      app/Filament/Company/Pages/Reports/AccountTransactions.php
  4. 12
    23
      app/Filament/Company/Resources/Purchases/BillResource.php
  5. 78
    0
      app/Filament/Company/Resources/Purchases/BillResource/Pages/ViewBill.php
  6. 182
    0
      app/Filament/Company/Resources/Purchases/BillResource/RelationManagers/PaymentsRelationManager.php
  7. 0
    50
      app/Filament/Company/Resources/Purchases/BuyableOfferingResource.php
  8. 0
    21
      app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/CreateBuyableOffering.php
  9. 0
    29
      app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/EditBuyableOffering.php
  10. 0
    25
      app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/ListBuyableOfferings.php
  11. 1
    18
      app/Filament/Company/Resources/Sales/InvoiceResource.php
  12. 2
    19
      app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php
  13. 0
    50
      app/Filament/Company/Resources/Sales/SellableOfferingResource.php
  14. 0
    21
      app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/CreateSellableOffering.php
  15. 0
    29
      app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/EditSellableOffering.php
  16. 0
    25
      app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/ListSellableOfferings.php
  17. 2
    2
      app/Providers/FilamentCompaniesServiceProvider.php
  18. 44
    0
      app/Providers/MacroServiceProvider.php
  19. 3
    10
      app/Services/AccountService.php
  20. 21
    3
      app/Services/ReportService.php
  21. 37
    13
      resources/views/components/company/tables/reports/account-transactions.blade.php

+ 1
- 1
app/DTO/AccountTransactionDTO.php View File

14
         public string $credit,
14
         public string $credit,
15
         public string $balance,
15
         public string $balance,
16
         public ?TransactionType $type,
16
         public ?TransactionType $type,
17
-        public ?string $tableAction,
17
+        public ?array $tableAction,
18
     ) {}
18
     ) {}
19
 }
19
 }

+ 0
- 2
app/Enums/Accounting/TransactionType.php View File

14
     case Journal = 'journal';
14
     case Journal = 'journal';
15
     case Transfer = 'transfer';
15
     case Transfer = 'transfer';
16
 
16
 
17
-    case Approval = 'approval';
18
-
19
     public function getLabel(): ?string
17
     public function getLabel(): ?string
20
     {
18
     {
21
         return $this->name;
19
         return $this->name;

+ 7
- 13
app/Filament/Company/Pages/Reports/AccountTransactions.php View File

17
 use Filament\Forms\Components\Select;
17
 use Filament\Forms\Components\Select;
18
 use Filament\Forms\Form;
18
 use Filament\Forms\Form;
19
 use Filament\Support\Enums\Alignment;
19
 use Filament\Support\Enums\Alignment;
20
+use Filament\Support\Enums\MaxWidth;
20
 use Filament\Tables\Actions\Action;
21
 use Filament\Tables\Actions\Action;
21
 use Guava\FilamentClusters\Forms\Cluster;
22
 use Guava\FilamentClusters\Forms\Cluster;
22
 use Illuminate\Contracts\Support\Htmlable;
23
 use Illuminate\Contracts\Support\Htmlable;
38
         $this->exportService = $exportService;
39
         $this->exportService = $exportService;
39
     }
40
     }
40
 
41
 
42
+    public function getMaxContentWidth(): MaxWidth | string | null
43
+    {
44
+        return 'max-w-8xl';
45
+    }
46
+
41
     protected function initializeDefaultFilters(): void
47
     protected function initializeDefaultFilters(): void
42
     {
48
     {
43
         if (empty($this->getFilterState('selectedAccount'))) {
49
         if (empty($this->getFilterState('selectedAccount'))) {
44
             $this->setFilterState('selectedAccount', 'all');
50
             $this->setFilterState('selectedAccount', 'all');
45
         }
51
         }
46
 
52
 
47
-        if (empty($this->getFilterState('basis'))) {
48
-            $this->setFilterState('basis', 'accrual');
49
-        }
50
-
51
         if (empty($this->getFilterState('selectedEntity'))) {
53
         if (empty($this->getFilterState('selectedEntity'))) {
52
             $this->setFilterState('selectedEntity', 'all');
54
             $this->setFilterState('selectedEntity', 'all');
53
         }
55
         }
81
     public function filtersForm(Form $form): Form
83
     public function filtersForm(Form $form): Form
82
     {
84
     {
83
         return $form
85
         return $form
84
-            ->columns(3)
86
+            ->columns(5)
85
             ->schema([
87
             ->schema([
86
                 Select::make('selectedAccount')
88
                 Select::make('selectedAccount')
87
                     ->label('Account')
89
                     ->label('Account')
95
                 ])->extraFieldWrapperAttributes([
97
                 ])->extraFieldWrapperAttributes([
96
                     'class' => 'report-hidden-label',
98
                     'class' => 'report-hidden-label',
97
                 ]),
99
                 ]),
98
-                Select::make('basis')
99
-                    ->label('Accounting Basis')
100
-                    ->options([
101
-                        'accrual' => 'Accrual (Paid & Unpaid)',
102
-                        'cash' => 'Cash Basis (Paid)',
103
-                    ])
104
-                    ->selectablePlaceholder(false),
105
                 Select::make('selectedEntity')
100
                 Select::make('selectedEntity')
106
                     ->label('Entity')
101
                     ->label('Entity')
107
                     ->options($this->getEntityOptions())
102
                     ->options($this->getEntityOptions())
162
             endDate: $this->getFormattedEndDate(),
157
             endDate: $this->getFormattedEndDate(),
163
             columns: $columns,
158
             columns: $columns,
164
             accountId: $this->getFilterState('selectedAccount'),
159
             accountId: $this->getFilterState('selectedAccount'),
165
-            basis: $this->getFilterState('basis'),
166
             entityId: $this->getFilterState('selectedEntity'),
160
             entityId: $this->getFilterState('selectedEntity'),
167
         );
161
         );
168
     }
162
     }

+ 12
- 23
app/Filament/Company/Resources/Purchases/BillResource.php View File

15
 use App\Utilities\Currency\CurrencyConverter;
15
 use App\Utilities\Currency\CurrencyConverter;
16
 use Awcodes\TableRepeater\Components\TableRepeater;
16
 use Awcodes\TableRepeater\Components\TableRepeater;
17
 use Awcodes\TableRepeater\Header;
17
 use Awcodes\TableRepeater\Header;
18
-use Carbon\CarbonInterface;
19
 use Closure;
18
 use Closure;
20
 use Filament\Forms;
19
 use Filament\Forms;
21
 use Filament\Forms\Form;
20
 use Filament\Forms\Form;
28
 use Illuminate\Database\Eloquent\Builder;
27
 use Illuminate\Database\Eloquent\Builder;
29
 use Illuminate\Database\Eloquent\Collection;
28
 use Illuminate\Database\Eloquent\Collection;
30
 use Illuminate\Database\Eloquent\Model;
29
 use Illuminate\Database\Eloquent\Model;
31
-use Illuminate\Support\Carbon;
32
 use Illuminate\Support\Facades\Auth;
30
 use Illuminate\Support\Facades\Auth;
33
 
31
 
34
 class BillResource extends Resource
32
 class BillResource extends Resource
275
     public static function table(Table $table): Table
273
     public static function table(Table $table): Table
276
     {
274
     {
277
         return $table
275
         return $table
276
+            ->defaultSort('due_date')
278
             ->columns([
277
             ->columns([
279
                 Tables\Columns\TextColumn::make('status')
278
                 Tables\Columns\TextColumn::make('status')
280
                     ->badge()
279
                     ->badge()
281
                     ->searchable(),
280
                     ->searchable(),
282
                 Tables\Columns\TextColumn::make('due_date')
281
                 Tables\Columns\TextColumn::make('due_date')
283
                     ->label('Due')
282
                     ->label('Due')
284
-                    ->formatStateUsing(function (Tables\Columns\TextColumn $column, mixed $state) {
285
-                        if (blank($state)) {
286
-                            return null;
287
-                        }
288
-
289
-                        $date = Carbon::parse($state)
290
-                            ->setTimezone($timezone ?? $column->getTimezone());
291
-
292
-                        if ($date->isToday()) {
293
-                            return 'Today';
294
-                        }
295
-
296
-                        return $date->diffForHumans([
297
-                            'options' => CarbonInterface::ONE_DAY_WORDS,
298
-                        ]);
299
-                    })
283
+                    ->asRelativeDay()
300
                     ->sortable(),
284
                     ->sortable(),
301
                 Tables\Columns\TextColumn::make('date')
285
                 Tables\Columns\TextColumn::make('date')
302
                     ->date()
286
                     ->date()
303
                     ->sortable(),
287
                     ->sortable(),
304
                 Tables\Columns\TextColumn::make('bill_number')
288
                 Tables\Columns\TextColumn::make('bill_number')
305
                     ->label('Number')
289
                     ->label('Number')
306
-                    ->searchable(),
290
+                    ->searchable()
291
+                    ->sortable(),
307
                 Tables\Columns\TextColumn::make('vendor.name')
292
                 Tables\Columns\TextColumn::make('vendor.name')
308
                     ->sortable(),
293
                     ->sortable(),
309
                 Tables\Columns\TextColumn::make('total')
294
                 Tables\Columns\TextColumn::make('total')
310
-                    ->currency(),
295
+                    ->currency()
296
+                    ->sortable(),
311
                 Tables\Columns\TextColumn::make('amount_paid')
297
                 Tables\Columns\TextColumn::make('amount_paid')
312
                     ->label('Amount Paid')
298
                     ->label('Amount Paid')
313
-                    ->currency(),
299
+                    ->currency()
300
+                    ->sortable(),
314
                 Tables\Columns\TextColumn::make('amount_due')
301
                 Tables\Columns\TextColumn::make('amount_due')
315
                     ->label('Amount Due')
302
                     ->label('Amount Due')
316
-                    ->currency(),
303
+                    ->currency()
304
+                    ->sortable(),
317
             ])
305
             ])
318
             ->filters([
306
             ->filters([
319
                 Tables\Filters\SelectFilter::make('vendor')
307
                 Tables\Filters\SelectFilter::make('vendor')
562
     public static function getRelations(): array
550
     public static function getRelations(): array
563
     {
551
     {
564
         return [
552
         return [
565
-            //
553
+            BillResource\RelationManagers\PaymentsRelationManager::class,
566
         ];
554
         ];
567
     }
555
     }
568
 
556
 
571
         return [
559
         return [
572
             'index' => Pages\ListBills::route('/'),
560
             'index' => Pages\ListBills::route('/'),
573
             'create' => Pages\CreateBill::route('/create'),
561
             'create' => Pages\CreateBill::route('/create'),
562
+            'view' => Pages\ViewBill::route('/{record}'),
574
             'edit' => Pages\EditBill::route('/{record}/edit'),
563
             'edit' => Pages\EditBill::route('/{record}/edit'),
575
         ];
564
         ];
576
     }
565
     }

+ 78
- 0
app/Filament/Company/Resources/Purchases/BillResource/Pages/ViewBill.php View File

1
+<?php
2
+
3
+namespace App\Filament\Company\Resources\Purchases\BillResource\Pages;
4
+
5
+use App\Filament\Company\Resources\Purchases\BillResource;
6
+use App\Filament\Company\Resources\Purchases\VendorResource;
7
+use App\Models\Accounting\Bill;
8
+use Filament\Actions;
9
+use Filament\Infolists\Components\Section;
10
+use Filament\Infolists\Components\TextEntry;
11
+use Filament\Infolists\Infolist;
12
+use Filament\Resources\Pages\ViewRecord;
13
+use Filament\Support\Enums\FontWeight;
14
+use Filament\Support\Enums\IconPosition;
15
+use Filament\Support\Enums\IconSize;
16
+
17
+class ViewBill extends ViewRecord
18
+{
19
+    protected static string $resource = BillResource::class;
20
+
21
+    protected $listeners = [
22
+        'refresh' => '$refresh',
23
+    ];
24
+
25
+    protected function getHeaderActions(): array
26
+    {
27
+        return [
28
+            Actions\ActionGroup::make([
29
+                Actions\EditAction::make(),
30
+                Actions\DeleteAction::make(),
31
+                Bill::getReplicateAction(),
32
+            ])
33
+                ->label('Actions')
34
+                ->button()
35
+                ->outlined()
36
+                ->dropdownPlacement('bottom-end')
37
+                ->icon('heroicon-c-chevron-down')
38
+                ->iconSize(IconSize::Small)
39
+                ->iconPosition(IconPosition::After),
40
+        ];
41
+    }
42
+
43
+    public function infolist(Infolist $infolist): Infolist
44
+    {
45
+        return $infolist
46
+            ->schema([
47
+                Section::make('Bill Details')
48
+                    ->columns(4)
49
+                    ->schema([
50
+                        TextEntry::make('bill_number')
51
+                            ->label('Invoice #'),
52
+                        TextEntry::make('status')
53
+                            ->badge(),
54
+                        TextEntry::make('vendor.name')
55
+                            ->label('Vendor')
56
+                            ->color('primary')
57
+                            ->weight(FontWeight::SemiBold)
58
+                            ->url(static fn (Bill $record) => VendorResource::getUrl('edit', ['record' => $record->vendor_id])),
59
+                        TextEntry::make('total')
60
+                            ->label('Total')
61
+                            ->money(),
62
+                        TextEntry::make('amount_due')
63
+                            ->label('Amount Due')
64
+                            ->money(),
65
+                        TextEntry::make('date')
66
+                            ->label('Date')
67
+                            ->date(),
68
+                        TextEntry::make('due_date')
69
+                            ->label('Due')
70
+                            ->asRelativeDay(),
71
+                        TextEntry::make('paid_at')
72
+                            ->label('Paid At')
73
+                            ->placeholder('Not Paid')
74
+                            ->date(),
75
+                    ]),
76
+            ]);
77
+    }
78
+}

+ 182
- 0
app/Filament/Company/Resources/Purchases/BillResource/RelationManagers/PaymentsRelationManager.php View File

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

+ 0
- 50
app/Filament/Company/Resources/Purchases/BuyableOfferingResource.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Purchases;
4
-
5
-use App\Filament\Company\Resources\Common\OfferingResource;
6
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource\Pages;
7
-use App\Models\Common\Offering;
8
-use Filament\Forms\Form;
9
-use Filament\Resources\Resource;
10
-use Filament\Tables\Table;
11
-use Illuminate\Database\Eloquent\Builder;
12
-
13
-class BuyableOfferingResource extends Resource
14
-{
15
-    protected static ?string $model = Offering::class;
16
-
17
-    protected static ?string $pluralModelLabel = 'Products & Services';
18
-
19
-    public static function getEloquentQuery(): Builder
20
-    {
21
-        return parent::getEloquentQuery()
22
-            ->whereNotNull('expense_account_id');
23
-    }
24
-
25
-    public static function form(Form $form): Form
26
-    {
27
-        return OfferingResource::form($form);
28
-    }
29
-
30
-    public static function table(Table $table): Table
31
-    {
32
-        return OfferingResource::table($table);
33
-    }
34
-
35
-    public static function getRelations(): array
36
-    {
37
-        return [
38
-            //
39
-        ];
40
-    }
41
-
42
-    public static function getPages(): array
43
-    {
44
-        return [
45
-            'index' => Pages\ListBuyableOfferings::route('/'),
46
-            'create' => Pages\CreateBuyableOffering::route('/create'),
47
-            'edit' => Pages\EditBuyableOffering::route('/{record}/edit'),
48
-        ];
49
-    }
50
-}

+ 0
- 21
app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/CreateBuyableOffering.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Purchases\BuyableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource;
6
-use App\Filament\Company\Resources\Sales\SellableOfferingResource;
7
-use Filament\Resources\Pages\CreateRecord;
8
-
9
-class CreateBuyableOffering extends CreateRecord
10
-{
11
-    protected static string $resource = BuyableOfferingResource::class;
12
-
13
-    protected function getRedirectUrl(): string
14
-    {
15
-        if ($this->record->income_account_id && ! $this->record->expense_account_id) {
16
-            return SellableOfferingResource::getUrl();
17
-        } else {
18
-            return $this->getResource()::getUrl('index');
19
-        }
20
-    }
21
-}

+ 0
- 29
app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/EditBuyableOffering.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Purchases\BuyableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource;
6
-use App\Filament\Company\Resources\Sales\SellableOfferingResource;
7
-use Filament\Actions;
8
-use Filament\Resources\Pages\EditRecord;
9
-
10
-class EditBuyableOffering extends EditRecord
11
-{
12
-    protected static string $resource = BuyableOfferingResource::class;
13
-
14
-    protected function getHeaderActions(): array
15
-    {
16
-        return [
17
-            Actions\DeleteAction::make(),
18
-        ];
19
-    }
20
-
21
-    protected function getRedirectUrl(): string
22
-    {
23
-        if ($this->record->income_account_id && ! $this->record->expense_account_id) {
24
-            return SellableOfferingResource::getUrl();
25
-        } else {
26
-            return $this->getResource()::getUrl('index');
27
-        }
28
-    }
29
-}

+ 0
- 25
app/Filament/Company/Resources/Purchases/BuyableOfferingResource/Pages/ListBuyableOfferings.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Purchases\BuyableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource;
6
-use Filament\Actions;
7
-use Filament\Resources\Pages\ListRecords;
8
-use Illuminate\Contracts\Support\Htmlable;
9
-
10
-class ListBuyableOfferings extends ListRecords
11
-{
12
-    protected static string $resource = BuyableOfferingResource::class;
13
-
14
-    protected function getHeaderActions(): array
15
-    {
16
-        return [
17
-            Actions\CreateAction::make(),
18
-        ];
19
-    }
20
-
21
-    public function getHeading(): string | Htmlable
22
-    {
23
-        return 'Products & Services (Purchases)';
24
-    }
25
-}

+ 1
- 18
app/Filament/Company/Resources/Sales/InvoiceResource.php View File

18
 use App\Utilities\Currency\CurrencyConverter;
18
 use App\Utilities\Currency\CurrencyConverter;
19
 use Awcodes\TableRepeater\Components\TableRepeater;
19
 use Awcodes\TableRepeater\Components\TableRepeater;
20
 use Awcodes\TableRepeater\Header;
20
 use Awcodes\TableRepeater\Header;
21
-use Carbon\CarbonInterface;
22
 use Closure;
21
 use Closure;
23
 use Filament\Forms;
22
 use Filament\Forms;
24
 use Filament\Forms\Components\FileUpload;
23
 use Filament\Forms\Components\FileUpload;
32
 use Illuminate\Database\Eloquent\Builder;
31
 use Illuminate\Database\Eloquent\Builder;
33
 use Illuminate\Database\Eloquent\Collection;
32
 use Illuminate\Database\Eloquent\Collection;
34
 use Illuminate\Database\Eloquent\Model;
33
 use Illuminate\Database\Eloquent\Model;
35
-use Illuminate\Support\Carbon;
36
 use Illuminate\Support\Facades\Auth;
34
 use Illuminate\Support\Facades\Auth;
37
 use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
35
 use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
38
 
36
 
346
                     ->searchable(),
344
                     ->searchable(),
347
                 Tables\Columns\TextColumn::make('due_date')
345
                 Tables\Columns\TextColumn::make('due_date')
348
                     ->label('Due')
346
                     ->label('Due')
349
-                    ->formatStateUsing(function (Tables\Columns\TextColumn $column, mixed $state) {
350
-                        if (blank($state)) {
351
-                            return null;
352
-                        }
353
-
354
-                        $date = Carbon::parse($state)
355
-                            ->setTimezone($timezone ?? $column->getTimezone());
356
-
357
-                        if ($date->isToday()) {
358
-                            return 'Today';
359
-                        }
360
-
361
-                        return $date->diffForHumans([
362
-                            'options' => CarbonInterface::ONE_DAY_WORDS,
363
-                        ]);
364
-                    })
347
+                    ->asRelativeDay()
365
                     ->sortable(),
348
                     ->sortable(),
366
                 Tables\Columns\TextColumn::make('date')
349
                 Tables\Columns\TextColumn::make('date')
367
                     ->date()
350
                     ->date()

+ 2
- 19
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php View File

5
 use App\Filament\Company\Resources\Sales\ClientResource;
5
 use App\Filament\Company\Resources\Sales\ClientResource;
6
 use App\Filament\Company\Resources\Sales\InvoiceResource;
6
 use App\Filament\Company\Resources\Sales\InvoiceResource;
7
 use App\Models\Accounting\Invoice;
7
 use App\Models\Accounting\Invoice;
8
-use Carbon\CarbonInterface;
9
 use Filament\Actions;
8
 use Filament\Actions;
10
 use Filament\Infolists\Components\Section;
9
 use Filament\Infolists\Components\Section;
11
 use Filament\Infolists\Components\TextEntry;
10
 use Filament\Infolists\Components\TextEntry;
14
 use Filament\Support\Enums\FontWeight;
13
 use Filament\Support\Enums\FontWeight;
15
 use Filament\Support\Enums\IconPosition;
14
 use Filament\Support\Enums\IconPosition;
16
 use Filament\Support\Enums\IconSize;
15
 use Filament\Support\Enums\IconSize;
17
-use Illuminate\Support\Carbon;
18
 
16
 
19
 class ViewInvoice extends ViewRecord
17
 class ViewInvoice extends ViewRecord
20
 {
18
 {
59
                             ->label('Client')
57
                             ->label('Client')
60
                             ->color('primary')
58
                             ->color('primary')
61
                             ->weight(FontWeight::SemiBold)
59
                             ->weight(FontWeight::SemiBold)
62
-                            ->url(fn (Invoice $record) => ClientResource::getUrl('edit', ['record' => $record->client_id])),
60
+                            ->url(static fn (Invoice $record) => ClientResource::getUrl('edit', ['record' => $record->client_id])),
63
                         TextEntry::make('total')
61
                         TextEntry::make('total')
64
                             ->label('Total')
62
                             ->label('Total')
65
                             ->money(),
63
                             ->money(),
71
                             ->date(),
69
                             ->date(),
72
                         TextEntry::make('due_date')
70
                         TextEntry::make('due_date')
73
                             ->label('Due')
71
                             ->label('Due')
74
-                            ->formatStateUsing(function (TextEntry $entry, mixed $state) {
75
-                                if (blank($state)) {
76
-                                    return null;
77
-                                }
78
-
79
-                                $date = Carbon::parse($state)
80
-                                    ->setTimezone($timezone ?? $entry->getTimezone());
81
-
82
-                                if ($date->isToday()) {
83
-                                    return 'Today';
84
-                                }
85
-
86
-                                return $date->diffForHumans([
87
-                                    'options' => CarbonInterface::ONE_DAY_WORDS,
88
-                                ]);
89
-                            }),
72
+                            ->asRelativeDay(),
90
                         TextEntry::make('approved_at')
73
                         TextEntry::make('approved_at')
91
                             ->label('Approved At')
74
                             ->label('Approved At')
92
                             ->placeholder('Not Approved')
75
                             ->placeholder('Not Approved')

+ 0
- 50
app/Filament/Company/Resources/Sales/SellableOfferingResource.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Sales;
4
-
5
-use App\Filament\Company\Resources\Common\OfferingResource;
6
-use App\Filament\Company\Resources\Sales\SellableOfferingResource\Pages;
7
-use App\Models\Common\Offering;
8
-use Filament\Forms\Form;
9
-use Filament\Resources\Resource;
10
-use Filament\Tables\Table;
11
-use Illuminate\Database\Eloquent\Builder;
12
-
13
-class SellableOfferingResource extends Resource
14
-{
15
-    protected static ?string $model = Offering::class;
16
-
17
-    protected static ?string $pluralModelLabel = 'Products & Services';
18
-
19
-    public static function getEloquentQuery(): Builder
20
-    {
21
-        return parent::getEloquentQuery()
22
-            ->whereNotNull('income_account_id');
23
-    }
24
-
25
-    public static function form(Form $form): Form
26
-    {
27
-        return OfferingResource::form($form);
28
-    }
29
-
30
-    public static function table(Table $table): Table
31
-    {
32
-        return OfferingResource::table($table);
33
-    }
34
-
35
-    public static function getRelations(): array
36
-    {
37
-        return [
38
-            //
39
-        ];
40
-    }
41
-
42
-    public static function getPages(): array
43
-    {
44
-        return [
45
-            'index' => Pages\ListSellableOfferings::route('/'),
46
-            'create' => Pages\CreateSellableOffering::route('/create'),
47
-            'edit' => Pages\EditSellableOffering::route('/{record}/edit'),
48
-        ];
49
-    }
50
-}

+ 0
- 21
app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/CreateSellableOffering.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Sales\SellableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource;
6
-use App\Filament\Company\Resources\Sales\SellableOfferingResource;
7
-use Filament\Resources\Pages\CreateRecord;
8
-
9
-class CreateSellableOffering extends CreateRecord
10
-{
11
-    protected static string $resource = SellableOfferingResource::class;
12
-
13
-    protected function getRedirectUrl(): string
14
-    {
15
-        if ($this->record->expense_account_id && ! $this->record->income_account_id) {
16
-            return BuyableOfferingResource::getUrl();
17
-        } else {
18
-            return $this->getResource()::getUrl('index');
19
-        }
20
-    }
21
-}

+ 0
- 29
app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/EditSellableOffering.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Sales\SellableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Purchases\BuyableOfferingResource;
6
-use App\Filament\Company\Resources\Sales\SellableOfferingResource;
7
-use Filament\Actions;
8
-use Filament\Resources\Pages\EditRecord;
9
-
10
-class EditSellableOffering extends EditRecord
11
-{
12
-    protected static string $resource = SellableOfferingResource::class;
13
-
14
-    protected function getHeaderActions(): array
15
-    {
16
-        return [
17
-            Actions\DeleteAction::make(),
18
-        ];
19
-    }
20
-
21
-    protected function getRedirectUrl(): string
22
-    {
23
-        if ($this->record->expense_account_id && ! $this->record->income_account_id) {
24
-            return BuyableOfferingResource::getUrl();
25
-        } else {
26
-            return $this->getResource()::getUrl('index');
27
-        }
28
-    }
29
-}

+ 0
- 25
app/Filament/Company/Resources/Sales/SellableOfferingResource/Pages/ListSellableOfferings.php View File

1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Sales\SellableOfferingResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Sales\SellableOfferingResource;
6
-use Filament\Actions;
7
-use Filament\Resources\Pages\ListRecords;
8
-use Illuminate\Contracts\Support\Htmlable;
9
-
10
-class ListSellableOfferings extends ListRecords
11
-{
12
-    protected static string $resource = SellableOfferingResource::class;
13
-
14
-    protected function getHeaderActions(): array
15
-    {
16
-        return [
17
-            Actions\CreateAction::make()->label('New Product or Service'),
18
-        ];
19
-    }
20
-
21
-    public function getHeading(): string | Htmlable
22
-    {
23
-        return 'Products & Services (Sales)';
24
-    }
25
-}

+ 2
- 2
app/Providers/FilamentCompaniesServiceProvider.php View File

126
                         ...OfferingResource::getNavigationItems(),
126
                         ...OfferingResource::getNavigationItems(),
127
                     ])
127
                     ])
128
                     ->groups([
128
                     ->groups([
129
-                        NavigationGroup::make('Sales & Payments')
130
-                            ->label('Sales & Payments')
129
+                        NavigationGroup::make('Sales')
130
+                            ->label('Sales')
131
                             ->icon('heroicon-o-currency-dollar')
131
                             ->icon('heroicon-o-currency-dollar')
132
                             ->items([
132
                             ->items([
133
                                 ...InvoiceResource::getNavigationItems(),
133
                                 ...InvoiceResource::getNavigationItems(),

+ 44
- 0
app/Providers/MacroServiceProvider.php View File

10
 use App\Utilities\Accounting\AccountCode;
10
 use App\Utilities\Accounting\AccountCode;
11
 use App\Utilities\Currency\CurrencyAccessor;
11
 use App\Utilities\Currency\CurrencyAccessor;
12
 use BackedEnum;
12
 use BackedEnum;
13
+use Carbon\CarbonInterface;
13
 use Closure;
14
 use Closure;
14
 use Filament\Forms\Components\DatePicker;
15
 use Filament\Forms\Components\DatePicker;
15
 use Filament\Forms\Components\Field;
16
 use Filament\Forms\Components\Field;
16
 use Filament\Forms\Components\TextInput;
17
 use Filament\Forms\Components\TextInput;
18
+use Filament\Infolists\Components\TextEntry;
17
 use Filament\Tables\Columns\TextColumn;
19
 use Filament\Tables\Columns\TextColumn;
18
 use Illuminate\Support\Carbon;
20
 use Illuminate\Support\Carbon;
19
 use Illuminate\Support\ServiceProvider;
21
 use Illuminate\Support\ServiceProvider;
177
             return $this;
179
             return $this;
178
         });
180
         });
179
 
181
 
182
+        TextColumn::macro('asRelativeDay', function (?string $timezone = null): static {
183
+            $this->formatStateUsing(function (TextColumn $column, mixed $state) use ($timezone) {
184
+                if (blank($state)) {
185
+                    return null;
186
+                }
187
+
188
+                $date = Carbon::parse($state)
189
+                    ->setTimezone($timezone ?? $column->getTimezone());
190
+
191
+                if ($date->isToday()) {
192
+                    return 'Today';
193
+                }
194
+
195
+                return $date->diffForHumans([
196
+                    'options' => CarbonInterface::ONE_DAY_WORDS,
197
+                ]);
198
+            });
199
+
200
+            return $this;
201
+        });
202
+
203
+        TextEntry::macro('asRelativeDay', function (?string $timezone = null): static {
204
+            $this->formatStateUsing(function (TextEntry $entry, mixed $state) use ($timezone) {
205
+                if (blank($state)) {
206
+                    return null;
207
+                }
208
+
209
+                $date = Carbon::parse($state)
210
+                    ->setTimezone($timezone ?? $entry->getTimezone());
211
+
212
+                if ($date->isToday()) {
213
+                    return 'Today';
214
+                }
215
+
216
+                return $date->diffForHumans([
217
+                    'options' => CarbonInterface::ONE_DAY_WORDS,
218
+                ]);
219
+            });
220
+
221
+            return $this;
222
+        });
223
+
180
         Money::macro('swapAmountFor', function ($newCurrency) {
224
         Money::macro('swapAmountFor', function ($newCurrency) {
181
             $oldCurrency = $this->currency->getCurrency();
225
             $oldCurrency = $this->currency->getCurrency();
182
             $balanceInSubunits = $this->getAmount();
226
             $balanceInSubunits = $this->getAmount();

+ 3
- 10
app/Services/AccountService.php View File

113
         return array_filter($balances, static fn ($value) => $value !== null);
113
         return array_filter($balances, static fn ($value) => $value !== null);
114
     }
114
     }
115
 
115
 
116
-    public function getTransactionDetailsSubquery(string $startDate, string $endDate, string $basis = 'accrual', ?string $entityId = null): Closure
116
+    public function getTransactionDetailsSubquery(string $startDate, string $endDate, ?string $entityId = null): Closure
117
     {
117
     {
118
-        return static function ($query) use ($startDate, $endDate, $basis, $entityId) {
118
+        return static function ($query) use ($startDate, $endDate, $entityId) {
119
             $query->select(
119
             $query->select(
120
                 'journal_entries.id',
120
                 'journal_entries.id',
121
                 'journal_entries.account_id',
121
                 'journal_entries.account_id',
128
                 ->whereBetween('transactions.posted_at', [$startDate, $endDate])
128
                 ->whereBetween('transactions.posted_at', [$startDate, $endDate])
129
                 ->join('transactions', 'transactions.id', '=', 'journal_entries.transaction_id')
129
                 ->join('transactions', 'transactions.id', '=', 'journal_entries.transaction_id')
130
                 ->orderBy('transactions.posted_at')
130
                 ->orderBy('transactions.posted_at')
131
-                ->with('transaction:id,type,description,posted_at');
132
-
133
-            if ($basis === 'cash') {
134
-                $query->where(function ($query) {
135
-                    $query->whereNull('transactions.transactionable_id')
136
-                        ->orWhere('transactions.is_payment', true);
137
-                });
138
-            }
131
+                ->with('transaction:id,type,description,posted_at,is_payment,transactionable_id,transactionable_type');
139
 
132
 
140
             if ($entityId) {
133
             if ($entityId) {
141
                 $entityId = (int) $entityId;
134
                 $entityId = (int) $entityId;

+ 21
- 3
app/Services/ReportService.php View File

12
 use App\Enums\Accounting\AccountCategory;
12
 use App\Enums\Accounting\AccountCategory;
13
 use App\Enums\Accounting\AccountType;
13
 use App\Enums\Accounting\AccountType;
14
 use App\Models\Accounting\Account;
14
 use App\Models\Accounting\Account;
15
+use App\Models\Accounting\Transaction;
15
 use App\Support\Column;
16
 use App\Support\Column;
16
 use App\Utilities\Currency\CurrencyAccessor;
17
 use App\Utilities\Currency\CurrencyAccessor;
17
 use App\Utilities\Currency\CurrencyConverter;
18
 use App\Utilities\Currency\CurrencyConverter;
152
         return new Money($retainedEarnings, CurrencyAccessor::getDefaultCurrency());
153
         return new Money($retainedEarnings, CurrencyAccessor::getDefaultCurrency());
153
     }
154
     }
154
 
155
 
155
-    public function buildAccountTransactionsReport(string $startDate, string $endDate, ?array $columns = null, ?string $accountId = 'all', ?string $basis = 'accrual', ?string $entityId = 'all'): ReportDTO
156
+    public function buildAccountTransactionsReport(string $startDate, string $endDate, ?array $columns = null, ?string $accountId = 'all', ?string $entityId = 'all'): ReportDTO
156
     {
157
     {
157
         $columns ??= [];
158
         $columns ??= [];
158
         $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
159
         $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
164
         $query = $this->accountService->getAccountBalances($startDate, $endDate, $accountIds)
165
         $query = $this->accountService->getAccountBalances($startDate, $endDate, $accountIds)
165
             ->orderByRaw('LENGTH(code), code');
166
             ->orderByRaw('LENGTH(code), code');
166
 
167
 
167
-        $accounts = $query->with(['journalEntries' => $this->accountService->getTransactionDetailsSubquery($startDate, $endDate, $basis, $entityId)])->get();
168
+        $accounts = $query->with(['journalEntries' => $this->accountService->getTransactionDetailsSubquery($startDate, $endDate, $entityId)])->get();
168
 
169
 
169
         $reportCategories = [];
170
         $reportCategories = [];
170
 
171
 
208
                     credit: $journalEntry->type->isCredit() ? $formattedAmount : '',
209
                     credit: $journalEntry->type->isCredit() ? $formattedAmount : '',
209
                     balance: money($currentBalance, $defaultCurrency)->format(),
210
                     balance: money($currentBalance, $defaultCurrency)->format(),
210
                     type: $transaction->type,
211
                     type: $transaction->type,
211
-                    tableAction: $transaction->type->isJournal() ? 'updateJournalTransaction' : 'updateTransaction'
212
+                    tableAction: $this->determineTableAction($transaction),
212
                 );
213
                 );
213
             }
214
             }
214
 
215
 
246
         return new ReportDTO(categories: $reportCategories, fields: $columns);
247
         return new ReportDTO(categories: $reportCategories, fields: $columns);
247
     }
248
     }
248
 
249
 
250
+    private function determineTableAction(Transaction $transaction): array
251
+    {
252
+        if ($transaction->transactionable_type === null || $transaction->is_payment) {
253
+            return [
254
+                'type' => 'transaction',
255
+                'action' => $transaction->type->isJournal() ? 'updateJournalTransaction' : 'updateTransaction',
256
+                'id' => $transaction->id,
257
+            ];
258
+        }
259
+
260
+        return [
261
+            'type' => 'transactionable',
262
+            'model' => $transaction->transactionable_type,
263
+            'id' => $transaction->transactionable_id,
264
+        ];
265
+    }
266
+
249
     public function buildTrialBalanceReport(string $trialBalanceType, string $asOfDate, array $columns = []): ReportDTO
267
     public function buildTrialBalanceReport(string $trialBalanceType, string $asOfDate, array $columns = []): ReportDTO
250
     {
268
     {
251
         $asOfDateCarbon = Carbon::parse($asOfDate);
269
         $asOfDateCarbon = Carbon::parse($asOfDate);

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

1
+@php
2
+    use App\Filament\Company\Pages\Accounting\Transactions;
3
+    use App\Models\Accounting\Bill;
4
+    use App\Filament\Company\Resources\Purchases\BillResource\Pages\ViewBill;
5
+    use App\Filament\Company\Resources\Sales\InvoiceResource\Pages\ViewInvoice;
6
+
7
+    $iconPosition = \Filament\Support\Enums\IconPosition::After;
8
+@endphp
9
+
1
 <table class="w-full table-auto divide-y divide-gray-200 dark:divide-white/5">
10
 <table class="w-full table-auto divide-y divide-gray-200 dark:divide-white/5">
2
     <x-company.tables.header :headers="$report->getHeaders()" :alignmentClass="[$report, 'getAlignmentClass']"/>
11
     <x-company.tables.header :headers="$report->getHeaders()" :alignmentClass="[$report, 'getAlignmentClass']"/>
3
     @foreach($report->getCategories() as $categoryIndex => $category)
12
     @foreach($report->getCategories() as $categoryIndex => $category)
41
                         >
50
                         >
42
                             @if(is_array($cell) && isset($cell['description']))
51
                             @if(is_array($cell) && isset($cell['description']))
43
                                 @if(isset($cell['id']) && $cell['tableAction'])
52
                                 @if(isset($cell['id']) && $cell['tableAction'])
44
-                                    <x-filament::link
45
-                                        :href="\App\Filament\Company\Pages\Accounting\Transactions::getUrl(parameters: [
46
-                                            'tableAction' => $cell['tableAction'],
47
-                                            'tableActionRecord' => $cell['id'],
48
-                                        ])"
49
-                                        target="_blank"
50
-                                        color="primary"
51
-                                        icon="heroicon-o-arrow-top-right-on-square"
52
-                                        :icon-position="\Filament\Support\Enums\IconPosition::After"
53
-                                        icon-size="w-4 h-4 min-w-4 min-h-4"
54
-                                    >
55
-                                        {{ $cell['description'] }}
56
-                                    </x-filament::link>
53
+                                    @if($cell['tableAction']['type'] === 'transaction')
54
+                                        <x-filament::link
55
+                                            :href="Transactions::getUrl(parameters: [
56
+                                                'tableAction' => $cell['tableAction']['action'],
57
+                                                'tableActionRecord' => $cell['tableAction']['id'],
58
+                                            ])"
59
+                                            target="_blank"
60
+                                            color="primary"
61
+                                            icon="heroicon-o-arrow-top-right-on-square"
62
+                                            :icon-position="$iconPosition"
63
+                                            icon-size="w-4 h-4 min-w-4 min-h-4"
64
+                                        >
65
+                                            {{ $cell['description'] }}
66
+                                        </x-filament::link>
67
+                                    @else
68
+                                        <x-filament::link
69
+                                            :href="$cell['tableAction']['model'] === Bill::class
70
+                                                ? ViewBill::getUrl(['record' => $cell['tableAction']['id']])
71
+                                                : ViewInvoice::getUrl(['record' => $cell['tableAction']['id']])"
72
+                                            target="_blank"
73
+                                            color="primary"
74
+                                            icon="heroicon-o-arrow-top-right-on-square"
75
+                                            :icon-position="$iconPosition"
76
+                                            icon-size="w-4 h-4 min-w-4 min-h-4"
77
+                                        >
78
+                                            {{ $cell['description'] }}
79
+                                        </x-filament::link>
80
+                                    @endif
57
                                 @else
81
                                 @else
58
                                     {{ $cell['description'] }}
82
                                     {{ $cell['description'] }}
59
                                 @endif
83
                                 @endif

Loading…
Cancel
Save