refactor: extract transaction creation and account syncing to actions

- Introduce CreateRecordTransactionsAction to handle transaction creation for any model
- Introduce SyncAccountsAction to encapsulate account synchronization logic
- Refactor CreateSaleAction to use new actions and handle full sale creation flow
- Simplify CreateExpense and CreateSale pages by delegating to actions
- Ensure proper transaction handling with database rollback on failure
This commit is contained in:
Jp
2026-02-16 01:48:25 +08:00
parent e04689acca
commit 7bcfaff311
5 changed files with 90 additions and 65 deletions

View File

@@ -2,21 +2,54 @@
namespace App\Actions\Sales; namespace App\Actions\Sales;
use App\Actions\Transactions\CreateRecordTransactionsAction;
use App\Commands\Sales\CreateSaleCommand; use App\Commands\Sales\CreateSaleCommand;
use App\Commands\Series\CreateSeriesCommand;
use App\Models\Sale; use App\Models\Sale;
use Illuminate\Support\Facades\DB;
class CreateSaleAction class CreateSaleAction
{ {
/** /**
* Create a new class instance. * Create a new class instance.
*/ */
public function __construct(private CreateSaleCommand $createSaleCommand) public function __construct(
{ private CreateSaleCommand $createSaleCommand,
// private CreateSeriesCommand $createSeriesCommand,
} ){ }
public function __invoke(array $data): Sale public function __invoke(array $data, array $transactions): Sale
{ {
return $this->createSaleCommand->execute($data); $record = $this->createSaleCommand->execute($data);
try {
DB::beginTransaction();
//create transactions for the sale
app(CreateRecordTransactionsAction::class)($record, $transactions);
$accountIds = collect($transactions)
->pluck('account_id')
->filter()
->unique()
->values()
->all();
//sync accounts to sale
app(SyncAccountsAction::class)($record, $accountIds);
//increment current_series
$this->createSeriesCommand->execute([
'branch_id' => $record->branch_id,
'series' => $record->reference_number,
]);
DB::commit();
} catch (\Exception $exception) {
DB::rollBack();
throw new \Exception('Failed to save transactions : '.$exception->getMessage());
}
return $record;
} }
} }

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Actions\Sales;
use App\Models\Sale;
use Illuminate\Support\Facades\DB;
class SyncAccountsAction
{
public function __invoke(Sale $sale, array $accounts): void
{
DB::transaction(function () use ($sale, $accounts) {
$sale->accounts()->sync($accounts);
}, attempts: 2);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Actions\Transactions;
use App\DataObjects\CreateTransactionDTO;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Pipeline;
class CreateRecordTransactionsAction
{
public function __invoke(Model $record, array $transactions): void
{
foreach ($transactions as $transaction) {
$tData = [
'branch_id' => $record->branch_id,
'happened_on' => $record->happened_on,
...$transaction,
];
$payload = new CreateTransactionDTO(data: $tData, transactionable: $record);
Pipeline::send(passable: $payload)->through(
[
CreateTransactionAction::class,
]
)->thenReturn();
}
}
}

View File

@@ -2,15 +2,13 @@
namespace App\Filament\Resources\ExpenseResource\Pages; namespace App\Filament\Resources\ExpenseResource\Pages;
use App\Actions\Transactions\CreateTransactionAction; use App\Actions\Transactions\CreateRecordTransactionsAction;
use App\DataObjects\CreateTransactionDTO;
use App\Filament\Resources\ClientResource; use App\Filament\Resources\ClientResource;
use App\Filament\Resources\ExpenseResource; use App\Filament\Resources\ExpenseResource;
use App\Models\Client; use App\Models\Client;
use Exception; use Exception;
use Filament\Resources\Pages\CreateRecord; use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Pipeline;
use Symfony\Component\Console\Exception\LogicException; use Symfony\Component\Console\Exception\LogicException;
class CreateExpense extends CreateRecord class CreateExpense extends CreateRecord
@@ -76,24 +74,7 @@ class CreateExpense extends CreateRecord
$transactions = $this->form->getState()['transactions'] ?? []; $transactions = $this->form->getState()['transactions'] ?? [];
try { try {
$branch = $this->getRecord()->branch; app(CreateRecordTransactionsAction::class)($this->getRecord(), $transactions);
foreach ($transactions as $transaction) {
$data = [
'branch_id' => $branch->id,
'happened_on' => $this->getRecord()->happened_on,
...$transaction,
];
$payload = new CreateTransactionDTO(data: $data, transactionable: $this->getRecord());
Pipeline::send(passable: $payload)->through(
[
CreateTransactionAction::class,
]
)->thenReturn();
}
$accountIds = collect($transactions) $accountIds = collect($transactions)
->pluck('account_id') ->pluck('account_id')

View File

@@ -3,8 +3,8 @@
namespace App\Filament\Resources\SaleResource\Pages; namespace App\Filament\Resources\SaleResource\Pages;
use App\Actions\Sales\CreateSaleAction; use App\Actions\Sales\CreateSaleAction;
use App\Actions\Transactions\CreateTransactionAction; use App\Actions\Sales\SyncAccountsAction;
use App\DataObjects\CreateTransactionDTO; use App\Actions\Transactions\CreateRecordTransactionsAction;
use App\Filament\Resources\ClientResource; use App\Filament\Resources\ClientResource;
use App\Filament\Resources\SaleResource; use App\Filament\Resources\SaleResource;
use App\Models\Branch; use App\Models\Branch;
@@ -15,7 +15,6 @@ use Filament\Resources\Pages\CreateRecord;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Pipeline;
class CreateSale extends CreateRecord class CreateSale extends CreateRecord
{ {
@@ -82,41 +81,7 @@ class CreateSale extends CreateRecord
public function processCreate(array $data, array $transactions): Model public function processCreate(array $data, array $transactions): Model
{ {
try { $record = app(CreateSaleAction::class)($this->getFormDataMutation($data), $transactions);
DB::beginTransaction();
$record = app(CreateSaleAction::class)($this->getFormDataMutation($data));
$branch = $record->branch;
foreach ($transactions as $transaction) {
$tData = [
'branch_id' => $branch->id,
'happened_on' => $record->happened_on,
...$transaction,
];
$payload = new CreateTransactionDTO(data: $tData, transactionable: $record);
Pipeline::send(passable: $payload)->through(
[
CreateTransactionAction::class,
]
)->thenReturn();
}
$accountIds = collect($transactions)
->pluck('account_id')
->filter()
->unique()
->values()
->all();
$record->accounts()->sync($accountIds);
DB::commit();
} catch (\Exception $exception) {
DB::rollBack();
throw new \Exception('Failed to save transactions : '.$exception->getMessage());
}
return $record; return $record;
} }