Andrew Wallo 6 月之前
父節點
當前提交
fc5f6f8b2e

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

413
                             ->url(static fn (Invoice $record) => Pages\ViewInvoice::getUrl(['record' => $record])),
413
                             ->url(static fn (Invoice $record) => Pages\ViewInvoice::getUrl(['record' => $record])),
414
                         Invoice::getReplicateAction(Tables\Actions\ReplicateAction::class),
414
                         Invoice::getReplicateAction(Tables\Actions\ReplicateAction::class),
415
                         Invoice::getApproveDraftAction(Tables\Actions\Action::class),
415
                         Invoice::getApproveDraftAction(Tables\Actions\Action::class),
416
-                        Invoice::getBlockedApproveAction(Tables\Actions\Action::class),
417
                         Invoice::getMarkAsSentAction(Tables\Actions\Action::class),
416
                         Invoice::getMarkAsSentAction(Tables\Actions\Action::class),
418
                         Tables\Actions\Action::make('recordPayment')
417
                         Tables\Actions\Action::make('recordPayment')
419
                             ->label(fn (Invoice $record) => $record->status === InvoiceStatus::Overpaid ? 'Refund Overpayment' : 'Record Payment')
418
                             ->label(fn (Invoice $record) => $record->status === InvoiceStatus::Overpaid ? 'Refund Overpayment' : 'Record Payment')

+ 27
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php 查看文件

5
 use App\Enums\Accounting\DocumentType;
5
 use App\Enums\Accounting\DocumentType;
6
 use App\Filament\Company\Resources\Sales\ClientResource;
6
 use App\Filament\Company\Resources\Sales\ClientResource;
7
 use App\Filament\Company\Resources\Sales\InvoiceResource;
7
 use App\Filament\Company\Resources\Sales\InvoiceResource;
8
+use App\Filament\Infolists\Components\BannerEntry;
8
 use App\Filament\Infolists\Components\DocumentPreview;
9
 use App\Filament\Infolists\Components\DocumentPreview;
9
 use App\Models\Accounting\Invoice;
10
 use App\Models\Accounting\Invoice;
10
 use Filament\Actions;
11
 use Filament\Actions;
17
 use Filament\Support\Enums\IconPosition;
18
 use Filament\Support\Enums\IconPosition;
18
 use Filament\Support\Enums\IconSize;
19
 use Filament\Support\Enums\IconSize;
19
 use Filament\Support\Enums\MaxWidth;
20
 use Filament\Support\Enums\MaxWidth;
21
+use Illuminate\Support\HtmlString;
20
 
22
 
21
 class ViewInvoice extends ViewRecord
23
 class ViewInvoice extends ViewRecord
22
 {
24
 {
40
             Actions\ActionGroup::make([
42
             Actions\ActionGroup::make([
41
                 Actions\ActionGroup::make([
43
                 Actions\ActionGroup::make([
42
                     Invoice::getApproveDraftAction(),
44
                     Invoice::getApproveDraftAction(),
43
-                    Invoice::getBlockedApproveAction(),
44
                     Invoice::getMarkAsSentAction(),
45
                     Invoice::getMarkAsSentAction(),
45
                     Invoice::getPrintDocumentAction(),
46
                     Invoice::getPrintDocumentAction(),
46
                     Invoice::getReplicateAction(),
47
                     Invoice::getReplicateAction(),
61
     {
62
     {
62
         return $infolist
63
         return $infolist
63
             ->schema([
64
             ->schema([
65
+                BannerEntry::make('inactiveAdjustments')
66
+                    ->label('Inactive adjustments')
67
+                    ->warning()
68
+                    ->icon('heroicon-o-exclamation-triangle')
69
+                    ->visible(fn (Invoice $record) => $record->hasInactiveAdjustments() && $record->canBeApproved())
70
+                    ->columnSpanFull()
71
+                    ->description(function (Invoice $record) {
72
+                        $inactiveAdjustments = collect();
73
+
74
+                        foreach ($record->lineItems as $lineItem) {
75
+                            foreach ($lineItem->adjustments as $adjustment) {
76
+                                if ($adjustment->isInactive() && $inactiveAdjustments->doesntContain($adjustment->name)) {
77
+                                    $inactiveAdjustments->push($adjustment->name);
78
+                                }
79
+                            }
80
+                        }
81
+
82
+                        $adjustmentsList = $inactiveAdjustments->map(static function ($name) {
83
+                            return "<span class='font-medium'>{$name}</span>";
84
+                        })->join(', ');
85
+
86
+                        $output = "<p class='text-sm'>This invoice contains inactive adjustments that need to be addressed before approval: {$adjustmentsList}</p>";
87
+
88
+                        return new HtmlString($output);
89
+                    }),
64
                 Section::make('Invoice Details')
90
                 Section::make('Invoice Details')
65
                     ->columns(4)
91
                     ->columns(4)
66
                     ->schema([
92
                     ->schema([

+ 9
- 0
app/Models/Accounting/Document.php 查看文件

35
         return $this->lineItems()->exists();
35
         return $this->lineItems()->exists();
36
     }
36
     }
37
 
37
 
38
+    public function hasInactiveAdjustments(): bool
39
+    {
40
+        return $this->lineItems->contains(function (DocumentLineItem $lineItem) {
41
+            return $lineItem->adjustments->contains(function (Adjustment $adjustment) {
42
+                return $adjustment->isInactive();
43
+            });
44
+        });
45
+    }
46
+
38
     public static function getPrintDocumentAction(string $action = Action::class): MountableAction
47
     public static function getPrintDocumentAction(string $action = Action::class): MountableAction
39
     {
48
     {
40
         return $action::make('printPdf')
49
         return $action::make('printPdf')

+ 21
- 13
app/Models/Accounting/Invoice.php 查看文件

23
 use Filament\Actions\MountableAction;
23
 use Filament\Actions\MountableAction;
24
 use Filament\Actions\ReplicateAction;
24
 use Filament\Actions\ReplicateAction;
25
 use Filament\Actions\StaticAction;
25
 use Filament\Actions\StaticAction;
26
+use Filament\Notifications\Notification;
26
 use Filament\Support\Enums\Alignment;
27
 use Filament\Support\Enums\Alignment;
27
 use Illuminate\Database\Eloquent\Attributes\CollectedBy;
28
 use Illuminate\Database\Eloquent\Attributes\CollectedBy;
28
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
29
 use Illuminate\Database\Eloquent\Attributes\ObservedBy;
34
 use Illuminate\Database\Eloquent\Relations\MorphOne;
35
 use Illuminate\Database\Eloquent\Relations\MorphOne;
35
 use Illuminate\Support\Carbon;
36
 use Illuminate\Support\Carbon;
36
 use Illuminate\Support\HtmlString;
37
 use Illuminate\Support\HtmlString;
38
+use Livewire\Component;
37
 
39
 
38
 #[CollectedBy(DocumentCollection::class)]
40
 #[CollectedBy(DocumentCollection::class)]
39
 #[ObservedBy(InvoiceObserver::class)]
41
 #[ObservedBy(InvoiceObserver::class)]
461
         return CurrencyConverter::convertCentsToFormatSimple($convertedCents);
463
         return CurrencyConverter::convertCentsToFormatSimple($convertedCents);
462
     }
464
     }
463
 
465
 
464
-    public function hasInactiveAdjustments(): bool
465
-    {
466
-        return $this->lineItems->contains(function (DocumentLineItem $lineItem) {
467
-            return $lineItem->adjustments->contains(function (Adjustment $adjustment) {
468
-                return $adjustment->isInactive();
469
-            });
470
-        });
471
-    }
472
-
473
     // TODO: Potentially handle this another way
466
     // TODO: Potentially handle this another way
474
     public static function getBlockedApproveAction(string $action = Action::class): MountableAction
467
     public static function getBlockedApproveAction(string $action = Action::class): MountableAction
475
     {
468
     {
515
             ->label('Approve')
508
             ->label('Approve')
516
             ->icon('heroicon-m-check-circle')
509
             ->icon('heroicon-m-check-circle')
517
             ->visible(function (self $record) {
510
             ->visible(function (self $record) {
518
-                return $record->canBeApproved() && ! $record->hasInactiveAdjustments();
511
+                return $record->canBeApproved();
519
             })
512
             })
520
             ->requiresConfirmation()
513
             ->requiresConfirmation()
521
             ->databaseTransaction()
514
             ->databaseTransaction()
522
             ->successNotificationTitle('Invoice approved')
515
             ->successNotificationTitle('Invoice approved')
523
-            ->action(function (self $record, MountableAction $action) {
524
-                $record->approveDraft();
516
+            ->action(function (self $record, MountableAction $action, Component $livewire) {
517
+                if ($record->hasInactiveAdjustments()) {
518
+                    $isViewPage = $livewire instanceof InvoiceResource\Pages\ViewInvoice;
519
+
520
+                    if (! $isViewPage) {
521
+                        redirect(InvoiceResource\Pages\ViewInvoice::getUrl(['record' => $record->id]));
522
+                    } else {
523
+                        Notification::make()
524
+                            ->warning()
525
+                            ->title('Cannot approve invoice')
526
+                            ->body('This invoice has inactive adjustments that must be addressed first.')
527
+                            ->persistent()
528
+                            ->send();
529
+                    }
530
+                } else {
531
+                    $record->approveDraft();
525
 
532
 
526
-                $action->success();
533
+                    $action->success();
534
+                }
527
             });
535
             });
528
     }
536
     }
529
 
537
 

Loading…
取消
儲存