From 265cf28e7beb675cf338779bab0d9340d48216fa Mon Sep 17 00:00:00 2001 From: "sweep-ai[bot]" <128439645+sweep-ai[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:47:45 +0000 Subject: [PATCH] Add Tax Rate Management and Invoice Tax Calculation Features --- .../App/Resources/InvoiceResource.php | 25 +++++- .../App/Resources/TaxRateResource.php | 82 +++++++++++++++++++ app/Models/Invoice.php | 28 +++++++ app/Models/TaxRate.php | 44 ++++++++++ ...24_01_10_000000_create_tax_rates_table.php | 42 ++++++++++ 5 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 app/Filament/App/Resources/TaxRateResource.php create mode 100644 app/Models/TaxRate.php create mode 100644 database/migrations/2024_01_10_000000_create_tax_rates_table.php diff --git a/app/Filament/App/Resources/InvoiceResource.php b/app/Filament/App/Resources/InvoiceResource.php index 3426ab4c..ba508787 100644 --- a/app/Filament/App/Resources/InvoiceResource.php +++ b/app/Filament/App/Resources/InvoiceResource.php @@ -33,7 +33,30 @@ public static function form(Form $form): Form ->label('Customer'), DatePicker::make('invoice_date'), TextInput::make('total_amount') - ->numeric(), + ->numeric() + ->reactive() + ->afterStateUpdated(function ($state, callable $set, $get) { + if ($get('tax_rate_id')) { + $taxRate = TaxRate::find($get('tax_rate_id')); + $taxAmount = $state * ($taxRate->rate / 100); + $set('tax_amount', $taxAmount); + } + }), + BelongsToSelect::make('tax_rate_id') + ->relationship('taxRate', 'name') + ->label('Tax Rate') + ->reactive() + ->afterStateUpdated(function ($state, callable $set, $get) { + if ($state && $get('total_amount')) { + $taxRate = TaxRate::find($state); + $taxAmount = $get('total_amount') * ($taxRate->rate / 100); + $set('tax_amount', $taxAmount); + } + }), + TextInput::make('tax_amount') + ->numeric() + ->disabled() + ->label('Tax Amount'), Select::make('payment_status') ->options([ 'pending' => 'Pending', diff --git a/app/Filament/App/Resources/TaxRateResource.php b/app/Filament/App/Resources/TaxRateResource.php new file mode 100644 index 00000000..2566c9a3 --- /dev/null +++ b/app/Filament/App/Resources/TaxRateResource.php @@ -0,0 +1,82 @@ + + +schema([ + Forms\Components\TextInput::make('name') + ->required() + ->maxLength(255), + Forms\Components\TextInput::make('rate') + ->required() + ->numeric() + ->step(0.01) + ->suffix('%'), + Forms\Components\Textarea::make('description') + ->maxLength(65535), + Forms\Components\Toggle::make('is_compound') + ->label('Compound Tax') + ->helperText('Apply this tax after other taxes'), + Forms\Components\Toggle::make('is_active') + ->label('Active') + ->default(true), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->searchable(), + Tables\Columns\TextColumn::make('rate') + ->suffix('%') + ->sortable(), + Tables\Columns\IconColumn::make('is_active') + ->boolean(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TernaryFilter::make('is_active') + ->label('Active'), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListTaxRates::route('/'), + 'create' => Pages\CreateTaxRate::route('/create'), + 'edit' => Pages\EditTaxRate::route('/{record}/edit'), + ]; + } +} \ No newline at end of file diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 06013670..c397b74d 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -15,11 +15,39 @@ class Invoice extends Model "customer_id", "invoice_date", "total_amount", + "tax_amount", + "tax_rate_id", "payment_status" ]; + protected $casts = [ + 'total_amount' => 'decimal:2', + 'tax_amount' => 'decimal:2', + ]; + public function customer() { return $this->belongsTo(Customer::class); } + + public function taxRate() + { + return $this->belongsTo(TaxRate::class); + } + + public function calculateTax() + { + if (!$this->taxRate) { + return 0; + } + + $taxAmount = $this->total_amount * ($this->taxRate->rate / 100); + $this->tax_amount = $taxAmount; + return $taxAmount; + } + + public function getTotalWithTax() + { + return $this->total_amount + $this->tax_amount; + } } diff --git a/app/Models/TaxRate.php b/app/Models/TaxRate.php new file mode 100644 index 00000000..f03e61ae --- /dev/null +++ b/app/Models/TaxRate.php @@ -0,0 +1,44 @@ + + + 'float', + 'is_compound' => 'boolean', + 'is_active' => 'boolean' + ]; + + public function invoices() + { + return $this->hasMany(Invoice::class); + } + + public function customers() + { + return $this->belongsToMany(Customer::class); + } + + public function suppliers() + { + return $this->belongsToMany(Supplier::class); + } +} \ No newline at end of file diff --git a/database/migrations/2024_01_10_000000_create_tax_rates_table.php b/database/migrations/2024_01_10_000000_create_tax_rates_table.php new file mode 100644 index 00000000..1376a103 --- /dev/null +++ b/database/migrations/2024_01_10_000000_create_tax_rates_table.php @@ -0,0 +1,42 @@ + + +id('tax_rate_id'); + $table->string('name'); + $table->decimal('rate', 5, 2); + $table->text('description')->nullable(); + $table->boolean('is_compound')->default(false); + $table->boolean('is_active')->default(true); + $table->timestamps(); + }); + + Schema::create('customer_tax_rate', function (Blueprint $table) { + $table->foreignId('customer_id')->constrained('customers', 'customer_id'); + $table->foreignId('tax_rate_id')->constrained('tax_rates', 'tax_rate_id'); + $table->primary(['customer_id', 'tax_rate_id']); + }); + + Schema::create('supplier_tax_rate', function (Blueprint $table) { + $table->foreignId('supplier_id')->constrained('suppliers', 'supplier_id'); + $table->foreignId('tax_rate_id')->constrained('tax_rates', 'tax_rate_id'); + $table->primary(['supplier_id', 'tax_rate_id']); + }); + } + + public function down() + { + Schema::dropIfExists('supplier_tax_rate'); + Schema::dropIfExists('customer_tax_rate'); + Schema::dropIfExists('tax_rates'); + } +}; \ No newline at end of file