Andrew Wallo 1 year ago
parent
commit
2eabfe879e

+ 24
- 2
app/Filament/Company/Pages/Accounting/Transactions.php View File

@@ -10,6 +10,7 @@ use App\Facades\Accounting;
10 10
 use App\Filament\Company\Pages\Service\ConnectedAccount;
11 11
 use App\Filament\Forms\Components\DateRangeSelect;
12 12
 use App\Filament\Forms\Components\JournalEntryRepeater;
13
+use App\Filament\Tables\Actions\ReplicateBulkAction;
13 14
 use App\Models\Accounting\Account;
14 15
 use App\Models\Accounting\JournalEntry;
15 16
 use App\Models\Accounting\Transaction;
@@ -373,8 +374,17 @@ class Transactions extends Page implements HasTable
373 374
                     Tables\Actions\ReplicateAction::make()
374 375
                         ->excludeAttributes(['created_by', 'updated_by', 'created_at', 'updated_at'])
375 376
                         ->modal(false)
376
-                        ->beforeReplicaSaved(static function (Transaction $transaction) {
377
-                            $transaction->description = '(Copy of) ' . $transaction->description;
377
+                        ->beforeReplicaSaved(static function (Transaction $replica) {
378
+                            $replica->description = '(Copy of) ' . $replica->description;
379
+                        })
380
+                        ->after(static function (Transaction $original, Transaction $replica) {
381
+                            $original->journalEntries->each(function (JournalEntry $entry) use ($replica) {
382
+                                $entry->replicate([
383
+                                    'transaction_id',
384
+                                ])->fill([
385
+                                    'transaction_id' => $replica->id,
386
+                                ])->save();
387
+                            });
378 388
                         }),
379 389
                 ])
380 390
                     ->dropdownPlacement('bottom-start')
@@ -383,6 +393,18 @@ class Transactions extends Page implements HasTable
383 393
             ->bulkActions([
384 394
                 Tables\Actions\BulkActionGroup::make([
385 395
                     Tables\Actions\DeleteBulkAction::make(),
396
+                    ReplicateBulkAction::make()
397
+                        ->label('Replicate')
398
+                        ->modalWidth(MaxWidth::Large)
399
+                        ->modalDescription('Replicating transactions will also replicate their journal entries. Are you sure you want to proceed?')
400
+                        ->successNotificationTitle('Transactions Replicated Successfully')
401
+                        ->failureNotificationTitle('Failed to Replicate Transactions')
402
+                        ->deselectRecordsAfterCompletion()
403
+                        ->excludeAttributes(['created_by', 'updated_by', 'created_at', 'updated_at'])
404
+                        ->beforeReplicaSaved(static function (Transaction $replica) {
405
+                            $replica->description = '(Copy of) ' . $replica->description;
406
+                        })
407
+                        ->withReplicatedRelationships(['journalEntries']),
386 408
                 ]),
387 409
             ]);
388 410
     }

+ 114
- 0
app/Filament/Tables/Actions/ReplicateBulkAction.php View File

@@ -0,0 +1,114 @@
1
+<?php
2
+
3
+namespace App\Filament\Tables\Actions;
4
+
5
+use Closure;
6
+use Filament\Actions\Concerns\CanReplicateRecords;
7
+use Filament\Actions\Contracts\ReplicatesRecords;
8
+use Filament\Tables\Actions\BulkAction;
9
+use Illuminate\Database\Eloquent\Collection;
10
+use Illuminate\Database\Eloquent\Model;
11
+use Illuminate\Database\Eloquent\Relations\BelongsToMany;
12
+use Illuminate\Database\Eloquent\Relations\HasMany;
13
+use Illuminate\Database\Eloquent\Relations\HasOne;
14
+
15
+class ReplicateBulkAction extends BulkAction implements ReplicatesRecords
16
+{
17
+    use CanReplicateRecords;
18
+
19
+    protected ?Closure $afterReplicaSaved = null;
20
+
21
+    protected array $relationshipsToReplicate = [];
22
+
23
+    public static function getDefaultName(): ?string
24
+    {
25
+        return 'replicate';
26
+    }
27
+
28
+    protected function setUp(): void
29
+    {
30
+        parent::setUp();
31
+
32
+        $this->label(__('Replicate Selected'));
33
+
34
+        $this->modalHeading(fn (): string => __('Replicate selected :label', ['label' => $this->getPluralModelLabel()]));
35
+
36
+        $this->modalSubmitActionLabel(__('Replicate'));
37
+
38
+        $this->successNotificationTitle(__('Replicated'));
39
+
40
+        $this->icon('heroicon-m-square-3-stack-3d');
41
+
42
+        $this->requiresConfirmation();
43
+
44
+        $this->modalIcon('heroicon-o-square-3-stack-3d');
45
+
46
+        $this->action(function () {
47
+            $result = $this->process(function (Collection $records) {
48
+                $records->each(function (Model $record) {
49
+                    $this->replica = $record->replicate($this->getExcludedAttributes());
50
+
51
+                    $this->replica->fill($record->attributesToArray());
52
+
53
+                    $this->callBeforeReplicaSaved();
54
+
55
+                    $this->replica->save();
56
+
57
+                    $this->replicateRelationships($record, $this->replica);
58
+
59
+                    $this->callAfterReplicaSaved($record, $this->replica);
60
+                });
61
+            });
62
+
63
+            try {
64
+                return $result;
65
+            } finally {
66
+                $this->success();
67
+            }
68
+        });
69
+    }
70
+
71
+    public function replicateRelationships(Model $original, Model $replica): void
72
+    {
73
+        foreach ($this->relationshipsToReplicate as $relationship) {
74
+            $relation = $original->$relationship();
75
+
76
+            if ($relation instanceof BelongsToMany) {
77
+                $replica->$relationship()->sync($relation->pluck($relation->getRelated()->getKeyName()));
78
+            } elseif ($relation instanceof HasMany) {
79
+                $relation->each(function (Model $related) use ($replica, $relationship) {
80
+                    $relatedReplica = $related->replicate($this->getExcludedAttributes());
81
+                    $relatedReplica->{$replica->$relationship()->getForeignKeyName()} = $replica->getKey();
82
+                    $relatedReplica->save();
83
+                });
84
+            } elseif ($relation instanceof HasOne && $relation->exists()) {
85
+                $related = $relation->first();
86
+                $relatedReplica = $related->replicate($this->getExcludedAttributes());
87
+                $relatedReplica->{$replica->$relationship()->getForeignKeyName()} = $replica->getKey();
88
+                $relatedReplica->save();
89
+            }
90
+        }
91
+    }
92
+
93
+    public function withReplicatedRelationships(array $relationships): static
94
+    {
95
+        $this->relationshipsToReplicate = $relationships;
96
+
97
+        return $this;
98
+    }
99
+
100
+    public function afterReplicaSaved(Closure $callback): static
101
+    {
102
+        $this->afterReplicaSaved = $callback;
103
+
104
+        return $this;
105
+    }
106
+
107
+    public function callAfterReplicaSaved(Model $original, Model $replica): void
108
+    {
109
+        $this->evaluate($this->afterReplicaSaved, [
110
+            'original' => $original,
111
+            'replica' => $replica,
112
+        ]);
113
+    }
114
+}

+ 2
- 1
app/Observers/TransactionObserver.php View File

@@ -17,7 +17,8 @@ class TransactionObserver
17 17
      */
18 18
     public function created(Transaction $transaction): void
19 19
     {
20
-        if ($transaction->type->isJournal()) {
20
+        // Additional check to avoid duplication during replication
21
+        if ($transaction->journalEntries()->exists() || $transaction->type->isJournal() || str_starts_with($transaction->description, '(Copy of)')) {
21 22
             return;
22 23
         }
23 24
 

Loading…
Cancel
Save