WIP Work on Payments

This commit is contained in:
Nisse Lommerde 2025-01-24 21:37:05 -05:00
parent f937302159
commit cadc096227
26 changed files with 2143 additions and 1277 deletions

View File

@ -5,7 +5,7 @@
enum IconEnum: string
{
case DEFAULT = 'heroicon-o-rectangle-stack';
case INVOICE = 'lucide-receipt-text';
case INVOICE = 'lucide-file-text';
case ORDER = 'lucide-shopping-cart';
case QUOTE = 'lucide-quote';
case CUSTOMER = 'lucide-building';
@ -15,8 +15,8 @@ enum IconEnum: string
case TAX_RATE = 'lucide-circle-dollar-sign';
// case PRODUCT_SERVICE = 'heroicon-o-rectangle-stack';
// case CUSTOMER_SALES = 'heroicon-o-rectangle-stack';
// case INVOICE_REPORT = 'heroicon-o-rectangle-stack';
case CUSTOMER_SALES = 'lucide-book-user';
case INVOICE_REPORT = 'lucide-files';
case TAB_ALL = 'lucide-layout-grid';
case TAB_OVERDUE = 'lucide-calendar-clock';

View File

@ -18,13 +18,13 @@ class CustomerReportResource extends Resource
{
protected static ?string $model = Customer::class;
protected static ?string $navigationIcon = IconEnum::CUSTOMER_SALES->value;
protected static ?string $navigationGroup = 'Reports';
protected static ?string $navigationIcon = IconEnum::DEFAULT->value;
protected static ?string $navigationLabel = 'Customer Reports';
protected static ?string $navigationLabel = 'Customer Sales';
protected static ?int $navigationSort = 2;
protected static ?int $navigationSort = 4;
public static function form(Form $form): Form
{

View File

@ -17,7 +17,7 @@
class InvoiceReportResource extends Resource
{
protected static ?string $navigationIcon = IconEnum::DEFAULT->value;
protected static ?string $navigationIcon = IconEnum::INVOICE_REPORT->value;
protected static ?string $navigationGroup = 'Reports';

View File

@ -31,9 +31,9 @@ class InvoiceResource extends Resource
protected static ?string $navigationIcon = IconEnum::INVOICE->value;
protected static ?string $navigationGroup = 'Production';
protected static ?string $navigationGroup = 'Financial';
protected static ?int $navigationSort = 2;
protected static ?int $navigationSort = 1;
public static function form(Form $form): Form
{

View File

@ -24,7 +24,7 @@ class PackingSlipResource extends Resource
protected static ?string $navigationIcon = IconEnum::PACKING_SLIP->value;
protected static ?string $navigationGroup = 'Management';
protected static ?string $navigationGroup = 'Production';
protected static ?int $navigationSort = 2;

View File

@ -0,0 +1,74 @@
<?php
namespace App\Filament\Admin\Resources;
use App\Filament\Admin\Resources\PaymentResource\Pages;
use App\Models\Payment;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
class PaymentResource extends Resource
{
protected static ?string $model = Payment::class;
protected static ?string $navigationIcon = 'lucide-hand-coins';
protected static ?string $navigationGroup = 'Financial';
protected static ?int $navigationSort = 2;
public static function form(Form $form): Form
{
return $form
->schema([
Section::make([
Select::make('customer_id')
->relationship('customer', 'company_name'),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
//
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function canAccess(): bool
{
return auth()->user()->is_admin;
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListPayments::route('/'),
'create' => Pages\CreatePayment::route('/create'),
'edit' => Pages\EditPayment::route('/{record}/edit'),
];
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Admin\Resources\PaymentResource\Pages;
use App\Filament\Admin\Resources\PaymentResource;
use Filament\Resources\Pages\CreateRecord;
class CreatePayment extends CreateRecord
{
protected static string $resource = PaymentResource::class;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Admin\Resources\PaymentResource\Pages;
use App\Filament\Admin\Resources\PaymentResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditPayment extends EditRecord
{
protected static string $resource = PaymentResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Admin\Resources\PaymentResource\Pages;
use App\Filament\Admin\Resources\PaymentResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListPayments extends ListRecords
{
protected static string $resource = PaymentResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -22,6 +22,8 @@ class ServiceTypeResource extends Resource
protected static ?string $label = 'Product Services';
protected static ?int $navigationSort = 2;
public static function getWidgets(): array
{
return [

View File

@ -54,6 +54,13 @@ class Invoice extends Model
'total' => 'float',
];
public function remainingBalance(): float
{
$applied = $this->payments()->sum('pivot.applied_amount');
return max(0, $this->total - $applied);
}
public function calculateTotals(): void
{
$this->refresh();
@ -120,4 +127,11 @@ public function invoiceReports(): BelongsToMany
{
return $this->belongsToMany(InvoiceReport::class);
}
public function payments(): BelongsToMany
{
return $this->belongsToMany(Payment::class)
->withPivot('applied_amount')
->withTimestamps();
}
}

64
app/Models/Payment.php Normal file
View File

@ -0,0 +1,64 @@
<?php
namespace App\Models;
use App\Enums\InvoiceStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Payment extends Model
{
use HasFactory;
protected $fillable = [
'customer_id',
'amount',
'unapplied_amount',
];
public function applyToInvoices()
{
$remaning = $this->unapplied_amount ?? $this->amount;
$invoices = Invoice::where('customer_id', $this->customer_id)
->where('status', '==', 'UNPAID')
->orderBy('date')
->get();
foreach ($invoices as $invoice) {
$balance = $invoice->remainingBalance();
if ($remaning <= 0) {
break;
}
$applied = min($remaining, $balance);
$invoice->payments()->attach($this->id, ['applied_amount' => $applied]);
$remaining -= $applied;
if ($invoice->remainingBalance() == 0) {
$invoice->setStatus(InvoiceStatus::PAID);
} elseif ($applied > 0) {
$invoice->setStatus(InvoiceStatus::UNPAID);
}
}
$this->unapplied_amount = $remaining;
$this->save();
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
}
public function invoices(): BelongsToMany
{
return $this->belongsToMany(Invoice::class)
->withPivot('applied_amount')
->withTimestamps();
}
}

View File

@ -6,6 +6,7 @@
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Navigation\NavigationGroup;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
@ -53,6 +54,13 @@ public function panel(Panel $panel): Panel
->authMiddleware([
Authenticate::class,
])
->sidebarWidth('13rem');
->sidebarWidth('13rem')
->navigationGroups([
NavigationGroup::make('Production'),
NavigationGroup::make('Management'),
NavigationGroup::make('Financial'),
NavigationGroup::make('Reports'),
NavigationGroup::make('Settings'),
]);
}
}

1491
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('payments', function (Blueprint $table) {
$table->id();
$table->foreignId('customer_id')->constrained();
$table->decimal('amount', 8, 2);
$table->decimal('unapplied_amount', 8, 2)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('payments');
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('invoice_payment', function (Blueprint $table) {
$table->id();
$table->foreignId('invoice_id')->constrained();
$table->foreignId('payment_id')->constrained();
$table->decimal('applied_amount', 8, 2);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('invoice_payment');
}
};

1473
package-lock.json generated

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long