From 1c3df1f70807d153fc06202e02f97b7c80c8dc19 Mon Sep 17 00:00:00 2001 From: Nisse Lommerde Date: Tue, 18 Feb 2025 20:13:54 -0500 Subject: [PATCH] #93 WIP Payments --- app/Enums/IconEnum.php | 1 + .../Admin/Resources/InvoiceResource.php | 48 ++++++++++++++++--- .../PaymentsRelationManager.php | 48 +++++++++++++++++++ .../Admin/Resources/PaymentResource.php | 26 ++++++---- .../PaymentResource/Pages/ListPayments.php | 4 +- .../InvoicesRelationManager.php | 48 +++++++++++++++++++ app/Models/Payment.php | 15 +++--- app/Observers/PaymentObserver.php | 2 +- .../migrations/018_create_payments_table.php | 2 + 9 files changed, 170 insertions(+), 24 deletions(-) create mode 100644 app/Filament/Admin/Resources/InvoiceResource/RelationManagers/PaymentsRelationManager.php create mode 100644 app/Filament/Admin/Resources/PaymentResource/RelationManagers/InvoicesRelationManager.php diff --git a/app/Enums/IconEnum.php b/app/Enums/IconEnum.php index 7923179..6ee1ed6 100644 --- a/app/Enums/IconEnum.php +++ b/app/Enums/IconEnum.php @@ -12,6 +12,7 @@ enum IconEnum: string case CUSTOMER = 'lucide-building'; case PACKING_SLIP = 'lucide-package'; case SHIPPING_ENTRY = 'lucide-truck'; + case PAYMENTS = 'lucide-hand-coins'; case USER = 'lucide-users'; case TAX_RATE = 'lucide-circle-dollar-sign'; case PRODUCT_SERVICE = 'heroicon-o-rectangle'; diff --git a/app/Filament/Admin/Resources/InvoiceResource.php b/app/Filament/Admin/Resources/InvoiceResource.php index 1f4a7d6..b005ca4 100644 --- a/app/Filament/Admin/Resources/InvoiceResource.php +++ b/app/Filament/Admin/Resources/InvoiceResource.php @@ -6,9 +6,11 @@ use App\Enums\InvoiceStatus; use App\Filament\Admin\Resources\CustomerResource\RelationManagers\InvoicesRelationManager; use App\Filament\Admin\Resources\InvoiceResource\RelationManagers\OrdersRelationManager; +use App\Filament\Admin\Resources\InvoiceResource\RelationManagers\PaymentsRelationManager; use App\Filament\Admin\Resources\InvoiceResource\RelationManagers\ProductServicesRelationManager; use App\Models\Customer; use App\Models\Invoice; +use App\Models\Payment; use Filament\Forms\Components\DatePicker; use Filament\Forms\Components\Grid; use Filament\Forms\Components\Group; @@ -22,6 +24,7 @@ use Filament\Resources\Resource; use Filament\Support\Enums\FontWeight; use Filament\Tables; +use Filament\Tables\Actions\BulkAction; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; @@ -253,18 +256,48 @@ public static function table(Table $table): Table ]) ->bulkActions([ - Tables\Actions\BulkAction::make('Mark as paid') - ->action(function (Collection $records) { - $records->each->setStatus(InvoiceStatus::PAID); + BulkAction::make('Create Payment') + ->icon(IconEnum::PAYMENTS->value) + ->form(fn ($form) => PaymentResource::form($form)) + ->action(function (Collection $records, array $data) { + if ($records->pluck('customer_id')->unique()->count() !== 1) { + Notification::make() + ->title('Invalid order combination') + ->body('Make sure all orders are from the same customer') + ->danger() + ->send(); + + return; + } + + $payment = Payment::create([ + 'customer_id' => $records->pluck('customer_id')->first(), + 'amount' => $data['amount'], + 'date' => $data['date'], + 'check_number' => $data['check_number'], + 'notes' => $data['notes'], + ]); + + $payment->applyToInvoices($records); + Notification::make() - ->title(count($records).' item(s) saved successfully') + ->title('Payment created successfully') ->success() ->send(); - }) - ->icon('lucide-circle-check') - ->deselectRecordsAfterCompletion(), + }), Tables\Actions\BulkActionGroup::make([ + Tables\Actions\BulkAction::make('Mark as paid') + ->action(function (Collection $records) { + $records->each->setStatus(InvoiceStatus::PAID); + Notification::make() + ->title(count($records).' item(s) saved successfully') + ->success() + ->send(); + }) + ->icon('lucide-circle-check') + ->deselectRecordsAfterCompletion(), + Tables\Actions\BulkAction::make('Mark as unpaid') ->action(function (Collection $records) { $records->each->setStatus(InvoiceStatus::UNPAID); @@ -292,6 +325,7 @@ public static function getRelations(): array return [ OrdersRelationManager::class, ProductServicesRelationManager::class, + PaymentsRelationManager::class, ]; } diff --git a/app/Filament/Admin/Resources/InvoiceResource/RelationManagers/PaymentsRelationManager.php b/app/Filament/Admin/Resources/InvoiceResource/RelationManagers/PaymentsRelationManager.php new file mode 100644 index 0000000..43a98bb --- /dev/null +++ b/app/Filament/Admin/Resources/InvoiceResource/RelationManagers/PaymentsRelationManager.php @@ -0,0 +1,48 @@ +schema([ + Forms\Components\TextInput::make('date') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->recordTitleAttribute('date') + ->columns([ + Tables\Columns\TextColumn::make('date'), + ]) + ->filters([ + // + ]) + ->headerActions([ + Tables\Actions\CreateAction::make(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Admin/Resources/PaymentResource.php b/app/Filament/Admin/Resources/PaymentResource.php index 9730538..bcd17b8 100644 --- a/app/Filament/Admin/Resources/PaymentResource.php +++ b/app/Filament/Admin/Resources/PaymentResource.php @@ -2,16 +2,19 @@ namespace App\Filament\Admin\Resources; +use App\Enums\IconEnum; use App\Filament\Admin\Resources\CustomerResource\RelationManagers\PaymentsRelationManager; +use App\Filament\Admin\Resources\InvoiceResource\Pages\ListInvoices; use App\Filament\Admin\Resources\PaymentResource\Pages; +use App\Filament\Admin\Resources\PaymentResource\RelationManagers\InvoicesRelationManager; use App\Models\Payment; +use Filament\Forms\Components\DatePicker; use Filament\Forms\Components\Section; use Filament\Forms\Components\Select; use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Form; use Filament\Resources\Resource; -use Filament\Tables\Actions\ViewAction; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; @@ -19,7 +22,7 @@ class PaymentResource extends Resource { protected static ?string $model = Payment::class; - protected static ?string $navigationIcon = 'lucide-hand-coins'; + protected static ?string $navigationIcon = IconEnum::PAYMENTS->value; protected static ?string $navigationGroup = 'Financial'; @@ -32,14 +35,23 @@ public static function form(Form $form): Form Section::make([ Select::make('customer_id') ->relationship('customer', 'company_name') + ->prefix('$') ->required() ->searchable() + ->hidden(fn ($livewire) => $livewire::class === ListInvoices::class) ->preload(), + TextInput::make('amount') ->required() ->minValue(0) ->maxValue(99999999) ->numeric(), + + TextInput::make('check_number'), + + DatePicker::make('date') + ->default(today()), + Textarea::make('notes'), ]), ]); @@ -58,9 +70,7 @@ public static function table(Table $table): Table ->hidden(fn ($livewire) => $livewire::class === PaymentsRelationManager::class) ->searchable(), - TextColumn::make('notes') - ->limit(100) - ->extraHeaderAttributes(['class' => 'w-full']), + TextColumn::make('check_number'), TextColumn::make('amount') ->searchable() @@ -72,7 +82,7 @@ public static function table(Table $table): Table ->money(), ]) ->actions([ - ViewAction::make(), + \Filament\Tables\Actions\EditAction::make(), ]); } @@ -84,7 +94,7 @@ public static function canAccess(): bool public static function getRelations(): array { return [ - // + InvoicesRelationManager::class, ]; } @@ -94,7 +104,7 @@ public static function getPages(): array 'index' => Pages\ListPayments::route('/'), // 'view' => Pages\ViewPayment::route('/{record}'), // 'create' => Pages\CreatePayment::route('/create'), - // 'edit' => Pages\EditPayment::route('/{record}/edit'), + 'edit' => Pages\EditPayment::route('/{record}/edit'), ]; } } diff --git a/app/Filament/Admin/Resources/PaymentResource/Pages/ListPayments.php b/app/Filament/Admin/Resources/PaymentResource/Pages/ListPayments.php index 06267c9..9225d33 100644 --- a/app/Filament/Admin/Resources/PaymentResource/Pages/ListPayments.php +++ b/app/Filament/Admin/Resources/PaymentResource/Pages/ListPayments.php @@ -16,7 +16,7 @@ class ListPayments extends ListRecords protected function getHeaderActions(): array { return [ - Actions\Action::make('distributePayments') + /* Actions\Action::make('distributePayments') ->icon(IconEnum::DISTRIBUTE_PAYMENTS->value) ->action(function (PaymentService $paymentService) { $paymentService->distributePayments(); @@ -26,7 +26,7 @@ protected function getHeaderActions(): array ->body('Payments have been distributed') ->success() ->send(); - }), + }),*/ Actions\CreateAction::make() ->icon(IconEnum::NEW->value), diff --git a/app/Filament/Admin/Resources/PaymentResource/RelationManagers/InvoicesRelationManager.php b/app/Filament/Admin/Resources/PaymentResource/RelationManagers/InvoicesRelationManager.php new file mode 100644 index 0000000..a3f6892 --- /dev/null +++ b/app/Filament/Admin/Resources/PaymentResource/RelationManagers/InvoicesRelationManager.php @@ -0,0 +1,48 @@ +schema([ + Forms\Components\TextInput::make('internal_id') + ->required() + ->maxLength(255), + ]); + } + + public function table(Table $table): Table + { + return $table + ->recordTitleAttribute('internal_id') + ->columns([ + Tables\Columns\TextColumn::make('internal_id'), + ]) + ->filters([ + // + ]) + ->headerActions([ + Tables\Actions\CreateAction::make(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 33ec6b3..94b5228 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; #[ObservedBy(PaymentObserver::class)] @@ -18,21 +19,23 @@ class Payment extends Model protected $fillable = [ 'customer_id', + 'check_number', + 'date', 'amount', 'unapplied_amount', 'notes', ]; - public function applyToInvoices(): void + public function applyToInvoices(Collection $invoices): void { $remaining = $this->unapplied_amount ?? $this->amount; - $invoices = Invoice::where('customer_id', $this->customer_id) - ->where('status', InvoiceStatus::UNPAID) - ->orderBy('date') - ->get(); + $filteredInvoices = $invoices->whereIn('status', [ + InvoiceStatus::UNPAID, + InvoiceStatus::PARTIALLY_PAID, + ]); - foreach ($invoices as $invoice) { + foreach ($filteredInvoices as $invoice) { $balance = $invoice->remainingBalance(); if ($remaining <= 0) { diff --git a/app/Observers/PaymentObserver.php b/app/Observers/PaymentObserver.php index d099fc3..5ee8435 100644 --- a/app/Observers/PaymentObserver.php +++ b/app/Observers/PaymentObserver.php @@ -11,7 +11,7 @@ class PaymentObserver */ public function saved(Payment $payment): void { - $payment->applyToInvoices(); + // $payment->applyToInvoices(); } /** diff --git a/database/migrations/018_create_payments_table.php b/database/migrations/018_create_payments_table.php index 76a713b..243d02c 100644 --- a/database/migrations/018_create_payments_table.php +++ b/database/migrations/018_create_payments_table.php @@ -15,6 +15,8 @@ public function up(): void $table->id(); $table->foreignId('customer_id')->constrained(); + $table->text('check_number')->nullable(); + $table->date('date')->default(today()); $table->decimal('amount', 8, 2); $table->decimal('unapplied_amount', 8, 2)->nullable(); $table->text('notes')->nullable();