Преглед изворни кода

refactor bank account/account relationship

3.x
Andrew Wallo пре 11 месеци
родитељ
комит
4cb82533f5

+ 9
- 11
app/Filament/Company/Pages/Accounting/Transactions.php Прегледај датотеку

@@ -770,19 +770,17 @@ class Transactions extends Page implements HasTable
770 770
 
771 771
     protected function getBankAccountOptions(?int $excludedAccountId = null, ?int $currentBankAccountId = null): array
772 772
     {
773
-        return BankAccount::join('accounts', 'accounts.bank_account_id', '=', 'bank_accounts.id')
774
-            ->where('accounts.archived', false)
775
-            ->select(['bank_accounts.id', 'accounts.name', 'accounts.subtype_id'])
776
-            ->with(['account.subtype' => static function ($query) {
773
+        return BankAccount::query()
774
+            ->whereHas('account', function (Builder $query) {
775
+                $query->where('archived', false);
776
+            })
777
+            ->with(['account' => function ($query) {
778
+                $query->where('archived', false);
779
+            }, 'account.subtype' => function ($query) {
777 780
                 $query->select(['id', 'name']);
778 781
             }])
779
-            ->when($excludedAccountId, function (Builder $query) use ($excludedAccountId) {
780
-                $query->whereNot('accounts.id', $excludedAccountId);
781
-            })
782
-            ->when($currentBankAccountId, function (Builder $query) use ($currentBankAccountId) {
783
-                // Ensure the current bank account is included even if archived
784
-                $query->orWhere('bank_accounts.id', $currentBankAccountId);
785
-            })
782
+            ->when($excludedAccountId, fn (Builder $query) => $query->where('account_id', '!=', $excludedAccountId))
783
+            ->when($currentBankAccountId, fn (Builder $query) => $query->orWhere('id', $currentBankAccountId))
786 784
             ->get()
787 785
             ->groupBy('account.subtype.name')
788 786
             ->map(fn (Collection $bankAccounts, string $subtype) => $bankAccounts->pluck('account.name', 'id'))

+ 3
- 3
app/Models/Accounting/Account.php Прегледај датотеку

@@ -18,6 +18,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
18 18
 use Illuminate\Database\Eloquent\Model;
19 19
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
20 20
 use Illuminate\Database\Eloquent\Relations\HasMany;
21
+use Illuminate\Database\Eloquent\Relations\HasOne;
21 22
 use Illuminate\Support\Carbon;
22 23
 
23 24
 #[ObservedBy(AccountObserver::class)]
@@ -41,7 +42,6 @@ class Account extends Model
41 42
         'description',
42 43
         'archived',
43 44
         'default',
44
-        'bank_account_id',
45 45
         'created_by',
46 46
         'updated_by',
47 47
     ];
@@ -74,9 +74,9 @@ class Account extends Model
74 74
         return $this->belongsTo(Currency::class, 'currency_code', 'code');
75 75
     }
76 76
 
77
-    public function bankAccount(): BelongsTo
77
+    public function bankAccount(): HasOne
78 78
     {
79
-        return $this->belongsTo(BankAccount::class, 'bank_account_id');
79
+        return $this->hasOne(BankAccount::class, 'account_id');
80 80
     }
81 81
 
82 82
     public function getLastTransactionDate(): ?string

+ 3
- 2
app/Models/Banking/BankAccount.php Прегледај датотеку

@@ -33,6 +33,7 @@ class BankAccount extends Model
33 33
 
34 34
     protected $fillable = [
35 35
         'company_id',
36
+        'account_id',
36 37
         'institution_id',
37 38
         'type',
38 39
         'number',
@@ -55,9 +56,9 @@ class BankAccount extends Model
55 56
         return $this->hasOne(ConnectedBankAccount::class, 'bank_account_id');
56 57
     }
57 58
 
58
-    public function account(): HasOne
59
+    public function account(): BelongsTo
59 60
     {
60
-        return $this->hasOne(Account::class, 'bank_account_id');
61
+        return $this->belongsTo(Account::class, 'account_id');
61 62
     }
62 63
 
63 64
     public function institution(): BelongsTo

+ 2
- 2
app/Services/AccountService.php Прегледај датотеку

@@ -226,8 +226,8 @@ class AccountService
226 226
             ->whereExists(function (\Illuminate\Database\Query\Builder $subQuery) {
227 227
                 $subQuery->select(DB::raw(1))
228 228
                     ->from('journal_entries as je')
229
-                    ->join('accounts as bank_accounts', 'bank_accounts.id', '=', 'je.account_id')
230
-                    ->whereNotNull('bank_accounts.bank_account_id')
229
+                    ->join('bank_accounts', 'bank_accounts.account_id', '=', 'je.account_id') // Join bank_accounts on account_id
230
+                    ->whereNotNull('bank_accounts.id') // Ensure there is a linked BankAccount
231 231
                     ->whereColumn('je.transaction_id', 'journal_entries.transaction_id');
232 232
             })
233 233
             ->groupBy('accounts.id')

+ 11
- 9
app/Services/ChartOfAccountsService.php Прегледај датотеку

@@ -48,7 +48,6 @@ class ChartOfAccountsService
48 48
     {
49 49
         if (isset($subtypeConfig['accounts']) && is_array($subtypeConfig['accounts'])) {
50 50
             $baseCode = $subtypeConfig['base_code'];
51
-
52 51
             $defaultCurrencyCode = CurrencyAccessor::getDefaultCurrency();
53 52
 
54 53
             if (empty($defaultCurrencyCode)) {
@@ -56,14 +55,8 @@ class ChartOfAccountsService
56 55
             }
57 56
 
58 57
             foreach ($subtypeConfig['accounts'] as $accountName => $accountDetails) {
59
-                $bankAccount = null;
60
-
61
-                if ($subtypeConfig['multi_currency'] && isset($subtypeConfig['bank_account_type'])) {
62
-                    $bankAccount = $this->createBankAccountForMultiCurrency($company, $subtypeConfig['bank_account_type']);
63
-                }
64
-
65
-                $company->accounts()->createQuietly([
66
-                    'bank_account_id' => $bankAccount?->id,
58
+                // Create the Account without directly setting bank_account_id
59
+                $account = $company->accounts()->createQuietly([
67 60
                     'subtype_id' => $subtype->id,
68 61
                     'category' => $subtype->type->getCategory()->value,
69 62
                     'type' => $subtype->type->value,
@@ -75,6 +68,15 @@ class ChartOfAccountsService
75 68
                     'created_by' => $company->owner->id,
76 69
                     'updated_by' => $company->owner->id,
77 70
                 ]);
71
+
72
+                // Check if we need to create a BankAccount for this Account
73
+                if ($subtypeConfig['multi_currency'] && isset($subtypeConfig['bank_account_type'])) {
74
+                    $bankAccount = $this->createBankAccountForMultiCurrency($company, $subtypeConfig['bank_account_type']);
75
+
76
+                    // Associate the BankAccount with the Account
77
+                    $bankAccount->account()->associate($account);
78
+                    $bankAccount->saveQuietly();
79
+                }
78 80
             }
79 81
         }
80 82
     }

+ 19
- 11
database/factories/Accounting/AccountFactory.php Прегледај датотеку

@@ -39,31 +39,39 @@ class AccountFactory extends Factory
39 39
 
40 40
     public function withBankAccount(string $name): static
41 41
     {
42
-        return $this->state(function (array $attributes) use ($name) {
43
-            $bankAccount = BankAccount::factory()->create();
42
+        return $this->afterCreating(function (Account $account) use ($name) {
44 43
             $accountSubtype = AccountSubtype::where('name', 'Cash and Cash Equivalents')->first();
45 44
 
46
-            return [
47
-                'bank_account_id' => $bankAccount->id,
45
+            // Create and associate a BankAccount with the Account
46
+            $bankAccount = BankAccount::factory()->create([
47
+                'account_id' => $account->id, // Associate with Account
48
+            ]);
49
+
50
+            // Update the Account with the subtype and name
51
+            $account->update([
48 52
                 'subtype_id' => $accountSubtype->id,
49 53
                 'name' => $name,
50
-            ];
54
+            ]);
51 55
         });
52 56
     }
53 57
 
54 58
     public function withForeignBankAccount(string $name, string $currencyCode, float $rate): static
55 59
     {
56
-        return $this->state(function (array $attributes) use ($currencyCode, $rate, $name) {
57
-            $currency = Currency::factory()->forCurrency($currencyCode, $rate)->create();
58
-            $bankAccount = BankAccount::factory()->create();
60
+        return $this->afterCreating(function (Account $account) use ($currencyCode, $rate, $name) {
59 61
             $accountSubtype = AccountSubtype::where('name', 'Cash and Cash Equivalents')->first();
60 62
 
61
-            return [
62
-                'bank_account_id' => $bankAccount->id,
63
+            // Create the Currency and BankAccount
64
+            $currency = Currency::factory()->forCurrency($currencyCode, $rate)->create();
65
+            $bankAccount = BankAccount::factory()->create([
66
+                'account_id' => $account->id, // Associate with Account
67
+            ]);
68
+
69
+            // Update the Account with the subtype, name, and currency code
70
+            $account->update([
63 71
                 'subtype_id' => $accountSubtype->id,
64 72
                 'name' => $name,
65 73
                 'currency_code' => $currency->code,
66
-            ];
74
+            ]);
67 75
         });
68 76
     }
69 77
 }

+ 13
- 13
database/migrations/2023_09_03_100000_create_accounting_tables.php Прегледај датотеку

@@ -36,18 +36,6 @@ return new class extends Migration
36 36
             $table->timestamps();
37 37
         });
38 38
 
39
-        Schema::create('bank_accounts', function (Blueprint $table) {
40
-            $table->id();
41
-            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
42
-            $table->foreignId('institution_id')->nullable()->constrained('institutions')->nullOnDelete();
43
-            $table->string('type')->default(BankAccountType::DEFAULT);
44
-            $table->string('number', 20)->nullable();
45
-            $table->boolean('enabled')->default(true);
46
-            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
47
-            $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
48
-            $table->timestamps();
49
-        });
50
-
51 39
         Schema::create('accounts', function (Blueprint $table) {
52 40
             $table->id();
53 41
             $table->foreignId('company_id')->constrained()->cascadeOnDelete();
@@ -61,7 +49,6 @@ return new class extends Migration
61 49
             $table->text('description')->nullable();
62 50
             $table->boolean('archived')->default(false);
63 51
             $table->boolean('default')->default(false);
64
-            $table->foreignId('bank_account_id')->nullable()->constrained('bank_accounts')->cascadeOnDelete();
65 52
             $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
66 53
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
67 54
             $table->timestamps();
@@ -69,6 +56,19 @@ return new class extends Migration
69 56
             $table->unique(['company_id', 'code']);
70 57
         });
71 58
 
59
+        Schema::create('bank_accounts', function (Blueprint $table) {
60
+            $table->id();
61
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
62
+            $table->foreignId('account_id')->nullable()->constrained('accounts')->nullOnDelete();
63
+            $table->foreignId('institution_id')->nullable()->constrained('institutions')->nullOnDelete();
64
+            $table->string('type')->default(BankAccountType::DEFAULT);
65
+            $table->string('number', 20)->nullable();
66
+            $table->boolean('enabled')->default(true);
67
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
68
+            $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
69
+            $table->timestamps();
70
+        });
71
+
72 72
         Schema::create('connected_bank_accounts', function (Blueprint $table) {
73 73
             $table->id();
74 74
             $table->foreignId('company_id')->constrained()->cascadeOnDelete();

+ 7
- 3
database/migrations/2024_11_13_225714_create_adjustmentables_table.php Прегледај датотеку

@@ -13,10 +13,14 @@ return new class extends Migration
13 13
     {
14 14
         Schema::create('adjustmentables', function (Blueprint $table) {
15 15
             $table->id();
16
-            $table->foreignId('adjustment_id')->constrained()->cascadeOnDelete();
17
-            $table->string('adjustment_type');
18
-            $table->morphs('adjustmentable');
16
+            $table->unsignedBigInteger('adjustment_id');   // No foreign key constraint due to polymorphism
17
+            $table->string('adjustment_type');             // Type of adjustment (e.g., "App\Models\Tax" or "App\Models\Discount")
18
+            $table->morphs('adjustmentable');              // Creates adjustmentable_id and adjustmentable_type
19 19
             $table->timestamps();
20
+
21
+            // Optional indexes for efficient querying
22
+            $table->index(['adjustment_id', 'adjustment_type']);
23
+            $table->index(['adjustmentable_id', 'adjustmentable_type']);
20 24
         });
21 25
     }
22 26
 

+ 6
- 0
tests/Feature/Accounting/TransactionTest.php Прегледај датотеку

@@ -132,6 +132,8 @@ it('handles multi-currency transfers correctly', function () {
132 132
         ->withForeignBankAccount('CAD Bank Account', 'CAD', 1.36)
133 133
         ->create();
134 134
 
135
+    $foreignBankAccount->refresh();
136
+
135 137
     ConfigureCurrencies::syncCurrencies();
136 138
 
137 139
     // Create a transfer of 1500 CAD from the foreign bank account to USD bank account
@@ -164,6 +166,8 @@ it('handles multi-currency deposits correctly', function () {
164 166
         ->withForeignBankAccount('BHD Bank Account', 'BHD', 0.38)
165 167
         ->create();
166 168
 
169
+    $foreignBankAccount->refresh();
170
+
167 171
     ConfigureCurrencies::syncCurrencies();
168 172
 
169 173
     // Create a deposit of 1500 BHD to the foreign bank account
@@ -195,6 +199,8 @@ it('handles multi-currency withdrawals correctly', function () {
195 199
         ->withForeignBankAccount('Foreign Bank Account', 'GBP', 0.76) // GBP account
196 200
         ->create();
197 201
 
202
+    $foreignBankAccount->refresh();
203
+
198 204
     ConfigureCurrencies::syncCurrencies();
199 205
 
200 206
     /** @var Transaction $transaction */

Loading…
Откажи
Сачувај