diff --git a/app/TransactionRules/Actions/ConvertToWithdrawal.php b/app/TransactionRules/Actions/ConvertToWithdrawal.php index 3f6fbf940e..f9b2fca188 100644 --- a/app/TransactionRules/Actions/ConvertToWithdrawal.php +++ b/app/TransactionRules/Actions/ConvertToWithdrawal.php @@ -31,8 +31,9 @@ use FireflyIII\Models\AccountType; use FireflyIII\Models\RuleAction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; +use FireflyIII\User; use Log; - +use DB; /** * @@ -40,8 +41,7 @@ use Log; */ class ConvertToWithdrawal implements ActionInterface { - /** @var RuleAction The rule action */ - private $action; + private RuleAction $action; /** * TriggerInterface constructor. @@ -57,7 +57,8 @@ class ConvertToWithdrawal implements ActionInterface * Execute the action. * * @param TransactionJournal $journal - * + * @deprecated + * @codeCoverageIgnore * @return bool * @throws FireflyException */ @@ -121,7 +122,8 @@ class ConvertToWithdrawal implements ActionInterface * Is converted to a withdrawal from B to C. * * @param TransactionJournal $journal - * + * @deprecated + * @codeCoverageIgnore * @return bool * @throws FireflyException */ @@ -174,7 +176,8 @@ class ConvertToWithdrawal implements ActionInterface * Output is a withdrawal from A to C. * * @param TransactionJournal $journal - * + * @deprecated + * @codeCoverageIgnore * @return bool * @throws FireflyException */ @@ -211,11 +214,108 @@ class ConvertToWithdrawal implements ActionInterface return true; } + + /** + * Input is a transfer from A to B. + * Output is a withdrawal from A to C. + * + * @param array $journal + * @return bool + * @throws FireflyException + */ + private function convertTransferArray(array $journal): bool + { + + + // find or create expense account. + $user = User::find($journal['user_id']); + /** @var AccountFactory $factory */ + $factory = app(AccountFactory::class); + $factory->setUser($user); + + + $expenseName = '' === $this->action->action_value ? $journal['destination_account_name'] : $this->action->action_value; + $expense = $factory->findOrCreate($expenseName, AccountType::EXPENSE); + + Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $expenseName)); + + // update destination transaction(s) to be new expense account. + DB::table('transactions') + ->where('transaction_journal_id', '=', $journal['transaction_journal_id']) + ->where('amount', '>', 0) + ->update(['account_id' => $expense->id]); + + // change transaction type of journal: + $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + DB::table('transaction_journals') + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['transaction_type_id' => $newType->id]); + + Log::debug('Converted transfer to withdrawal.'); + + return true; + } + /** * @inheritDoc */ public function actOnArray(array $journal): bool { - // TODO: Implement actOnArray() method. + $type = $journal['transaction_type_type']; + if (TransactionType::WITHDRAWAL === $type) { + Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id)); + return false; + } + + if (TransactionType::DEPOSIT === $type) { + Log::debug('Going to transform a deposit to a withdrawal.'); + + return $this->convertDepositArray($journal); + } + if (TransactionType::TRANSFER === $type) { + Log::debug('Going to transform a transfer to a withdrawal.'); + + return $this->convertTransferArray($journal); + } + + return false; // @codeCoverageIgnore + } + + private function convertDepositArray(array $journal): bool + { + $user = User::find($journal['user_id']); + /** @var AccountFactory $factory */ + $factory = app(AccountFactory::class); + $factory->setUser($user); + + $expenseName = '' === $this->action->action_value ? $journal['source_account_name'] : $this->action->action_value; + $expense = $factory->findOrCreate($expenseName, AccountType::EXPENSE); + $destinationId = $journal['destination_account_id']; + + + Log::debug(sprintf('ConvertToWithdrawal. Action value is "%s", expense name is "%s"', $this->action->action_value, $expenseName)); + + // update source transaction(s) to be the original destination account + DB::table('transactions') + ->where('transaction_journal_id', '=', $journal['transaction_journal_id']) + ->where('amount', '<', 0) + ->update(['account_id' => $destinationId]); + + // update destination transaction(s) to be new expense account. + DB::table('transactions') + ->where('transaction_journal_id', '=', $journal['transaction_journal_id']) + ->where('amount', '>', 0) + ->update(['account_id' => $expense->id]); + + // change transaction type of journal: + $newType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + DB::table('transaction_journals') + ->where('id', '=', $journal['transaction_journal_id']) + ->update(['transaction_type_id' => $newType->id]); + + Log::debug('Converted deposit to withdrawal.'); + + return true; + } } diff --git a/tests/Unit/TransactionRules/Actions/ConvertToWithdrawalTest.php b/tests/Unit/TransactionRules/Actions/ConvertToWithdrawalTest.php index 0f31d3064a..c6b1c37636 100644 --- a/tests/Unit/TransactionRules/Actions/ConvertToWithdrawalTest.php +++ b/tests/Unit/TransactionRules/Actions/ConvertToWithdrawalTest.php @@ -26,8 +26,10 @@ namespace Tests\Unit\TransactionRules\Actions; use Exception; use FireflyIII\Factory\AccountFactory; +use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; use FireflyIII\Models\RuleAction; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; use FireflyIII\TransactionRules\Actions\ConvertToWithdrawal; use Log; @@ -47,9 +49,6 @@ class ConvertToWithdrawalTest extends TestCase */ public function setUp(): void { - self::markTestIncomplete('Incomplete for refactor.'); - - return; parent::setUp(); Log::info(sprintf('Now in %s.', get_class($this))); } @@ -61,25 +60,33 @@ class ConvertToWithdrawalTest extends TestCase */ public function testActDeposit() { - $expense = $this->getRandomExpense(); - $name = 'Random expense #' . $this->randomInt(); - $deposit = $this->getRandomDeposit(); + /** @var TransactionJournal $deposit */ + $deposit = $this->user()->transactionJournals()->where('description', 'Deposit for ConvertToWithdrawalTest.')->first(); + + // new expense account: + $name = sprintf('Random expense #%d', $this->randomInt()); + + // original destination: + $destination = Account::where('name','Checking Account')->first(); // journal is a deposit: $this->assertEquals(TransactionType::DEPOSIT, $deposit->transactionType->type); - // mock used stuff: - $factory = $this->mock(AccountFactory::class); - $factory->shouldReceive('setUser')->once(); - $factory->shouldReceive('findOrCreate')->once()->withArgs([$name, AccountType::EXPENSE])->andReturn($expense); - + // array: + $array = [ + 'user_id' => $this->user()->id, + 'transaction_journal_id' => $deposit->id, + 'transaction_type_type' => $deposit->transactionType->type, + 'source_account_name' => 'Boss', + 'destination_account_id' => $destination->id + ]; // fire the action: $ruleAction = new RuleAction; $ruleAction->action_value = $name; $action = new ConvertToWithdrawal($ruleAction); try { - $result = $action->act($deposit); + $result = $action->actOnArray($array); } catch (Exception $e) { Log::error($e->getMessage()); Log::error($e->getTraceAsString()); @@ -93,28 +100,39 @@ class ConvertToWithdrawalTest extends TestCase } /** - * Convert a transfer to a deposit. + * Convert a transfer to a withdrawal. * * @covers \FireflyIII\TransactionRules\Actions\ConvertToWithdrawal */ public function testActTransfer() { - $expense = $this->getRandomExpense(); - $name = 'Random expense #' . $this->randomInt(); - $transfer = $this->getRandomTransfer(); + /** @var TransactionJournal $transfer */ + $transfer = $this->user()->transactionJournals()->where('description', 'Transfer for ConvertToWithdrawalTest.')->first(); - // mock used stuff: - $factory = $this->mock(AccountFactory::class); - $factory->shouldReceive('setUser')->once(); - $factory->shouldReceive('findOrCreate')->once()->withArgs([$name, AccountType::EXPENSE])->andReturn($expense); + // new expense account: + $name = sprintf('Random new expense #%d', $this->randomInt()); + $destination = Account::whereName('Checking Account')->first(); + + // journal is a transfer: + $this->assertEquals(TransactionType::TRANSFER, $transfer->transactionType->type); + + // array: + $array = [ + 'user_id' => $this->user()->id, + 'transaction_journal_id' => $transfer->id, + 'transaction_type_type' => $transfer->transactionType->type, + 'destination_account_name' => $destination->name, + //'source_account_name' => 'Boss', + //'destination_account_id' => $destination->id + ]; // fire the action: $ruleAction = new RuleAction; $ruleAction->action_value = $name; $action = new ConvertToWithdrawal($ruleAction); try { - $result = $action->act($transfer); + $result = $action->actOnArray($array); } catch (Exception $e) { Log::error($e->getMessage()); Log::error($e->getTraceAsString()); @@ -122,7 +140,7 @@ class ConvertToWithdrawalTest extends TestCase } $this->assertTrue($result); - // journal is now a deposit. + // journal is now a withdrawal. $transfer->refresh(); $this->assertEquals(TransactionType::WITHDRAWAL, $transfer->transactionType->type); }