diff --git a/app/Filament/Pages/Dashboard.php b/app/Filament/Pages/Dashboard.php index e6bc611..d32813a 100644 --- a/app/Filament/Pages/Dashboard.php +++ b/app/Filament/Pages/Dashboard.php @@ -34,14 +34,13 @@ protected function getWidgets(): array { return [ AccountWidget::class, - FilamentInfoWidget::class, RevenueWidget::class, ]; } protected function getColumns(): int | string | array { - return 2; + return 1; } protected function getTitle(): string diff --git a/app/Filament/Resources/AccountResource.php b/app/Filament/Resources/AccountResource.php index f706c74..36fb63c 100644 --- a/app/Filament/Resources/AccountResource.php +++ b/app/Filament/Resources/AccountResource.php @@ -6,14 +6,17 @@ use App\Filament\Resources\AccountResource\RelationManagers; use App\Models\Account; use App\Models\AccountType; +use Filament\Tables\Actions\Action; use App\Models\Currency; use Filament\Forms; +use Filament\Forms\Components\Card; use Filament\Forms\Components\Grid; use Filament\Resources\Form; use Filament\Resources\Resource; use Filament\Resources\Table; use Filament\Tables; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletingScope; class AccountResource extends Resource @@ -22,6 +25,8 @@ class AccountResource extends Resource protected static ?string $navigationIcon = 'heroicon-o-cash'; + public ?Model $record = null; + public static function form(Form $form): Form { return $form @@ -71,6 +76,14 @@ public static function table(Table $table): Table Tables\Filters\TrashedFilter::make(), ]) ->actions([ + Action::make('detail') + ->label('Detail') + ->color('blue') + ->icon('heroicon-s-document') + ->hidden(fn ($record) => !$record->has_any_relation) + ->url(function ($record) { + return route('filament.resources.accounts.detail', ['record' => $record]); + }), Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make() @@ -85,6 +98,7 @@ public static function getPages(): array { return [ 'index' => Pages\ManageAccounts::route('/'), + 'detail' => Pages\AccountDetail::route('/{record}/detail'), ]; } diff --git a/app/Filament/Resources/AccountResource/Pages/AccountDetail.php b/app/Filament/Resources/AccountResource/Pages/AccountDetail.php new file mode 100644 index 0000000..ff35b7a --- /dev/null +++ b/app/Filament/Resources/AccountResource/Pages/AccountDetail.php @@ -0,0 +1,34 @@ +record = $record; + $this->account = Account::find($record); + } +} diff --git a/app/Filament/Resources/RevenueResource.php b/app/Filament/Resources/RevenueResource.php index 486f65e..d44aa0e 100644 --- a/app/Filament/Resources/RevenueResource.php +++ b/app/Filament/Resources/RevenueResource.php @@ -8,6 +8,7 @@ use App\Filament\Resources\RevenueResource\Widgets\RevenueWidget; use App\Models\Account; use App\Models\Company; +use App\Models\Invoice; use App\Models\Revenue; use Carbon\Carbon; use Closure; diff --git a/app/Http/Livewire/Account/ExpenseTable.php b/app/Http/Livewire/Account/ExpenseTable.php new file mode 100644 index 0000000..4e4758b --- /dev/null +++ b/app/Http/Livewire/Account/ExpenseTable.php @@ -0,0 +1,105 @@ +where('account_id', $this->accountID); + } + + protected function getTableColumns(): array + { + return [ + Tables\Columns\TextColumn::make('due_at') + ->label('Date') + ->sortable() + ->dateTime('d/m/Y'), + Tables\Columns\TextColumn::make('description') + ->searchable(), + Tables\Columns\TextColumn::make('company.name') + ->searchable() + ->label('Company'), + Tables\Columns\TextColumn::make('corporation.name') + ->searchable() + ->label('Corporation'), + Tables\Columns\TextColumn::make('amount_with_currency') + ->label('Amount') + ->formatStateUsing(fn ($state) => '-' . $state) + ->sortable(), + ]; + } + + protected function getTableFilters(): array + { + return [ + Filter::make('due_at') + ->form([ + Forms\Components\DatePicker::make('due_from') + ->default(Carbon::now()->subYear()) + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('From Date'), + Forms\Components\DatePicker::make('due_until') + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('To Date') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['due_from'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '>=', $date), + ) + ->when( + $data['due_until'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '<=', $date), + ); + }), + Filter::make('amount') + ->form([ + Forms\Components\TextInput::make('min_amount') + ->label('Min Amount'), + Forms\Components\TextInput::make('max_amount') + ->label('Max Amount') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['min_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '>=', $amount), + ) + ->when( + $data['max_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '<=', $amount), + ); + }), + ]; + } + + protected function getTableFiltersFormColumns(): int + { + return 2; + } + + public function render() + { + return view('livewire.account.expense-table'); + } +} diff --git a/app/Http/Livewire/Account/RevenueTable.php b/app/Http/Livewire/Account/RevenueTable.php new file mode 100644 index 0000000..76594a6 --- /dev/null +++ b/app/Http/Livewire/Account/RevenueTable.php @@ -0,0 +1,104 @@ +where('account_id', $this->accountID); + } + + protected function getTableColumns(): array + { + return [ + Tables\Columns\TextColumn::make('due_at') + ->label('Date') + ->sortable() + ->dateTime('d/m/Y'), + Tables\Columns\TextColumn::make('description') + ->searchable(), + Tables\Columns\TextColumn::make('company.name') + ->searchable() + ->label('Company'), + Tables\Columns\TextColumn::make('corporation.name') + ->searchable() + ->label('Corporation'), + Tables\Columns\TextColumn::make('amount_with_currency') + ->label('Amount') + ->sortable(), + ]; + } + + protected function getTableFilters(): array + { + return [ + Filter::make('due_at') + ->form([ + Forms\Components\DatePicker::make('due_from') + ->default(Carbon::now()->subYear()) + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('From Date'), + Forms\Components\DatePicker::make('due_until') + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('To Date') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['due_from'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '>=', $date), + ) + ->when( + $data['due_until'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '<=', $date), + ); + }), + Filter::make('amount') + ->form([ + Forms\Components\TextInput::make('min_amount') + ->label('Min Amount'), + Forms\Components\TextInput::make('max_amount') + ->label('Max Amount') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['min_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '>=', $amount), + ) + ->when( + $data['max_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '<=', $amount), + ); + }), + ]; + } + + protected function getTableFiltersFormColumns(): int + { + return 2; + } + + public function render(): View + { + return view('livewire.account.revenue-table'); + } +} diff --git a/app/Http/Livewire/Account/TransactionTable.php b/app/Http/Livewire/Account/TransactionTable.php new file mode 100644 index 0000000..86c4448 --- /dev/null +++ b/app/Http/Livewire/Account/TransactionTable.php @@ -0,0 +1,106 @@ +where('to_account_id', $this->accountID); + $outgoing = Transaction::query()->where('from_account_id', $this->accountID); + return $incoming->union($outgoing); + } + + protected function getTableColumns(): array + { + return [ + Tables\Columns\TextColumn::make('due_at') + ->label('Date') + ->sortable() + ->dateTime('d/m/Y'), + Tables\Columns\TextColumn::make('description') + ->searchable(), + Tables\Columns\TextColumn::make('from_account.name') + ->searchable() + ->label('Sender Account'), + Tables\Columns\TextColumn::make('to_account.name') + ->searchable() + ->label('Receiver Account'), + Tables\Columns\TextColumn::make('amount_with_currency') + ->label('Amount') + ->formatStateUsing(fn (?Model $record, $state) => $record->from_account_id !== $this->accountID ? '-' . $state : $state) + ]; + } + + protected function getTableFilters(): array + { + return [ + Filter::make('due_at') + ->form([ + Forms\Components\DatePicker::make('due_from') + ->default(Carbon::now()->subYear()) + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('From Date'), + Forms\Components\DatePicker::make('due_until') + ->closeOnDateSelection() + ->timezone('Europe/Istanbul') + ->label('To Date') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['due_from'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '>=', $date), + ) + ->when( + $data['due_until'], + fn (Builder $query, $date): Builder => $query->whereDate('due_at', '<=', $date), + ); + }), + Filter::make('amount') + ->form([ + Forms\Components\TextInput::make('min_amount') + ->label('Min Amount'), + Forms\Components\TextInput::make('max_amount') + ->label('Max Amount') + ]) + ->query(function (Builder $query, array $data): Builder { + return $query + ->when( + $data['min_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '>=', $amount), + ) + ->when( + $data['max_amount'], + fn (Builder $query, $amount): Builder => $query->where('amount', '<=', $amount), + ); + }), + ]; + } + + protected function getTableFiltersFormColumns(): int + { + return 2; + } + + public function render() + { + return view('livewire.account.transaction-table'); + } +} diff --git a/app/Models/Account.php b/app/Models/Account.php index 1a89e3d..996d6d2 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -2,6 +2,8 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; @@ -55,7 +57,13 @@ public function incomingTransactions() public function getBalanceAttribute() { - return $this->starting_balance + $this->revenues()->sum('amount') - $this->expenses()->sum('amount') + $this->incomingTransactions()->sum('amount') - $this->outgoingTransactions()->sum('amount'); + $balance = $this->starting_balance + $this->revenues()->sum('amount') - $this->expenses()->sum('amount') + $this->incomingTransactions()->sum('amount') - $this->outgoingTransactions()->sum('amount'); + + if ($this->currency->position == 'right') { + return number_format($balance, 2) . ' ' . $this->currency->symbol; + } else { + return $this->currency->symbol . ' ' . number_format($balance, 2); + } } public function getHasAnyRelationAttribute() diff --git a/app/Models/Expense.php b/app/Models/Expense.php index b430a99..c4b81a0 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -26,7 +26,9 @@ class Expense extends Model protected $appends = [ 'has_any_relation', - 'bill_number' + 'bill_number', + 'transaction_type', + 'amount_with_currency' ]; public function account() @@ -63,4 +65,13 @@ public function getBillNumberAttribute() { return $this->bills()->first()->number ?? null; } + + public function getAmountWithCurrencyAttribute() + { + if ($this->corporation->currency->position === "right") { + return number_format($this->amount, 2) . " " . $this->corporation->currency->symbol; + } else { + return $this->corporation->currency->symbol . " " . number_format($this->amount, 2); + } + } } diff --git a/app/Models/Revenue.php b/app/Models/Revenue.php index 402215c..64e5f57 100644 --- a/app/Models/Revenue.php +++ b/app/Models/Revenue.php @@ -26,7 +26,9 @@ class Revenue extends Model protected $appends = [ 'has_any_relation', - 'invoice_number' + 'invoice_number', + 'transaction_type', + 'amount_with_currency' ]; public function account() @@ -63,4 +65,13 @@ public function getInvoiceNumberAttribute() { return $this->invoices()->first()->number ?? null; } + + public function getAmountWithCurrencyAttribute() + { + if ($this->corporation->currency->position === "right") { + return number_format($this->amount, 2) . " " . $this->corporation->currency->symbol; + } else { + return $this->corporation->currency->symbol . " " . number_format($this->amount, 2); + } + } } diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 59ab351..99967f6 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -11,6 +11,16 @@ class Transaction extends Model protected $fillable = ['from_account_id', 'to_account_id', 'amount', 'description', 'due_at']; + protected $appends = [ + 'transaction_type', + 'amount_with_currency' + ]; + + protected $casts = [ + 'due_at' => 'date' + ]; + + public function from_account() { return $this->belongsTo(Account::class, 'from_account_id'); @@ -20,4 +30,18 @@ public function to_account() { return $this->belongsTo(Account::class, 'to_account_id'); } + + public function getTransactionTypeAttribute() + { + return $this->from_account_id || $this->to_account_id ? 'transaction' : ''; + } + + public function getAmountWithCurrencyAttribute() + { + if ($this->from_account->currency->position === "right") { + return number_format($this->amount, 2) . " " . $this->from_account->currency->symbol; + } else { + return $this->from_account->currency->symbol . " " . number_format($this->amount, 2); + } + } } diff --git a/composer.json b/composer.json index 72e63c5..e4d21a6 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "laravel/sanctum": "^3.2", "laravel/tinker": "^2.8", "livewire/livewire": "^2.12", - "marjose123/filament-lockscreen": "^1.1" + "marjose123/filament-lockscreen": "^1.1", + "webbingbrasil/filament-datefilter": "^1.1" }, "require-dev": { "doctrine/dbal": "^3.6", diff --git a/composer.lock b/composer.lock index c780209..d6b21c2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0a09f2476a72cce462cd629c22798c22", + "content-hash": "d8d29ad962646faca1c7ccad318b842e", "packages": [ { "name": "akaunting/laravel-money", @@ -7178,6 +7178,54 @@ ], "time": "2022-03-08T17:03:00+00:00" }, + { + "name": "webbingbrasil/filament-datefilter", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/webbingbrasil/filament-datefilter.git", + "reference": "3613479d857ee5192966b9450441b577a3d1ed1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webbingbrasil/filament-datefilter/zipball/3613479d857ee5192966b9450441b577a3d1ed1f", + "reference": "3613479d857ee5192966b9450441b577a3d1ed1f", + "shasum": "" + }, + "require": { + "filament/tables": "^2.15", + "php": "^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Webbingbrasil\\FilamentDateFilter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Danilo Andrade", + "email": "danilo@webbingbrasil.com.br", + "role": "Developer" + } + ], + "description": "Date filter component for filament tables.", + "homepage": "https://github.com/webbingbrasil/filament-datefilter", + "keywords": [ + "filament", + "filter", + "laravel" + ], + "support": { + "issues": "https://github.com/webbingbrasil/filament-datefilter/issues", + "source": "https://github.com/webbingbrasil/filament-datefilter/tree/1.1.1" + }, + "time": "2022-10-24T21:46:22+00:00" + }, { "name": "webmozart/assert", "version": "1.11.0", diff --git a/resources/views/filament/resources/account-resource/pages/account-detail.blade.php b/resources/views/filament/resources/account-resource/pages/account-detail.blade.php new file mode 100644 index 0000000..2ac499c --- /dev/null +++ b/resources/views/filament/resources/account-resource/pages/account-detail.blade.php @@ -0,0 +1,18 @@ + + +
+
+ Total Balance +
+
+ {{ $account->balance }} +
+
+

Revenues

+ +

Expenses

+ +

Transactions

+ +
diff --git a/resources/views/livewire/account/expense-table.blade.php b/resources/views/livewire/account/expense-table.blade.php new file mode 100644 index 0000000..c3fedc4 --- /dev/null +++ b/resources/views/livewire/account/expense-table.blade.php @@ -0,0 +1,3 @@ +
+ {{ $this->table }} +
diff --git a/resources/views/livewire/account/revenue-table.blade.php b/resources/views/livewire/account/revenue-table.blade.php new file mode 100644 index 0000000..c3fedc4 --- /dev/null +++ b/resources/views/livewire/account/revenue-table.blade.php @@ -0,0 +1,3 @@ +
+ {{ $this->table }} +
diff --git a/resources/views/livewire/account/transaction-table.blade.php b/resources/views/livewire/account/transaction-table.blade.php new file mode 100644 index 0000000..c3fedc4 --- /dev/null +++ b/resources/views/livewire/account/transaction-table.blade.php @@ -0,0 +1,3 @@ +
+ {{ $this->table }} +