Andrew Wallo 10 个月前
父节点
当前提交
a7cb06067a

+ 2
- 0
app/Enums/Accounting/BillStatus.php 查看文件

@@ -7,6 +7,7 @@ use Filament\Support\Contracts\HasLabel;
7 7
 
8 8
 enum BillStatus: string implements HasColor, HasLabel
9 9
 {
10
+    case Overdue = 'overdue';
10 11
     case Partial = 'partial';
11 12
     case Paid = 'paid';
12 13
     case Unpaid = 'unpaid';
@@ -20,6 +21,7 @@ enum BillStatus: string implements HasColor, HasLabel
20 21
     public function getColor(): string | array | null
21 22
     {
22 23
         return match ($this) {
24
+            self::Overdue => 'danger',
23 25
             self::Partial, self::Unpaid => 'warning',
24 26
             self::Paid => 'success',
25 27
             self::Void => 'gray',

+ 13
- 3
app/Filament/Company/Pages/Accounting/Transactions.php 查看文件

@@ -12,6 +12,7 @@ use App\Filament\Forms\Components\DateRangeSelect;
12 12
 use App\Filament\Forms\Components\JournalEntryRepeater;
13 13
 use App\Filament\Tables\Actions\ReplicateBulkAction;
14 14
 use App\Models\Accounting\Account;
15
+use App\Models\Accounting\Invoice;
15 16
 use App\Models\Accounting\JournalEntry;
16 17
 use App\Models\Accounting\Transaction;
17 18
 use App\Models\Banking\BankAccount;
@@ -262,7 +263,16 @@ class Transactions extends Page implements HasTable
262 263
                     'account',
263 264
                     'bankAccount.account',
264 265
                     'journalEntries.account',
265
-                ]);
266
+                ])
267
+                    ->where(function (Builder $query) {
268
+                        $query->whereDoesntHaveMorph(
269
+                            'transactionable',
270
+                            [Invoice::class],
271
+                            function (Builder $query) {
272
+                                $query->where('type', TransactionType::Journal);
273
+                            }
274
+                        );
275
+                    });
266 276
             })
267 277
             ->columns([
268 278
                 Tables\Columns\TextColumn::make('posted_at')
@@ -275,7 +285,7 @@ class Transactions extends Page implements HasTable
275 285
                     ->toggleable(isToggledHiddenByDefault: true),
276 286
                 Tables\Columns\TextColumn::make('description')
277 287
                     ->label('Description')
278
-                    ->limit(30)
288
+                    ->limit(50)
279 289
                     ->toggleable(),
280 290
                 Tables\Columns\TextColumn::make('bankAccount.account.name')
281 291
                     ->label('Account')
@@ -320,7 +330,7 @@ class Transactions extends Page implements HasTable
320 330
                     ->options(TransactionType::class),
321 331
                 $this->buildDateRangeFilter('posted_at', 'Posted', true),
322 332
                 $this->buildDateRangeFilter('updated_at', 'Last Modified'),
323
-            ], layout: Tables\Enums\FiltersLayout::Modal)
333
+            ])
324 334
             ->filtersFormSchema(fn (array $filters): array => [
325 335
                 Grid::make()
326 336
                     ->schema([

+ 19
- 13
app/Filament/Company/Resources/Sales/InvoiceResource.php 查看文件

@@ -29,6 +29,7 @@ use Filament\Support\Enums\Alignment;
29 29
 use Filament\Support\Enums\MaxWidth;
30 30
 use Filament\Tables;
31 31
 use Filament\Tables\Table;
32
+use Illuminate\Database\Eloquent\Builder;
32 33
 use Illuminate\Database\Eloquent\Collection;
33 34
 use Illuminate\Database\Eloquent\Model;
34 35
 use Illuminate\Support\Carbon;
@@ -447,7 +448,8 @@ class InvoiceResource extends Resource
447 448
                     ->sortable(),
448 449
                 Tables\Columns\TextColumn::make('invoice_number')
449 450
                     ->label('Number')
450
-                    ->searchable(),
451
+                    ->searchable()
452
+                    ->sortable(),
451 453
                 Tables\Columns\TextColumn::make('client.name')
452 454
                     ->sortable(),
453 455
                 Tables\Columns\TextColumn::make('total')
@@ -460,24 +462,28 @@ class InvoiceResource extends Resource
460 462
                     ->currency(),
461 463
             ])
462 464
             ->filters([
463
-                Tables\Filters\SelectFilter::make('status')
464
-                    ->options(InvoiceStatus::class)
465
-                    ->native(false),
466 465
                 Tables\Filters\SelectFilter::make('client')
467 466
                     ->relationship('client', 'name')
468 467
                     ->searchable()
469 468
                     ->preload(),
469
+                Tables\Filters\SelectFilter::make('status')
470
+                    ->options(InvoiceStatus::class)
471
+                    ->native(false),
472
+                Tables\Filters\TernaryFilter::make('has_payments')
473
+                    ->label('Has Payments')
474
+                    ->queries(
475
+                        true: fn (Builder $query) => $query->whereHas('payments'),
476
+                        false: fn (Builder $query) => $query->whereDoesntHave('payments'),
477
+                    ),
470 478
                 DateRangeFilter::make('date')
471 479
                     ->fromLabel('From Date')
472
-                    ->untilLabel('Until Date')
473
-                    ->indicatorLabel('Date Range'),
474
-            ], layout: Tables\Enums\FiltersLayout::Modal)
475
-            ->filtersFormWidth(MaxWidth::Small)
476
-            ->filtersTriggerAction(
477
-                fn (Tables\Actions\Action $action) => $action
478
-                    ->label('Filter')
479
-                    ->slideOver(),
480
-            )
480
+                    ->untilLabel('To Date')
481
+                    ->indicatorLabel('Date'),
482
+                DateRangeFilter::make('due_date')
483
+                    ->fromLabel('From Due Date')
484
+                    ->untilLabel('To Due Date')
485
+                    ->indicatorLabel('Due'),
486
+            ])
481 487
             ->actions([
482 488
                 Tables\Actions\ActionGroup::make([
483 489
                     Tables\Actions\EditAction::make(),

+ 41
- 0
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ListInvoices.php 查看文件

@@ -2,9 +2,13 @@
2 2
 
3 3
 namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
4 4
 
5
+use App\Enums\Accounting\InvoiceStatus;
5 6
 use App\Filament\Company\Resources\Sales\InvoiceResource;
7
+use App\Models\Accounting\Invoice;
6 8
 use Filament\Actions;
9
+use Filament\Resources\Components\Tab;
7 10
 use Filament\Resources\Pages\ListRecords;
11
+use Illuminate\Database\Eloquent\Builder;
8 12
 
9 13
 class ListInvoices extends ListRecords
10 14
 {
@@ -16,4 +20,41 @@ class ListInvoices extends ListRecords
16 20
             Actions\CreateAction::make(),
17 21
         ];
18 22
     }
23
+
24
+    public function getTabs(): array
25
+    {
26
+        return [
27
+            'all' => Tab::make()
28
+                ->label('All'),
29
+
30
+            'overdue' => Tab::make()
31
+                ->label('Overdue')
32
+                ->modifyQueryUsing(function (Builder $query) {
33
+                    $query->where('status', InvoiceStatus::Overdue);
34
+                })
35
+                ->badge(Invoice::where('status', InvoiceStatus::Overdue)->count()),
36
+
37
+            'unpaid' => Tab::make()
38
+                ->label('Unpaid')
39
+                ->modifyQueryUsing(function (Builder $query) {
40
+                    $query->whereIn('status', [
41
+                        InvoiceStatus::Unsent,
42
+                        InvoiceStatus::Sent,
43
+                        InvoiceStatus::Partial,
44
+                    ]);
45
+                })
46
+                ->badge(Invoice::whereIn('status', [
47
+                    InvoiceStatus::Unsent,
48
+                    InvoiceStatus::Sent,
49
+                    InvoiceStatus::Partial,
50
+                ])->count()),
51
+
52
+            'draft' => Tab::make()
53
+                ->label('Draft')
54
+                ->modifyQueryUsing(function (Builder $query) {
55
+                    $query->where('status', InvoiceStatus::Draft);
56
+                })
57
+                ->badge(Invoice::where('status', InvoiceStatus::Draft)->count()),
58
+        ];
59
+    }
19 60
 }

+ 8
- 0
app/Models/Accounting/Invoice.php 查看文件

@@ -13,6 +13,7 @@ use App\Models\Common\Client;
13 13
 use App\Observers\InvoiceObserver;
14 14
 use Illuminate\Database\Eloquent\Attributes\CollectedBy;
15 15
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
16
+use Illuminate\Database\Eloquent\Casts\Attribute;
16 17
 use Illuminate\Database\Eloquent\Factories\HasFactory;
17 18
 use Illuminate\Database\Eloquent\Model;
18 19
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -100,6 +101,13 @@ class Invoice extends Model
100 101
             ->where('type', TransactionType::Journal);
101 102
     }
102 103
 
104
+    protected function isCurrentlyOverdue(): Attribute
105
+    {
106
+        return Attribute::get(function () {
107
+            return $this->due_date->isBefore(today()) && $this->canBeOverdue();
108
+        });
109
+    }
110
+
103 111
     public function isDraft(): bool
104 112
     {
105 113
         return $this->status === InvoiceStatus::Draft;

+ 1
- 1
app/Observers/InvoiceObserver.php 查看文件

@@ -33,7 +33,7 @@ class InvoiceObserver
33 33
 
34 34
     public function saving(Invoice $invoice): void
35 35
     {
36
-        if ($invoice->due_date->isBefore(today()) && $invoice->canBeOverdue()) {
36
+        if ($invoice->is_currently_overdue) {
37 37
             $invoice->status = InvoiceStatus::Overdue;
38 38
         }
39 39
     }

+ 5
- 1
app/Providers/FilamentCompaniesServiceProvider.php 查看文件

@@ -52,6 +52,7 @@ use Filament\Pages\Dashboard;
52 52
 use Filament\Panel;
53 53
 use Filament\PanelProvider;
54 54
 use Filament\Support\Colors\Color;
55
+use Filament\Support\Enums\MaxWidth;
55 56
 use Filament\Tables;
56 57
 use Filament\Widgets;
57 58
 use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
@@ -266,7 +267,10 @@ class FilamentCompaniesServiceProvider extends PanelProvider
266 267
         });
267 268
 
268 269
         Tables\Table::configureUsing(static function (Tables\Table $table): void {
269
-            $table->paginationPageOptions([5, 10, 25, 50, 100]);
270
+            $table
271
+                ->paginationPageOptions([5, 10, 25, 50, 100])
272
+                ->filtersFormWidth(MaxWidth::Small)
273
+                ->filtersTriggerAction(fn (Tables\Actions\Action $action) => $action->slideOver());
270 274
         }, isImportant: true);
271 275
     }
272 276
 

+ 3
- 1
database/factories/Accounting/DocumentLineItemFactory.php 查看文件

@@ -30,7 +30,7 @@ class DocumentLineItemFactory extends Factory
30 30
 
31 31
         return [
32 32
             'company_id' => 1,
33
-            'offering_id' => 1,
33
+            'offering_id' => $offering->id,
34 34
             'description' => $this->faker->sentence,
35 35
             'quantity' => $quantity,
36 36
             'unit_price' => $unitPrice,
@@ -49,6 +49,8 @@ class DocumentLineItemFactory extends Factory
49 49
                 $lineItem->salesDiscounts()->sync($offering->salesDiscounts->pluck('id')->toArray());
50 50
             }
51 51
 
52
+            $lineItem->refresh();
53
+
52 54
             $taxTotal = $lineItem->calculateTaxTotal()->getAmount();
53 55
             $discountTotal = $lineItem->calculateDiscountTotal()->getAmount();
54 56
 

+ 7
- 3
database/factories/Accounting/InvoiceFactory.php 查看文件

@@ -58,6 +58,8 @@ class InvoiceFactory extends Factory
58 58
                 return;
59 59
             }
60 60
 
61
+            $this->recalculateTotals($invoice);
62
+
61 63
             $invoice->approveDraft();
62 64
         });
63 65
     }
@@ -66,11 +68,12 @@ class InvoiceFactory extends Factory
66 68
     {
67 69
         return $this->afterCreating(function (Invoice $invoice) use ($invoiceStatus, $max, $min) {
68 70
             if ($invoice->isDraft()) {
71
+
72
+                $this->recalculateTotals($invoice);
73
+
69 74
                 $invoice->approveDraft();
70 75
             }
71 76
 
72
-            $this->recalculateTotals($invoice);
73
-
74 77
             $invoice->refresh();
75 78
 
76 79
             $totalAmountDue = $invoice->getRawOriginal('amount_due');
@@ -125,7 +128,7 @@ class InvoiceFactory extends Factory
125 128
 
126 129
             $this->recalculateTotals($invoice);
127 130
 
128
-            if ($invoice->due_date->isBefore(today()) && $invoice->canBeOverdue()) {
131
+            if ($invoice->is_currently_overdue) {
129 132
                 $invoice->updateQuietly([
130 133
                     'status' => InvoiceStatus::Overdue,
131 134
                 ]);
@@ -136,6 +139,7 @@ class InvoiceFactory extends Factory
136 139
     protected function recalculateTotals(Invoice $invoice): void
137 140
     {
138 141
         if ($invoice->lineItems()->exists()) {
142
+            $invoice->refresh();
139 143
             $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
140 144
             $taxTotal = $invoice->lineItems()->sum('tax_total') / 100;
141 145
             $discountTotal = $invoice->lineItems()->sum('discount_total') / 100;

正在加载...
取消
保存