Logo of Sweep
Implement double entry accounting for filament 3 transaction resourceliberu-accounting/accounting-laravel#48

> > >

✓ Completed in 7 minutes, 3 months ago using GPT-4  •   Book a call  •   Report a bug


Progress

  Createapp/Rules/DoubleEntryValidator.php45a2fac 
1<?php
2
3namespace App\Rules;
4
5use Illuminate\Contracts\Validation\Rule;
6use Illuminate\Support\Facades\DB;
7use App\Models\Transaction;
8
9class DoubleEntryValidator implements Rule
10{
11    public function passes($attribute, $value)
12    {
13        $debitSum = request()->input('debit_account_id') ? Transaction::where('debit_account_id', request()->input('debit_account_id'))->sum('amount') : 0;
14        $creditSum = request()->input('credit_account_id') ? Transaction::where('credit_account_id', request()->input('credit_account_id'))->sum('amount') : 0;
15
16        // Adding current transaction amount to the respective sum based on the attribute being validated
17        if ($attribute === 'debit_account_id') {
18            $debitSum += $value;
19        } else if ($attribute === 'credit_account_id') {
20            $creditSum += $value;
21        }
22
23        return $debitSum == $creditSum;
24    }
25
26    public function message()
27    {
28        return 'The transaction does not adhere to double-entry accounting principles.';
29    }
30}
31
  • Create a new file DoubleEntryValidator.php in the app/Rules directory.
  • This file will contain a custom validation rule that ensures the sum of debit transactions equals the sum of credit transactions for each entry.
  • The validator will use Laravel's built-in validation rule structure and will be applied in the TransactionResource form submission.
  • Import necessary models and validation classes from Laravel at the top of the file.
  • Implement the passes and message methods as required by Laravel's custom validation rules.

Example structure:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class DoubleEntryValidator implements Rule
{
    public function passes($attribute, $value)
    {
        // Logic to check if the transaction adheres to double-entry principles
    }

    public function message()
    {
        return 'The transaction does not adhere to double-entry accounting principles.';
    }
}
  Run GitHub Actions forapp/Rules/DoubleEntryValidator.php 
  Modifyapp/Filament/Admin/Resources/TransactionResource.php 

Changed app/Filament/Admin/Resources/TransactionResource.php in b70528d    

15use Illuminate\Database\Eloquent\Builder;15use Illuminate\Database\Eloquent\Builder;
16use Filament\Forms\Components\BelongsToSelect;16use Filament\Forms\Components\BelongsToSelect;
17use Illuminate\Database\Eloquent\SoftDeletingScope;17use Illuminate\Database\Eloquent\SoftDeletingScope;
18use App\Rules\DoubleEntryValidator;
18use App\Filament\Admin\Resources\TransactionResource\Pages;19use App\Filament\Admin\Resources\TransactionResource\Pages;
19use App\Filament\Admin\Resources\TransactionResource\RelationManagers;20use App\Filament\Admin\Resources\TransactionResource\RelationManagers;
2021
...
34 ->label('Description'),35 ->label('Description'),
35 TextInput::make('amount')36 TextInput::make('amount')
36 ->numeric()37 ->numeric()
37 ->label('Amount'),38 ->label('Amount')
39 ->rules(['required', new DoubleEntryValidator()]),
38 BelongsToSelect::make('debit_account_id')40 BelongsToSelect::make('debit_account_id')
39 ->relationship('debitAccount', 'name')41 ->relationship('debitAccount', 'name')
40 ->label('Debit Account'),42 ->label('Debit Account'),
  • Import the DoubleEntryValidator at the top of the file.
  • In the form method, add the DoubleEntryValidator to the validation rules for the amount field. This ensures that every transaction adheres to double-entry accounting principles before being saved.
  • Update the comments to reflect the inclusion of double-entry validation.

Example modification:

use App\Rules\DoubleEntryValidator;

...

TextInput::make('amount')
    ->numeric()
    ->label('Amount')
    ->rules(['required', new DoubleEntryValidator()]),
  Modifyapp/Models/Transaction.php 

Changed app/Models/Transaction.php in 0eba22a    

17 'amount',17 'amount',
18 'debit_account_id',18 'debit_account_id',
19 'credit_account_id',19 'credit_account_id',
20 // Add any other necessary fields for double-entry accounting here
20 ];21 ];
2122
22 public function debitAccount()23 public function debitAccount()
  • In the Transaction model, ensure that the relationships with debit and credit accounts are correctly defined to support double-entry accounting.
  • Add any necessary methods or attributes that might be needed to support the validation logic in DoubleEntryValidator. For example, methods to calculate the total debit and credit amounts for a transaction.
  • Ensure the $fillable property includes all fields necessary for double-entry accounting.

Example modification:

protected $fillable = [
    'transaction_date',
    'transaction_description',
    'amount',
    'debit_account_id',
    'credit_account_id',
    // Add any other necessary fields
];
  Run GitHub Actions forapp/Models/Transaction.php 

Plan

This is based on the results of the Planning step. The plan may expand from failed GitHub Actions runs.

  Createapp/Rules/DoubleEntryValidator.php45a2fac 
1<?php
2
3namespace App\Rules;
4
5use Illuminate\Contracts\Validation\Rule;
6use Illuminate\Support\Facades\DB;
7use App\Models\Transaction;
8
9class DoubleEntryValidator implements Rule
10{
11    public function passes($attribute, $value)
12    {
13        $debitSum = request()->input('debit_account_id') ? Transaction::where('debit_account_id', request()->input('debit_account_id'))->sum('amount') : 0;
14        $creditSum = request()->input('credit_account_id') ? Transaction::where('credit_account_id', request()->input('credit_account_id'))->sum('amount') : 0;
15
16        // Adding current transaction amount to the respective sum based on the attribute being validated
17        if ($attribute === 'debit_account_id') {
18            $debitSum += $value;
19        } else if ($attribute === 'credit_account_id') {
20            $creditSum += $value;
21        }
22
23        return $debitSum == $creditSum;
24    }
25
26    public function message()
27    {
28        return 'The transaction does not adhere to double-entry accounting principles.';
29    }
30}
31
  Run GitHub Actions forapp/Rules/DoubleEntryValidator.php 
  Run GitHub Actions forapp/Models/Transaction.php 

Code Snippets Found

This is based on the results of the Searching step.

app/Filament/Admin/Resources/TransactionResource.php:20-97 
20
21class TransactionResource extends Resource
22{
23    protected static ?string $model = Transaction::class;
24
25    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
26
27    public static function form(Form $form): Form
28    {
29        return $form
30            ->schema([
31                DatePicker::make('transaction_date')
32                    ->label('Date'),
33                Textarea::make('transaction_description')
34                    ->label('Description'),
35                TextInput::make('amount')
36                    ->numeric()
37                    ->label('Amount'),
38                BelongsToSelect::make('debit_account_id')
39                    ->relationship('debitAccount', 'name')
40                    ->label('Debit Account'),
41                BelongsToSelect::make('credit_account_id')
42                    ->relationship('creditAccount', 'name')
43                    ->label('Credit Account'),
44            ]);
45    }
46
47    public static function table(Table $table): Table
48    {
49        return $table
50            ->columns([
51                TextColumn::make('transaction_date')
52                    ->label('Date')
53                    ->searchable()
54                    ->sortable(),
55                TextColumn::make('transaction_description')
56                    ->label('Description')
57                    ->searchable()
58                    ->sortable(),
59                TextColumn::make('amount')->label('Amount')
60                    ->searchable()
61                    ->sortable(),
62                TextColumn::make('debit_account_id')
63                    ->label('Debit Account')
64                    ->searchable()
65                    ->sortable(),
66                TextColumn::make('credit_account_id')
67                    ->label('Credit Account')
68                    ->searchable()
69                    ->sortable(),
70            ])
71            ->filters([
72                //
73            ])
74            ->actions([
75                Tables\Actions\EditAction::make(),
76            ])
77            ->bulkActions([
78                Tables\Actions\BulkActionGroup::make([
79                    Tables\Actions\DeleteBulkAction::make(),
80                ]),
81            ]);
82    }
83
84    public static function getRelations(): array
85    {
86        return [
87            //
88        ];
89    }
90
91    public static function getPages(): array
92    {
93        return [
94            'index' => Pages\ListTransactions::route('/'),
95            'create' => Pages\CreateTransaction::route('/create'),
96            'edit' => Pages\EditTransaction::route('/{record}/edit'),
97        ];
app/Models/Transaction.php:0-35 
1<?php
2
3namespace App\Models;
4
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Database\Eloquent\Model;
7
8class Transaction extends Model
9{
10    use HasFactory;
11
12    protected $primaryKey = 'transaction_id';
13    
14    protected $fillable = [
15        'transaction_date',
16        'transaction_description',
17        'amount',
18        'debit_account_id',
19        'credit_account_id',
20    ];
21
22    public function debitAccount()
23    {
24        return $this->belongsTo(Account::class, 'debit_account_id');
25    }
26
27    public function creditAccount()
28    {
29        return $this->belongsTo(Account::class, 'credit_account_id');
30    }
31
32    public function categories()
33    {
34        return $this->belongsToMany(Category::class);
35    }