diff --git a/app/Models/Account.php b/app/Models/Account.php index b4c3487c8e..1e79b0aa2c 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -41,6 +41,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property int $id * @property string $name * @property string $iban + * @property AccountType $accountType */ class Account extends Model { diff --git a/app/Models/AccountType.php b/app/Models/AccountType.php index 5602821e59..9729d5a34f 100644 --- a/app/Models/AccountType.php +++ b/app/Models/AccountType.php @@ -28,6 +28,8 @@ use FireflyIII\Models\Account; /** * Class AccountType. + * @property string $type + * */ class AccountType extends Model { diff --git a/app/Support/Import/Routine/Spectre/StageImportDataHandler.php b/app/Support/Import/Routine/Spectre/StageImportDataHandler.php index fcaa5122c1..dd12d32255 100644 --- a/app/Support/Import/Routine/Spectre/StageImportDataHandler.php +++ b/app/Support/Import/Routine/Spectre/StageImportDataHandler.php @@ -61,7 +61,7 @@ class StageImportDataHandler $accounts = $config['accounts'] ?? []; Log::debug(sprintf('Count of accounts in array is %d', \count($accounts))); if (\count($accounts) === 0) { - throw new FireflyException('There are no accounts in this import job. Cannot continue.'); + throw new FireflyException('There are no accounts in this import job. Cannot continue.'); // @codeCoverageIgnore } $toImport = $config['account_mapping'] ?? []; foreach ($toImport as $spectreId => $localId) { @@ -217,10 +217,10 @@ class StageImportDataHandler { $account = $this->accountRepository->findNull($accountId); if (null === $account) { - throw new FireflyException(sprintf('Cannot find Firefly III asset account with ID #%d. Job must stop now.', $accountId)); + throw new FireflyException(sprintf('Cannot find Firefly III asset account with ID #%d. Job must stop now.', $accountId)); // @codeCoverageIgnore } if ($account->accountType->type !== AccountType::ASSET) { - throw new FireflyException(sprintf('Account with ID #%d is not an asset account. Job must stop now.', $accountId)); + throw new FireflyException(sprintf('Account with ID #%d is not an asset account. Job must stop now.', $accountId)); // @codeCoverageIgnore } return $account; @@ -242,7 +242,7 @@ class StageImportDataHandler return new SpectreAccount($account); } } - throw new FireflyException(sprintf('Cannot find Spectre account with ID #%d in configuration. Job will exit.', $accountId)); + throw new FireflyException(sprintf('Cannot find Spectre account with ID #%d in configuration. Job will exit.', $accountId)); // @codeCoverageIgnore } /** @@ -255,7 +255,9 @@ class StageImportDataHandler private function getTransactions(SpectreAccount $spectreAccount, LocalAccount $localAccount): array { // grab all transactions - $request = new ListTransactionsRequest($this->importJob->user); + /** @var ListTransactionsRequest $request */ + $request = app(ListTransactionsRequest::class); + $request->setUser($this->importJob->user); $request->setAccount($spectreAccount); $request->call(); diff --git a/tests/Unit/Support/Import/Routine/Spectre/StageImportDataHandlerTest.php b/tests/Unit/Support/Import/Routine/Spectre/StageImportDataHandlerTest.php new file mode 100644 index 0000000000..011ae81e33 --- /dev/null +++ b/tests/Unit/Support/Import/Routine/Spectre/StageImportDataHandlerTest.php @@ -0,0 +1,240 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Support\Import\Routine\Spectre; + + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Services\Spectre\Object\Account as SpectreAccount; +use FireflyIII\Services\Spectre\Object\Transaction as SpectreTransaction; +use FireflyIII\Services\Spectre\Request\ListTransactionsRequest; +use FireflyIII\Support\Import\Routine\File\OpposingAccountMapper; +use FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler; +use Mockery; +use Tests\TestCase; + +/** + * Class StageImportDataHandlerTest + */ +class StageImportDataHandlerTest extends TestCase +{ + /** + * @covers \FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler + */ + public function testRunBasic(): void + { + // needs to be a full spectre account this time. + $spectreAccount = new SpectreAccount( + [ + 'id' => 1234, + 'login_id' => 5678, + 'currency_code' => 'EUR', + 'balance' => 1000, + 'name' => 'Fake Spectre Account', + 'nature' => 'account', + 'created_at' => '2018-01-01 12:12:12', + 'updated_at' => '2018-01-01 12:12:12', + 'extra' => [], + ] + ); + + $today = new Carbon; + // create fake transactions: + $op1 = 'Some opposing account #' . random_int(1, 100); + $op2 = 'Some opposing revenue account #' . random_int(1, 100); + $transactions = [ + new SpectreTransaction( + [ + 'id' => 1, + 'mode' => 'mode', + 'status' => 'active', + 'made_on' => $today->toW3cString(), + 'amount' => -123.45, + 'currency_code' => 'EUR', + 'description' => 'Fake description #' . random_int(1, 100), + 'category' => 'some-category', + 'duplicated' => false, + 'extra' => [ + 'payee' => $op1, + ], + 'account_id' => 1234, + 'created_at' => $today->toW3cString(), + 'updated_at' => $today->toW3cString(), + ] + ), + new SpectreTransaction( + [ + 'id' => 2, + 'mode' => 'mode', + 'status' => 'active', + 'made_on' => $today->toW3cString(), + 'amount' => 563.21, + 'currency_code' => 'EUR', + 'description' => 'Fake second description #' . random_int(1, 100), + 'category' => 'some-other-category', + 'duplicated' => false, + 'extra' => [ + 'payee' => $op2, + ], + 'account_id' => 1234, + 'created_at' => $today->toW3cString(), + 'updated_at' => $today->toW3cString(), + ] + ), + ]; + + + $account = $this->user()->accounts()->where('account_type_id', 3)->first(); + $expense = $this->user()->accounts()->where('account_type_id', 4)->first(); + $revenue = $this->user()->accounts()->where('account_type_id', 5)->first(); + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'sid_a_' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = [ + 'accounts' => [$spectreAccount->toArray()], + 'account_mapping' => [ + 1234 => 322, + ], + ]; + $job->save(); + + // mock repositories + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $importRepos = $this->mock(ImportJobRepositoryInterface::class); + $lrRequest = $this->mock(ListTransactionsRequest::class); + $mapper = $this->mock(OpposingAccountMapper::class); + + // expected result + $expected = [ + 0 => [ + 'type' => 'withdrawal', + 'date' => $today->format('Y-m-d'), + 'tags' => ['mode', 'active'], + 'user' => $job->user_id, + 'notes' => "Imported from \"Fake Spectre Account\" \npayee: " . $op1." \n", + 'external_id' => '1', + // journal data: + 'description' => $transactions[0]->getDescription(), + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'bill_id' => null, + 'bill_name' => null, + + // transaction data: + 'transactions' => [ + [ + 'currency_id' => null, + 'currency_code' => 'EUR', + 'description' => null, + 'amount' => '-123.45', + 'budget_id' => null, + 'budget_name' => null, + 'category_id' => null, + 'category_name' => 'some-category', + 'source_id' => $account->id, + 'source_name' => null, + 'destination_id' => $expense->id, + 'destination_name' => null, + 'foreign_currency_id' => null, + 'foreign_currency_code' => null, + 'foreign_amount' => null, + 'reconciled' => false, + 'identifier' => 0, + ], + ], + ], + 1 => [ + 'type' => 'deposit', + 'date' => $today->format('Y-m-d'), + 'tags' => ['mode', 'active'], + 'user' => $job->user_id, + 'notes' => "Imported from \"Fake Spectre Account\" \npayee: " . $op2 . " \n", + 'external_id' => '2', + // journal data: + 'description' => $transactions[1]->getDescription(), + 'piggy_bank_id' => null, + 'piggy_bank_name' => null, + 'bill_id' => null, + 'bill_name' => null, + + // transaction data: + 'transactions' => [ + [ + 'currency_id' => null, + 'currency_code' => 'EUR', + 'description' => null, + 'amount' => '563.21', + 'budget_id' => null, + 'budget_name' => null, + 'category_id' => null, + 'category_name' => 'some-other-category', + 'source_id' => $revenue->id, + 'source_name' => null, + 'destination_id' => $account->id, + 'destination_name' => null, + 'foreign_currency_id' => null, + 'foreign_currency_code' => null, + 'foreign_amount' => null, + 'reconciled' => false, + 'identifier' => 0, + ], + ], + ], + ]; + $accountRepos->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('findNull')->once()->withArgs([322])->andReturn($account); + $importRepos->shouldReceive('setUser')->once(); + $importRepos->shouldReceive('setTransactions')->once() + ->withArgs([Mockery::any(), $expected]); + $lrRequest->shouldReceive('setUser')->once(); + $lrRequest->shouldReceive('setAccount')->once()->withArgs([Mockery::any()]); + $lrRequest->shouldReceive('call')->once(); + $lrRequest->shouldReceive('getTransactions')->once()->andReturn($transactions); + $mapper->shouldReceive('setUser')->once(); + // mapper should be called twice: + $mapper->shouldReceive('map')->withArgs( + [null, -123.45, ['name' => $op1, 'iban' => null, 'number' => null, 'bic' => null]] + )->once()->andReturn($expense); + $mapper->shouldReceive('map')->withArgs( + [null, 563.21, ['name' => $op2, 'iban' => null, 'number' => null, 'bic' => null]] + )->once()->andReturn($revenue); + + + $handler = new StageImportDataHandler; + $handler->setImportJob($job); + try { + $handler->run(); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + +} \ No newline at end of file