diff --git a/app/Http/Controllers/Import/JobConfigurationController.php b/app/Http/Controllers/Import/JobConfigurationController.php index 6e95654d49..518072703b 100644 --- a/app/Http/Controllers/Import/JobConfigurationController.php +++ b/app/Http/Controllers/Import/JobConfigurationController.php @@ -178,7 +178,7 @@ class JobConfigurationController extends Controller Log::debug(sprintf('Going to create class "%s"', $className)); /** @var JobConfigurationInterface $configurator */ $configurator = app($className); - $configurator->setJob($importJob); + $configurator->setImportJob($importJob); return $configurator; } diff --git a/app/Http/Controllers/Import/JobStatusController.php b/app/Http/Controllers/Import/JobStatusController.php index d86faa35c9..cefd1d43ba 100644 --- a/app/Http/Controllers/Import/JobStatusController.php +++ b/app/Http/Controllers/Import/JobStatusController.php @@ -143,7 +143,7 @@ class JobStatusController extends Controller /** @var RoutineInterface $routine */ $routine = app($className); - $routine->setJob($importJob); + $routine->setImportJob($importJob); try { $routine->run(); } catch (FireflyException|Exception $e) { @@ -213,7 +213,7 @@ class JobStatusController extends Controller { /** @var ImportArrayStorage $storage */ $storage = app(ImportArrayStorage::class); - $storage->setJob($importJob); + $storage->setImportJob($importJob); try { $storage->store(); } catch (FireflyException|Exception $e) { diff --git a/app/Import/FileProcessor/CsvProcessor.php b/app/Import/FileProcessor/CsvProcessor.php deleted file mode 100644 index deba0399d5..0000000000 --- a/app/Import/FileProcessor/CsvProcessor.php +++ /dev/null @@ -1,377 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Import\FileProcessor; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Import\Object\ImportJournal; -use FireflyIII\Import\Specifics\SpecificInterface; -use FireflyIII\Models\ImportJob; -use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use Illuminate\Support\Collection; -use League\Csv\Reader; -use League\Csv\Statement; -use Log; - -/** - * @deprecated - * @codeCoverageIgnore - * - * Class CsvProcessor, as the name suggests, goes over CSV file line by line and creates - * "ImportJournal" objects, which are used in another step to create new journals and transactions - * and what-not. - */ -class CsvProcessor implements FileProcessorInterface -{ - /** @var ImportJob */ - private $job; - /** @var Collection */ - private $objects; - /** @var ImportJobRepositoryInterface */ - private $repository; - /** @var array */ - private $validConverters; - /** @var array */ - private $validSpecifics; - - /** - * FileProcessorInterface constructor. - */ - public function __construct() - { - $this->objects = new Collection; - $this->validSpecifics = array_keys(config('csv.import_specifics')); - $this->validConverters = array_keys(config('csv.import_roles')); - } - - /** - * @return Collection - * - * @throws FireflyException - */ - public function getObjects(): Collection - { - if (null === $this->job) { - throw new FireflyException('Cannot call getObjects() without a job.'); - } - - return $this->objects; - } - - /** - * Does the actual job. - * - * @return bool - * - * @throws FireflyException - * @throws \League\Csv\Exception - */ - public function run(): bool - { - if (null === $this->job) { - throw new FireflyException('Cannot call run() without a job.'); - } - Log::debug('Now in CsvProcessor run(). Job is now running...'); - - $entries = new Collection($this->getImportArray()); - $this->addStep(); - Log::notice('Building importable objects from CSV file.'); - Log::debug(sprintf('Number of entries: %d', $entries->count())); - $notImported = $entries->filter( - function (array $row, int $index) { - $row = array_values($row); - if ($this->rowAlreadyImported($row)) { - $message = sprintf('Row #%d has already been imported.', $index); - $this->repository->addError($this->job, $index, $message); - Log::info($message); - - return null; - } - - return $row; - } - ); - $this->addStep(); - Log::debug(sprintf('Number of entries left: %d', $notImported->count())); - - $notImported->each( - function (array $row, int $index) { - $journal = $this->importRow($index, $row); - $this->objects->push($journal); - } - ); - $this->addStep(); - - return true; - } - - /** - * Shorthand method to set the extended status. - * - * @codeCoverageIgnore - * - * @param array $array - */ - public function setExtendedStatus(array $array): void - { - $this->repository->setExtendedStatus($this->job, $array); - } - - /** - * Set import job for this processor. - * - * @param ImportJob $job - * - * @return FileProcessorInterface - */ - public function setJob(ImportJob $job): FileProcessorInterface - { - $this->job = $job; - $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); - - return $this; - } - - /** - * Shorthand method to add a step. - * - * @codeCoverageIgnore - */ - private function addStep() - { - $this->repository->addStepsDone($this->job, 1); - } - - /** - * Add meta data to the individual value and verify that it can be handled in a later stage. - * - * @param int $index - * @param string $value - * - * @return array - * - * @throws FireflyException - */ - private function annotateValue(int $index, string $value) - { - $config = $this->getConfig(); - $role = $config['column-roles'][$index] ?? '_ignore'; - $mapped = $config['column-mapping-config'][$index][$value] ?? null; - - // throw error when not a valid converter. - if (!\in_array($role, $this->validConverters)) { - throw new FireflyException(sprintf('"%s" is not a valid role.', $role)); - } - - $entry = [ - 'role' => $role, - 'value' => $value, - 'mapped' => $mapped, - ]; - - return $entry; - } - - /** - * Shorthand method to return configuration. - * - * @codeCoverageIgnore - * - * @return array - */ - private function getConfig(): array - { - return $this->repository->getConfiguration($this->job); - } - - /** - * @return array - * - * @throws \League\Csv\Exception - * @throws \League\Csv\Exception - */ - private function getImportArray(): array - { - $content = $this->repository->uploadFileContents($this->job); - $config = $this->getConfig(); - $reader = Reader::createFromString($content); - $delimiter = $config['delimiter'] ?? ','; - $hasHeaders = $config['has-headers'] ?? false; - $offset = 0; - if ('tab' === $delimiter) { - $delimiter = "\t"; // @codeCoverageIgnore - } - $reader->setDelimiter($delimiter); - if ($hasHeaders) { - $offset = 1; - } - $stmt = (new Statement)->offset($offset); - $records = $stmt->process($reader); - $return = []; - foreach ($records as $record) { - $return[] = $record; - } - Log::debug('Created a CSV reader.'); - - return $return; - } - - /** - * Will return string representation of JSON error code. - * - * @param int $jsonError - * - * @codeCoverageIgnore - * - * @return string - */ - private function getJsonError(int $jsonError): string - { - $messages = [ - JSON_ERROR_NONE => 'No JSON error', - JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded.', - JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON.', - JSON_ERROR_CTRL_CHAR => 'Control character error, possibly incorrectly encoded.', - JSON_ERROR_SYNTAX => 'Syntax error.', - JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded.', - JSON_ERROR_RECURSION => 'One or more recursive references in the value to be encoded.', - JSON_ERROR_INF_OR_NAN => 'One or more NAN or INF values in the value to be encoded.', - JSON_ERROR_UNSUPPORTED_TYPE => 'A value of a type that cannot be encoded was given.', - JSON_ERROR_INVALID_PROPERTY_NAME => 'A property name that cannot be encoded was given.', - JSON_ERROR_UTF16 => 'Malformed UTF-16 characters, possibly incorrectly encoded.', - ]; - if (isset($messages[$jsonError])) { - return $messages[$jsonError]; - } - - return 'Unknown JSON error'; - } - - /** - * Hash an array and return the result. - * - * @param array $array - * - * @return string - * - * @throws FireflyException - */ - private function getRowHash(array $array): string - { - $json = json_encode($array); - $jsonError = json_last_error(); - - if (false === $json) { - throw new FireflyException(sprintf('Error while encoding JSON for CSV row: %s', $this->getJsonError($jsonError))); // @codeCoverageIgnore - } - - return hash('sha256', $json); - } - - /** - * Take a row, build import journal by annotating each value and storing it in the import journal. - * - * @param int $index - * @param array $row - * - * @return ImportJournal - * - * @throws FireflyException - */ - private function importRow(int $index, array $row): ImportJournal - { - $row = array_values($row); - Log::debug(sprintf('Now at row %d', $index)); - $row = $this->specifics($row); - $hash = $this->getRowHash($row); - $config = $this->getConfig(); - - $journal = new ImportJournal; - $journal->setUser($this->job->user); - $journal->setHash($hash); - - /** - * @var int - * @var string $value - */ - foreach ($row as $rowIndex => $value) { - $value = trim((string)$value); - if (\strlen($value) > 0) { - $annotated = $this->annotateValue($rowIndex, $value); - Log::debug('Annotated value', $annotated); - $journal->setValue($annotated); - } - } - // set some extra info: - $importAccount = (int)($config['import-account'] ?? 0.0); - $journal->asset->setDefaultAccountId($importAccount); - - return $journal; - } - - /** - * Checks if the row has not been imported before. - * - * @param array $array - * - * @return bool - * - * @throws FireflyException - */ - private function rowAlreadyImported(array $array): bool - { - $hash = $this->getRowHash($array); - $count = $this->repository->countByHash($hash); - Log::debug(sprintf('Hash is %s and count is %d', $hash, $count)); - - return $count > 0; - } - - /** - * And this is the point where the specifix go to work. - * - * @param array $row - * - * @return array - * - * @throws FireflyException - */ - private function specifics(array $row): array - { - $config = $this->getConfig(); - $names = array_keys($config['specifics'] ?? []); - foreach ($names as $name) { - if (!\in_array($name, $this->validSpecifics)) { - throw new FireflyException(sprintf('"%s" is not a valid class name', $name)); - } - - /** @var SpecificInterface $specific */ - $specific = app('FireflyIII\Import\Specifics\\' . $name); - - // it returns the row, possibly modified: - $row = $specific->run($row); - } - - return $row; - } -} diff --git a/app/Import/FileProcessor/FileProcessorInterface.php b/app/Import/FileProcessor/FileProcessorInterface.php deleted file mode 100644 index 7b6de00ade..0000000000 --- a/app/Import/FileProcessor/FileProcessorInterface.php +++ /dev/null @@ -1,49 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Import\FileProcessor; - -use FireflyIII\Models\ImportJob; -use Illuminate\Support\Collection; - -/** - * Interface FileProcessorInterface. - */ -interface FileProcessorInterface -{ - /** - * @return Collection - */ - public function getObjects(): Collection; - - /** - * @return bool - */ - public function run(): bool; - - /** - * @param ImportJob $job - * - * @return FileProcessorInterface - */ - public function setJob(ImportJob $job): FileProcessorInterface; -} diff --git a/app/Import/JobConfiguration/FakeJobConfiguration.php b/app/Import/JobConfiguration/FakeJobConfiguration.php index 657546944e..6899bee71a 100644 --- a/app/Import/JobConfiguration/FakeJobConfiguration.php +++ b/app/Import/JobConfiguration/FakeJobConfiguration.php @@ -33,7 +33,7 @@ use Illuminate\Support\MessageBag; class FakeJobConfiguration implements JobConfigurationInterface { /** @var ImportJob */ - private $job; + private $importJob; /** @var ImportJobRepositoryInterface */ private $repository; @@ -57,8 +57,8 @@ class FakeJobConfiguration implements JobConfigurationInterface // 'artist' must be 'david bowie', case insensitive // 'song' must be 'golden years', case insensitive. // if stage is not "new", then album must be 'station to station' - $config = $this->job->configuration; - if ($this->job->stage === 'new') { + $config = $this->importJob->configuration; + if ($this->importJob->stage === 'new') { return (isset($config['artist']) && 'david bowie' === strtolower($config['artist'])) && (isset($config['song']) && 'golden years' === strtolower($config['song'])) && isset($config['apply-rules']); @@ -82,7 +82,7 @@ class FakeJobConfiguration implements JobConfigurationInterface $song = strtolower($data['song'] ?? ''); $album = strtolower($data['album'] ?? ''); $applyRules = isset($data['apply_rules']) ? (int)$data['apply_rules'] === 1 : null; - $configuration = $this->job->configuration; + $configuration = $this->importJob->configuration; if ($artist === 'david bowie') { // store artist $configuration['artist'] = $artist; @@ -101,7 +101,7 @@ class FakeJobConfiguration implements JobConfigurationInterface $configuration['apply-rules'] = $applyRules; } - $this->repository->setConfiguration($this->job, $configuration); + $this->repository->setConfiguration($this->importJob, $configuration); $messages = new MessageBag(); if (\count($configuration) !== 3) { @@ -135,7 +135,7 @@ class FakeJobConfiguration implements JobConfigurationInterface public function getNextView(): string { // first configure artist: - $config = $this->job->configuration; + $config = $this->importJob->configuration; $artist = $config['artist'] ?? ''; $song = $config['song'] ?? ''; $album = $config['album'] ?? ''; @@ -149,18 +149,18 @@ class FakeJobConfiguration implements JobConfigurationInterface if (strtolower($song) !== 'golden years') { return 'import.fake.enter-song'; } - if (strtolower($album) !== 'station to station' && $this->job->stage !== 'new') { + if (strtolower($album) !== 'station to station' && $this->importJob->stage !== 'new') { return 'import.fake.enter-album'; } return 'impossible-view'; // @codeCoverageIgnore } /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->job = $job; - $this->repository->setUser($job->user); + $this->importJob = $importJob; + $this->repository->setUser($importJob->user); } } diff --git a/app/Import/JobConfiguration/FileJobConfiguration.php b/app/Import/JobConfiguration/FileJobConfiguration.php index 19764c8806..38eedca1bd 100644 --- a/app/Import/JobConfiguration/FileJobConfiguration.php +++ b/app/Import/JobConfiguration/FileJobConfiguration.php @@ -75,7 +75,7 @@ class FileJobConfiguration implements JobConfigurationInterface public function configureJob(array $data): MessageBag { $configurator = $this->getConfigurationObject(); - $configurator->setJob($this->importJob); + $configurator->setImportJob($this->importJob); return $configurator->configureJob($data); } @@ -89,7 +89,7 @@ class FileJobConfiguration implements JobConfigurationInterface public function getNextData(): array { $configurator = $this->getConfigurationObject(); - $configurator->setJob($this->importJob); + $configurator->setImportJob($this->importJob); return $configurator->getNextData(); } @@ -124,12 +124,12 @@ class FileJobConfiguration implements JobConfigurationInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; - $this->repository->setUser($job->user); + $this->importJob = $importJob; + $this->repository->setUser($importJob->user); } /** diff --git a/app/Import/JobConfiguration/JobConfigurationInterface.php b/app/Import/JobConfiguration/JobConfigurationInterface.php index 70609f51d7..2a76e8b51a 100644 --- a/app/Import/JobConfiguration/JobConfigurationInterface.php +++ b/app/Import/JobConfiguration/JobConfigurationInterface.php @@ -35,6 +35,13 @@ interface JobConfigurationInterface */ public function __construct(); + /** + * Returns true when the initial configuration for this job is complete. + * + * @return bool + */ + public function configurationComplete(): bool; + /** * Store any data from the $data array into the job. Anything in the message bag will be flashed * as an error to the user, regardless of its content. @@ -60,14 +67,7 @@ interface JobConfigurationInterface public function getNextView(): string; /** - * Returns true when the initial configuration for this job is complete. - * - * @return bool + * @param ImportJob $importJob */ - public function configurationComplete(): bool; - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job): void; + public function setImportJob(ImportJob $importJob): void; } diff --git a/app/Import/Routine/BunqRoutine.php b/app/Import/Routine/BunqRoutine.php index 6117391639..a369bcefe6 100644 --- a/app/Import/Routine/BunqRoutine.php +++ b/app/Import/Routine/BunqRoutine.php @@ -927,12 +927,15 @@ class BunqRoutine implements RoutineInterface throw new NotImplementedException; } + /** - * @param ImportJob $job + * @param ImportJob $importJob + * + * @return void */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - // TODO: Implement setJob() method. + // TODO: Implement setImportJob() method. throw new NotImplementedException; } } diff --git a/app/Import/Routine/FakeRoutine.php b/app/Import/Routine/FakeRoutine.php index 8a42c1e8e1..32a48ccb84 100644 --- a/app/Import/Routine/FakeRoutine.php +++ b/app/Import/Routine/FakeRoutine.php @@ -81,7 +81,7 @@ class FakeRoutine implements RoutineInterface case 'final': /** @var StageFinalHandler $handler */ $handler = app(StageFinalHandler::class); - $handler->setJob($this->importJob); + $handler->setImportJob($this->importJob); $transactions = $handler->getTransactions(); $this->repository->setStatus($this->importJob, 'provider_finished'); $this->repository->setStage($this->importJob, 'final'); @@ -90,14 +90,14 @@ class FakeRoutine implements RoutineInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob * * @return */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); + $this->repository->setUser($importJob->user); } } diff --git a/app/Import/Routine/FileRoutine.php b/app/Import/Routine/FileRoutine.php index d626d83754..81aa08b086 100644 --- a/app/Import/Routine/FileRoutine.php +++ b/app/Import/Routine/FileRoutine.php @@ -55,7 +55,7 @@ class FileRoutine implements RoutineInterface // get processor, depending on file type // is just CSV for now. $processor = $this->getProcessor(); - $processor->setJob($this->importJob); + $processor->setImportJob($this->importJob); $transactions = $processor->run(); $this->repository->setStatus($this->importJob, 'provider_finished'); @@ -68,13 +68,13 @@ class FileRoutine implements RoutineInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob * * @return void */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($job->user); } diff --git a/app/Import/Routine/RoutineInterface.php b/app/Import/Routine/RoutineInterface.php index f1f967460d..aad9c4854a 100644 --- a/app/Import/Routine/RoutineInterface.php +++ b/app/Import/Routine/RoutineInterface.php @@ -40,9 +40,9 @@ interface RoutineInterface public function run(): void; /** - * @param ImportJob $job + * @param ImportJob $importJob * * @return void */ - public function setJob(ImportJob $job): void; + public function setImportJob(ImportJob $importJob): void; } diff --git a/app/Import/Routine/SpectreRoutine.php b/app/Import/Routine/SpectreRoutine.php index 85a3c8f54c..710ceba367 100644 --- a/app/Import/Routine/SpectreRoutine.php +++ b/app/Import/Routine/SpectreRoutine.php @@ -22,30 +22,8 @@ declare(strict_types=1); namespace FireflyIII\Import\Routine; -use Carbon\Carbon; -use DB; -use Exception; use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Import\Object\ImportJournal; -use FireflyIII\Import\Storage\ImportStorage; use FireflyIII\Models\ImportJob; -use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Repositories\Tag\TagRepositoryInterface; -use FireflyIII\Services\Spectre\Exception\SpectreException; -use FireflyIII\Services\Spectre\Object\Account; -use FireflyIII\Services\Spectre\Object\Customer; -use FireflyIII\Services\Spectre\Object\Login; -use FireflyIII\Services\Spectre\Object\Token; -use FireflyIII\Services\Spectre\Object\Transaction; -use FireflyIII\Services\Spectre\Request\CreateTokenRequest; -use FireflyIII\Services\Spectre\Request\ListAccountsRequest; -use FireflyIII\Services\Spectre\Request\ListCustomersRequest; -use FireflyIII\Services\Spectre\Request\ListLoginsRequest; -use FireflyIII\Services\Spectre\Request\ListTransactionsRequest; -use FireflyIII\Services\Spectre\Request\NewCustomerRequest; -use Illuminate\Support\Collection; -use Log; -use Preferences; /** * @deprecated @@ -54,544 +32,544 @@ use Preferences; */ class SpectreRoutine implements RoutineInterface { -// /** @var Collection */ -// public $errors; -// /** @var Collection */ -// public $journals; -// /** @var int */ -// public $lines = 0; -// /** @var ImportJob */ -// private $job; -// -// /** @var ImportJobRepositoryInterface */ -// private $repository; -// -// /** -// * ImportRoutine constructor. -// */ -// public function __construct() -// { -// $this->journals = new Collection; -// $this->errors = new Collection; -// } -// -// /** -// * @return Collection -// */ -// public function getErrors(): Collection -// { -// return $this->errors; -// } -// -// /** -// * @return Collection -// */ -// public function getJournals(): Collection -// { -// return $this->journals; -// } -// -// /** -// * @return int -// */ -// public function getLines(): int -// { -// return $this->lines; -// } -// -// /** -// * A Spectre job that ends up here is either "configured" or "running", and will be set to "running" -// * when it is "configured". -// * -// * Job has several stages, stored in extended status key 'stage' -// * -// * initial: just begun, nothing happened. action: get a customer and a token. Next status: has-token -// * has-token: redirect user to sandstorm, make user login. set job to: user-logged-in -// * user-logged-in: customer has an attempt. action: analyse/get attempt and go for next status. -// * if attempt failed: job status is error, save a warning somewhere? -// * if success, try to get accounts. Save in config key 'accounts'. set status: have-accounts and "configuring" -// * -// * have-accounts: make user link accounts and select accounts to import from. -// * -// * If job is "configuring" and stage "have-accounts" then present the accounts and make user link them to -// * own asset accounts. Store this mapping, set config to "have-account-mapping" and job status configured". -// * -// * have-account-mapping: start downloading transactions? -// * -// * -// * @return bool -// * -// * @throws FireflyException -// * @throws SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// public function run(): bool -// { -// if ('configured' === $this->getStatus()) { -// $this->repository->updateStatus($this->job, 'running'); -// } -// Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key)); -// set_time_limit(0); -// -// // check if job has token first! -// $stage = $this->getConfig()['stage'] ?? 'unknown'; -// -// switch ($stage) { -// case 'initial': -// // get customer and token: -// $this->runStageInitial(); -// break; -// case 'has-token': -// // import routine does nothing at this point: -// break; -// case 'user-logged-in': -// $this->runStageLoggedIn(); -// break; -// case 'have-account-mapping': -// $this->runStageHaveMapping(); -// break; -// default: -// throw new FireflyException(sprintf('Cannot handle stage %s', $stage)); -// } -// -// return true; -// } -// -// /** -// * @param ImportJob $job -// */ -// public function setJob(ImportJob $job) -// { -// $this->job = $job; -// $this->repository = app(ImportJobRepositoryInterface::class); -// $this->repository->setUser($job->user); -// } -// -// /** -// * @return Customer -// * -// * @throws \FireflyIII\Exceptions\FireflyException -// * @throws \FireflyIII\Services\Spectre\Exception\SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// protected function createCustomer(): Customer -// { -// $newCustomerRequest = new NewCustomerRequest($this->job->user); -// $customer = null; -// try { -// $newCustomerRequest->call(); -// $customer = $newCustomerRequest->getCustomer(); -// } catch (Exception $e) { -// // already exists, must fetch customer instead. -// Log::warning(sprintf('Customer exists already for user, fetch it: %s', $e->getMessage())); -// } -// if (null === $customer) { -// $getCustomerRequest = new ListCustomersRequest($this->job->user); -// $getCustomerRequest->call(); -// $customers = $getCustomerRequest->getCustomers(); -// /** @var Customer $current */ -// foreach ($customers as $current) { -// if ('default_ff3_customer' === $current->getIdentifier()) { -// $customer = $current; -// break; -// } -// } -// } -// -// Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray()); -// -// return $customer; -// } -// -// /** -// * @return Customer -// * -// * @throws FireflyException -// * @throws SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// protected function getCustomer(): Customer -// { -// $config = $this->getConfig(); -// if (null !== $config['customer']) { -// $customer = new Customer($config['customer']); -// -// return $customer; -// } -// -// $customer = $this->createCustomer(); -// $config['customer'] = [ -// 'id' => $customer->getId(), -// 'identifier' => $customer->getIdentifier(), -// 'secret' => $customer->getSecret(), -// ]; -// $this->setConfig($config); -// -// return $customer; -// } -// -// /** -// * @param Customer $customer -// * @param string $returnUri -// * -// * @return Token -// * -// * @throws \FireflyIII\Exceptions\FireflyException -// * @throws \FireflyIII\Services\Spectre\Exception\SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// protected function getToken(Customer $customer, string $returnUri): Token -// { -// $request = new CreateTokenRequest($this->job->user); -// $request->setUri($returnUri); -// $request->setCustomer($customer); -// $request->call(); -// Log::debug('Call to get token is finished'); -// -// return $request->getToken(); -// } -// -// /** -// * @throws FireflyException -// * @throws SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// protected function runStageInitial(): void -// { -// Log::debug('In runStageInitial()'); -// -// // create customer if user does not have one: -// $customer = $this->getCustomer(); -// Log::debug(sprintf('Customer ID is %s', $customer->getId())); -// -// // use customer to request a token: -// $uri = route('import.status', [$this->job->key]); -// $token = $this->getToken($customer, $uri); -// Log::debug(sprintf('Token is %s', $token->getToken())); -// -// // update job, give it the token: -// $config = $this->getConfig(); -// $config['has-token'] = true; -// $config['token'] = $token->getToken(); -// $config['token-expires'] = $token->getExpiresAt()->format('U'); -// $config['token-url'] = $token->getConnectUrl(); -// $config['stage'] = 'has-token'; -// $this->setConfig($config); -// -// Log::debug('Job config is now', $config); -// -// // update job, set status to "configuring". -// $this->setStatus('configuring'); -// Log::debug(sprintf('Job status is now %s', $this->job->status)); -// $this->addStep(); -// } -// -// /** -// * @throws FireflyException -// * @throws SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// protected function runStageLoggedIn(): void -// { -// Log::debug('In runStageLoggedIn'); -// // list all logins: -// $customer = $this->getCustomer(); -// $request = new ListLoginsRequest($this->job->user); -// $request->setCustomer($customer); -// $request->call(); -// -// $logins = $request->getLogins(); -// /** @var Login $final */ -// $final = null; -// // loop logins, find the latest with no error in it: -// $time = 0; -// /** @var Login $login */ -// foreach ($logins as $login) { -// $attempt = $login->getLastAttempt(); -// $attemptTime = (int)$attempt->getCreatedAt()->format('U'); -// if ($attemptTime > $time && null === $attempt->getFailErrorClass()) { -// $time = $attemptTime; -// $final = $login; -// } -// } -// if (null === $final) { -// Log::error('Could not find a valid login for this user.'); -// $this->repository->addError($this->job, 0, 'Spectre connection failed. Did you use invalid credentials, press Cancel or failed the 2FA challenge?'); -// $this->repository->setStatus($this->job, 'error'); -// -// return; -// } -// $this->addStep(); -// -// // list the users accounts using this login. -// $accountRequest = new ListAccountsRequest($this->job->user); -// $accountRequest->setLogin($login); -// $accountRequest->call(); -// $accounts = $accountRequest->getAccounts(); -// -// // store accounts in job: -// $all = []; -// /** @var Account $account */ -// foreach ($accounts as $account) { -// $all[] = $account->toArray(); -// } -// -// // update job: -// $config = $this->getConfig(); -// $config['accounts'] = $all; -// $config['login'] = $login->toArray(); -// $config['stage'] = 'have-accounts'; -// -// $this->setConfig($config); -// $this->setStatus('configuring'); -// $this->addStep(); -// } -// -// /** -// * Shorthand method. -// */ -// private function addStep() -// { -// $this->repository->addStepsDone($this->job, 1); -// } -// -// /** -// * Shorthand -// * -// * @param int $steps -// */ -// private function addTotalSteps(int $steps) -// { -// $this->repository->addTotalSteps($this->job, $steps); -// } -// -// /** -// * @return array -// */ -// private function getConfig(): array -// { -// return $this->repository->getConfiguration($this->job); -// } -// -// /** -// * Shorthand method. -// * -// * @return array -// */ -// private function getExtendedStatus(): array -// { -// return $this->repository->getExtendedStatus($this->job); -// } -// -// /** -// * Shorthand method. -// * -// * @return string -// */ -// private function getStatus(): string -// { -// return $this->repository->getStatus($this->job); -// } -// -// /** -// * @param array $all -// * -// * @throws FireflyException -// */ -// private function importTransactions(array $all) -// { -// Log::debug('Going to import transactions'); -// $collection = new Collection; -// // create import objects? -// foreach ($all as $accountId => $data) { -// Log::debug(sprintf('Now at account #%d', $accountId)); -// /** @var Transaction $transaction */ -// foreach ($data['transactions'] as $transaction) { -// Log::debug(sprintf('Now at transaction #%d', $transaction->getId())); -// /** @var Account $account */ -// $account = $data['account']; -// $importJournal = new ImportJournal; -// $importJournal->setUser($this->job->user); -// $importJournal->asset->setDefaultAccountId($data['import_id']); -// // call set value a bunch of times for various data entries: -// $tags = []; -// $tags[] = $transaction->getMode(); -// $tags[] = $transaction->getStatus(); -// if ($transaction->isDuplicated()) { -// $tags[] = 'possibly-duplicated'; -// } -// $extra = $transaction->getExtra()->toArray(); -// $notes = ''; -// // double space for newline in Markdown. -// $notes .= (string)trans('import.imported_from_account', ['account' => $account->getName()]) . ' ' . "\n"; -// -// foreach ($extra as $key => $value) { -// switch ($key) { -// case 'account_number': -// $importJournal->setValue(['role' => 'account-number', 'value' => $value]); -// break; -// case 'original_category': -// case 'original_subcategory': -// case 'customer_category_code': -// case 'customer_category_name': -// $tags[] = $value; -// break; -// case 'payee': -// $importJournal->setValue(['role' => 'opposing-name', 'value' => $value]); -// break; -// case 'original_amount': -// $importJournal->setValue(['role' => 'amount_foreign', 'value' => $value]); -// break; -// case 'original_currency_code': -// $importJournal->setValue(['role' => 'foreign-currency-code', 'value' => $value]); -// break; -// default: -// $notes .= $key . ': ' . $value . ' '; // for newline in Markdown. -// } -// } -// // hash -// $importJournal->setHash($transaction->getHash()); -// -// // account ID (Firefly III account): -// $importJournal->setValue(['role' => 'account-id', 'value' => $data['import_id'], 'mapped' => $data['import_id']]); -// -// // description: -// $importJournal->setValue(['role' => 'description', 'value' => $transaction->getDescription()]); -// -// // date: -// $importJournal->setValue(['role' => 'date-transaction', 'value' => $transaction->getMadeOn()->toIso8601String()]); -// -// // amount -// $importJournal->setValue(['role' => 'amount', 'value' => $transaction->getAmount()]); -// $importJournal->setValue(['role' => 'currency-code', 'value' => $transaction->getCurrencyCode()]); -// -// // various meta fields: -// $importJournal->setValue(['role' => 'category-name', 'value' => $transaction->getCategory()]); -// $importJournal->setValue(['role' => 'note', 'value' => $notes]); -// $importJournal->setValue(['role' => 'tags-comma', 'value' => implode(',', $tags)]); -// $collection->push($importJournal); -// } -// } -// $this->addStep(); -// Log::debug(sprintf('Going to try and store all %d them.', $collection->count())); -// -// $this->addTotalSteps(7 * $collection->count()); -// // try to store them (seven steps per transaction) -// $storage = new ImportStorage; -// -// $storage->setJob($this->job); -// $storage->setDateFormat('Y-m-d\TH:i:sO'); -// $storage->setObjects($collection); -// $storage->store(); -// Log::info('Back in importTransactions()'); -// -// // link to tag -// /** @var TagRepositoryInterface $repository */ -// $repository = app(TagRepositoryInterface::class); -// $repository->setUser($this->job->user); -// $data = [ -// 'tag' => trans('import.import_with_key', ['key' => $this->job->key]), -// 'date' => new Carbon, -// 'description' => null, -// 'latitude' => null, -// 'longitude' => null, -// 'zoomLevel' => null, -// 'tagMode' => 'nothing', -// ]; -// $tag = $repository->store($data); -// $extended = $this->getExtendedStatus(); -// $extended['tag'] = $tag->id; -// $this->setExtendedStatus($extended); -// -// Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag)); -// Log::debug('Looping journals...'); -// $journalIds = $storage->journals->pluck('id')->toArray(); -// $tagId = $tag->id; -// $this->addTotalSteps(\count($journalIds)); -// -// foreach ($journalIds as $journalId) { -// Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId)); -// DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]); -// $this->addStep(); -// } -// Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $storage->journals->count(), $tag->id, $tag->tag)); -// -// // set status to "finished"? -// // update job: -// $this->setStatus('finished'); -// $this->addStep(); -// -// } -// -// /** -// * @throws FireflyException -// * @throws SpectreException -// * @throws \Illuminate\Container\EntryNotFoundException -// */ -// private function runStageHaveMapping() -// { -// $config = $this->getConfig(); -// $accounts = $config['accounts'] ?? []; -// $all = []; -// $count = 0; -// /** @var array $accountArray */ -// foreach ($accounts as $accountArray) { -// $account = new Account($accountArray); -// $importId = (int)($config['accounts-mapped'][$account->getId()] ?? 0.0); -// $doImport = 0 !== $importId; -// if (!$doImport) { -// Log::debug(sprintf('Will NOT import from Spectre account #%d ("%s")', $account->getId(), $account->getName())); -// continue; -// } -// // grab all transactions -// $listTransactionsRequest = new ListTransactionsRequest($this->job->user); -// $listTransactionsRequest->setAccount($account); -// $listTransactionsRequest->call(); -// $transactions = $listTransactionsRequest->getTransactions(); -// $all[$account->getId()] = [ -// 'account' => $account, -// 'import_id' => $importId, -// 'transactions' => $transactions, -// ]; -// $count += \count($transactions); -// } -// Log::debug(sprintf('Total number of transactions: %d', $count)); -// $this->addStep(); -// -// $this->importTransactions($all); -// } -// -// /** -// * Shorthand. -// * -// * @param array $config -// */ -// private function setConfig(array $config): void -// { -// $this->repository->setConfiguration($this->job, $config); -// -// } -// -// /** -// * Shorthand method. -// * -// * @param array $extended -// */ -// private function setExtendedStatus(array $extended): void -// { -// $this->repository->setExtendedStatus($this->job, $extended); -// -// } -// -// /** -// * Shorthand. -// * -// * @param string $status -// */ -// private function setStatus(string $status): void -// { -// $this->repository->setStatus($this->job, $status); -// } + // /** @var Collection */ + // public $errors; + // /** @var Collection */ + // public $journals; + // /** @var int */ + // public $lines = 0; + // /** @var ImportJob */ + // private $job; + // + // /** @var ImportJobRepositoryInterface */ + // private $repository; + // + // /** + // * ImportRoutine constructor. + // */ + // public function __construct() + // { + // $this->journals = new Collection; + // $this->errors = new Collection; + // } + // + // /** + // * @return Collection + // */ + // public function getErrors(): Collection + // { + // return $this->errors; + // } + // + // /** + // * @return Collection + // */ + // public function getJournals(): Collection + // { + // return $this->journals; + // } + // + // /** + // * @return int + // */ + // public function getLines(): int + // { + // return $this->lines; + // } + // + // /** + // * A Spectre job that ends up here is either "configured" or "running", and will be set to "running" + // * when it is "configured". + // * + // * Job has several stages, stored in extended status key 'stage' + // * + // * initial: just begun, nothing happened. action: get a customer and a token. Next status: has-token + // * has-token: redirect user to sandstorm, make user login. set job to: user-logged-in + // * user-logged-in: customer has an attempt. action: analyse/get attempt and go for next status. + // * if attempt failed: job status is error, save a warning somewhere? + // * if success, try to get accounts. Save in config key 'accounts'. set status: have-accounts and "configuring" + // * + // * have-accounts: make user link accounts and select accounts to import from. + // * + // * If job is "configuring" and stage "have-accounts" then present the accounts and make user link them to + // * own asset accounts. Store this mapping, set config to "have-account-mapping" and job status configured". + // * + // * have-account-mapping: start downloading transactions? + // * + // * + // * @return bool + // * + // * @throws FireflyException + // * @throws SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // public function run(): bool + // { + // if ('configured' === $this->getStatus()) { + // $this->repository->updateStatus($this->job, 'running'); + // } + // Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key)); + // set_time_limit(0); + // + // // check if job has token first! + // $stage = $this->getConfig()['stage'] ?? 'unknown'; + // + // switch ($stage) { + // case 'initial': + // // get customer and token: + // $this->runStageInitial(); + // break; + // case 'has-token': + // // import routine does nothing at this point: + // break; + // case 'user-logged-in': + // $this->runStageLoggedIn(); + // break; + // case 'have-account-mapping': + // $this->runStageHaveMapping(); + // break; + // default: + // throw new FireflyException(sprintf('Cannot handle stage %s', $stage)); + // } + // + // return true; + // } + // + // /** + // * @param ImportJob $job + // */ + // public function setXJob(ImportJob $job) + // { + // $this->job = $job; + // $this->repository = app(ImportJobRepositoryInterface::class); + // $this->repository->setUser($job->user); + // } + // + // /** + // * @return Customer + // * + // * @throws \FireflyIII\Exceptions\FireflyException + // * @throws \FireflyIII\Services\Spectre\Exception\SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // protected function createCustomer(): Customer + // { + // $newCustomerRequest = new NewCustomerRequest($this->job->user); + // $customer = null; + // try { + // $newCustomerRequest->call(); + // $customer = $newCustomerRequest->getCustomer(); + // } catch (Exception $e) { + // // already exists, must fetch customer instead. + // Log::warning(sprintf('Customer exists already for user, fetch it: %s', $e->getMessage())); + // } + // if (null === $customer) { + // $getCustomerRequest = new ListCustomersRequest($this->job->user); + // $getCustomerRequest->call(); + // $customers = $getCustomerRequest->getCustomers(); + // /** @var Customer $current */ + // foreach ($customers as $current) { + // if ('default_ff3_customer' === $current->getIdentifier()) { + // $customer = $current; + // break; + // } + // } + // } + // + // Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray()); + // + // return $customer; + // } + // + // /** + // * @return Customer + // * + // * @throws FireflyException + // * @throws SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // protected function getCustomer(): Customer + // { + // $config = $this->getConfig(); + // if (null !== $config['customer']) { + // $customer = new Customer($config['customer']); + // + // return $customer; + // } + // + // $customer = $this->createCustomer(); + // $config['customer'] = [ + // 'id' => $customer->getId(), + // 'identifier' => $customer->getIdentifier(), + // 'secret' => $customer->getSecret(), + // ]; + // $this->setConfig($config); + // + // return $customer; + // } + // + // /** + // * @param Customer $customer + // * @param string $returnUri + // * + // * @return Token + // * + // * @throws \FireflyIII\Exceptions\FireflyException + // * @throws \FireflyIII\Services\Spectre\Exception\SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // protected function getToken(Customer $customer, string $returnUri): Token + // { + // $request = new CreateTokenRequest($this->job->user); + // $request->setUri($returnUri); + // $request->setCustomer($customer); + // $request->call(); + // Log::debug('Call to get token is finished'); + // + // return $request->getToken(); + // } + // + // /** + // * @throws FireflyException + // * @throws SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // protected function runStageInitial(): void + // { + // Log::debug('In runStageInitial()'); + // + // // create customer if user does not have one: + // $customer = $this->getCustomer(); + // Log::debug(sprintf('Customer ID is %s', $customer->getId())); + // + // // use customer to request a token: + // $uri = route('import.status', [$this->job->key]); + // $token = $this->getToken($customer, $uri); + // Log::debug(sprintf('Token is %s', $token->getToken())); + // + // // update job, give it the token: + // $config = $this->getConfig(); + // $config['has-token'] = true; + // $config['token'] = $token->getToken(); + // $config['token-expires'] = $token->getExpiresAt()->format('U'); + // $config['token-url'] = $token->getConnectUrl(); + // $config['stage'] = 'has-token'; + // $this->setConfig($config); + // + // Log::debug('Job config is now', $config); + // + // // update job, set status to "configuring". + // $this->setStatus('configuring'); + // Log::debug(sprintf('Job status is now %s', $this->job->status)); + // $this->addStep(); + // } + // + // /** + // * @throws FireflyException + // * @throws SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // protected function runStageLoggedIn(): void + // { + // Log::debug('In runStageLoggedIn'); + // // list all logins: + // $customer = $this->getCustomer(); + // $request = new ListLoginsRequest($this->job->user); + // $request->setCustomer($customer); + // $request->call(); + // + // $logins = $request->getLogins(); + // /** @var Login $final */ + // $final = null; + // // loop logins, find the latest with no error in it: + // $time = 0; + // /** @var Login $login */ + // foreach ($logins as $login) { + // $attempt = $login->getLastAttempt(); + // $attemptTime = (int)$attempt->getCreatedAt()->format('U'); + // if ($attemptTime > $time && null === $attempt->getFailErrorClass()) { + // $time = $attemptTime; + // $final = $login; + // } + // } + // if (null === $final) { + // Log::error('Could not find a valid login for this user.'); + // $this->repository->addError($this->job, 0, 'Spectre connection failed. Did you use invalid credentials, press Cancel or failed the 2FA challenge?'); + // $this->repository->setStatus($this->job, 'error'); + // + // return; + // } + // $this->addStep(); + // + // // list the users accounts using this login. + // $accountRequest = new ListAccountsRequest($this->job->user); + // $accountRequest->setLogin($login); + // $accountRequest->call(); + // $accounts = $accountRequest->getAccounts(); + // + // // store accounts in job: + // $all = []; + // /** @var Account $account */ + // foreach ($accounts as $account) { + // $all[] = $account->toArray(); + // } + // + // // update job: + // $config = $this->getConfig(); + // $config['accounts'] = $all; + // $config['login'] = $login->toArray(); + // $config['stage'] = 'have-accounts'; + // + // $this->setConfig($config); + // $this->setStatus('configuring'); + // $this->addStep(); + // } + // + // /** + // * Shorthand method. + // */ + // private function addStep() + // { + // $this->repository->addStepsDone($this->job, 1); + // } + // + // /** + // * Shorthand + // * + // * @param int $steps + // */ + // private function addTotalSteps(int $steps) + // { + // $this->repository->addTotalSteps($this->job, $steps); + // } + // + // /** + // * @return array + // */ + // private function getConfig(): array + // { + // return $this->repository->getConfiguration($this->job); + // } + // + // /** + // * Shorthand method. + // * + // * @return array + // */ + // private function getExtendedStatus(): array + // { + // return $this->repository->getExtendedStatus($this->job); + // } + // + // /** + // * Shorthand method. + // * + // * @return string + // */ + // private function getStatus(): string + // { + // return $this->repository->getStatus($this->job); + // } + // + // /** + // * @param array $all + // * + // * @throws FireflyException + // */ + // private function importTransactions(array $all) + // { + // Log::debug('Going to import transactions'); + // $collection = new Collection; + // // create import objects? + // foreach ($all as $accountId => $data) { + // Log::debug(sprintf('Now at account #%d', $accountId)); + // /** @var Transaction $transaction */ + // foreach ($data['transactions'] as $transaction) { + // Log::debug(sprintf('Now at transaction #%d', $transaction->getId())); + // /** @var Account $account */ + // $account = $data['account']; + // $importJournal = new ImportJournal; + // $importJournal->setUser($this->job->user); + // $importJournal->asset->setDefaultAccountId($data['import_id']); + // // call set value a bunch of times for various data entries: + // $tags = []; + // $tags[] = $transaction->getMode(); + // $tags[] = $transaction->getStatus(); + // if ($transaction->isDuplicated()) { + // $tags[] = 'possibly-duplicated'; + // } + // $extra = $transaction->getExtra()->toArray(); + // $notes = ''; + // // double space for newline in Markdown. + // $notes .= (string)trans('import.imported_from_account', ['account' => $account->getName()]) . ' ' . "\n"; + // + // foreach ($extra as $key => $value) { + // switch ($key) { + // case 'account_number': + // $importJournal->setValue(['role' => 'account-number', 'value' => $value]); + // break; + // case 'original_category': + // case 'original_subcategory': + // case 'customer_category_code': + // case 'customer_category_name': + // $tags[] = $value; + // break; + // case 'payee': + // $importJournal->setValue(['role' => 'opposing-name', 'value' => $value]); + // break; + // case 'original_amount': + // $importJournal->setValue(['role' => 'amount_foreign', 'value' => $value]); + // break; + // case 'original_currency_code': + // $importJournal->setValue(['role' => 'foreign-currency-code', 'value' => $value]); + // break; + // default: + // $notes .= $key . ': ' . $value . ' '; // for newline in Markdown. + // } + // } + // // hash + // $importJournal->setHash($transaction->getHash()); + // + // // account ID (Firefly III account): + // $importJournal->setValue(['role' => 'account-id', 'value' => $data['import_id'], 'mapped' => $data['import_id']]); + // + // // description: + // $importJournal->setValue(['role' => 'description', 'value' => $transaction->getDescription()]); + // + // // date: + // $importJournal->setValue(['role' => 'date-transaction', 'value' => $transaction->getMadeOn()->toIso8601String()]); + // + // // amount + // $importJournal->setValue(['role' => 'amount', 'value' => $transaction->getAmount()]); + // $importJournal->setValue(['role' => 'currency-code', 'value' => $transaction->getCurrencyCode()]); + // + // // various meta fields: + // $importJournal->setValue(['role' => 'category-name', 'value' => $transaction->getCategory()]); + // $importJournal->setValue(['role' => 'note', 'value' => $notes]); + // $importJournal->setValue(['role' => 'tags-comma', 'value' => implode(',', $tags)]); + // $collection->push($importJournal); + // } + // } + // $this->addStep(); + // Log::debug(sprintf('Going to try and store all %d them.', $collection->count())); + // + // $this->addTotalSteps(7 * $collection->count()); + // // try to store them (seven steps per transaction) + // $storage = new ImportStorage; + // + // $storage->setXJob($this->job); + // $storage->setDateFormat('Y-m-d\TH:i:sO'); + // $storage->setObjects($collection); + // $storage->store(); + // Log::info('Back in importTransactions()'); + // + // // link to tag + // /** @var TagRepositoryInterface $repository */ + // $repository = app(TagRepositoryInterface::class); + // $repository->setUser($this->job->user); + // $data = [ + // 'tag' => trans('import.import_with_key', ['key' => $this->job->key]), + // 'date' => new Carbon, + // 'description' => null, + // 'latitude' => null, + // 'longitude' => null, + // 'zoomLevel' => null, + // 'tagMode' => 'nothing', + // ]; + // $tag = $repository->store($data); + // $extended = $this->getExtendedStatus(); + // $extended['tag'] = $tag->id; + // $this->setExtendedStatus($extended); + // + // Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag)); + // Log::debug('Looping journals...'); + // $journalIds = $storage->journals->pluck('id')->toArray(); + // $tagId = $tag->id; + // $this->addTotalSteps(\count($journalIds)); + // + // foreach ($journalIds as $journalId) { + // Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId)); + // DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]); + // $this->addStep(); + // } + // Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $storage->journals->count(), $tag->id, $tag->tag)); + // + // // set status to "finished"? + // // update job: + // $this->setStatus('finished'); + // $this->addStep(); + // + // } + // + // /** + // * @throws FireflyException + // * @throws SpectreException + // * @throws \Illuminate\Container\EntryNotFoundException + // */ + // private function runStageHaveMapping() + // { + // $config = $this->getConfig(); + // $accounts = $config['accounts'] ?? []; + // $all = []; + // $count = 0; + // /** @var array $accountArray */ + // foreach ($accounts as $accountArray) { + // $account = new Account($accountArray); + // $importId = (int)($config['accounts-mapped'][$account->getId()] ?? 0.0); + // $doImport = 0 !== $importId; + // if (!$doImport) { + // Log::debug(sprintf('Will NOT import from Spectre account #%d ("%s")', $account->getId(), $account->getName())); + // continue; + // } + // // grab all transactions + // $listTransactionsRequest = new ListTransactionsRequest($this->job->user); + // $listTransactionsRequest->setAccount($account); + // $listTransactionsRequest->call(); + // $transactions = $listTransactionsRequest->getTransactions(); + // $all[$account->getId()] = [ + // 'account' => $account, + // 'import_id' => $importId, + // 'transactions' => $transactions, + // ]; + // $count += \count($transactions); + // } + // Log::debug(sprintf('Total number of transactions: %d', $count)); + // $this->addStep(); + // + // $this->importTransactions($all); + // } + // + // /** + // * Shorthand. + // * + // * @param array $config + // */ + // private function setConfig(array $config): void + // { + // $this->repository->setConfiguration($this->job, $config); + // + // } + // + // /** + // * Shorthand method. + // * + // * @param array $extended + // */ + // private function setExtendedStatus(array $extended): void + // { + // $this->repository->setExtendedStatus($this->job, $extended); + // + // } + // + // /** + // * Shorthand. + // * + // * @param string $status + // */ + // private function setStatus(string $status): void + // { + // $this->repository->setStatus($this->job, $status); + // } /** * At the end of each run(), the import routine must set the job to the expected status. * @@ -607,11 +585,13 @@ class SpectreRoutine implements RoutineInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob + * + * @return void */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - // TODO: Implement setJob() method. + // TODO: Implement setImportJob() method. throw new NotImplementedException; } } diff --git a/app/Import/Storage/ImportArrayStorage.php b/app/Import/Storage/ImportArrayStorage.php index a65a3bf8a3..7699caeb1f 100644 --- a/app/Import/Storage/ImportArrayStorage.php +++ b/app/Import/Storage/ImportArrayStorage.php @@ -67,7 +67,7 @@ class ImportArrayStorage * * @param ImportJob $importJob */ - public function setJob(ImportJob $importJob): void + public function setImportJob(ImportJob $importJob): void { $this->importJob = $importJob; $this->countTransfers(); diff --git a/app/Support/Import/Configuration/File/ConfigurationInterface.php b/app/Support/Import/Configuration/File/ConfigurationInterface.php index e9344f35a8..de8fd0cf85 100644 --- a/app/Support/Import/Configuration/File/ConfigurationInterface.php +++ b/app/Support/Import/Configuration/File/ConfigurationInterface.php @@ -47,7 +47,7 @@ interface ConfigurationInterface public function getNextData(): array; /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void; + public function setImportJob(ImportJob $importJob): void; } diff --git a/app/Support/Import/Configuration/File/ConfigureMappingHandler.php b/app/Support/Import/Configuration/File/ConfigureMappingHandler.php index e7c3efe693..962e126e76 100644 --- a/app/Support/Import/Configuration/File/ConfigureMappingHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureMappingHandler.php @@ -337,13 +337,13 @@ class ConfigureMappingHandler implements ConfigurationInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); + $this->repository->setUser($importJob->user); $this->attachments = app(AttachmentHelperInterface::class); $this->columnConfig = []; } diff --git a/app/Support/Import/Configuration/File/ConfigureRolesHandler.php b/app/Support/Import/Configuration/File/ConfigureRolesHandler.php index e857d32c16..adf1243792 100644 --- a/app/Support/Import/Configuration/File/ConfigureRolesHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureRolesHandler.php @@ -400,13 +400,13 @@ class ConfigureRolesHandler implements ConfigurationInterface /** * Set job and some start values. * - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); + $this->repository->setUser($importJob->user); $this->attachments = app(AttachmentHelperInterface::class); $this->totalColumns = 0; $this->examples = []; diff --git a/app/Support/Import/Configuration/File/ConfigureUploadHandler.php b/app/Support/Import/Configuration/File/ConfigureUploadHandler.php index 640f39833a..8d2fa53671 100644 --- a/app/Support/Import/Configuration/File/ConfigureUploadHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureUploadHandler.php @@ -81,15 +81,15 @@ class ConfigureUploadHandler implements ConfigurationInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); + $this->repository->setUser($importJob->user); $this->accountRepos = app(AccountRepositoryInterface::class); - $this->accountRepos->setUser($job->user); + $this->accountRepos->setUser($importJob->user); } diff --git a/app/Support/Import/Configuration/File/NewFileJobHandler.php b/app/Support/Import/Configuration/File/NewFileJobHandler.php index 77ce21f862..51cc9a4476 100644 --- a/app/Support/Import/Configuration/File/NewFileJobHandler.php +++ b/app/Support/Import/Configuration/File/NewFileJobHandler.php @@ -104,12 +104,12 @@ class NewFileJobHandler implements ConfigurationInterface /** * @param ImportJob $job */ - public function setJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - $this->importJob = $job; + $this->importJob = $importJob; $this->repository = app(ImportJobRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); - $this->repository->setUser($job->user); + $this->repository->setUser($importJob->user); } /** diff --git a/app/Support/Import/Routine/Fake/StageFinalHandler.php b/app/Support/Import/Routine/Fake/StageFinalHandler.php index 7f8bf81f14..f717e7a50f 100644 --- a/app/Support/Import/Routine/Fake/StageFinalHandler.php +++ b/app/Support/Import/Routine/Fake/StageFinalHandler.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\Routine\Fake; use Carbon\Carbon; +use FireflyIII\Models\ImportJob; /** * @codeCoverageIgnore @@ -34,17 +35,8 @@ use Carbon\Carbon; */ class StageFinalHandler { - - private $job; - - /** - * @param mixed $job - */ - public function setJob($job): void - { - $this->job = $job; - } - + /** @var ImportJob */ + private $importJob; /** * @return array @@ -58,7 +50,7 @@ class StageFinalHandler 'type' => 'withdrawal', 'date' => Carbon::create()->format('Y-m-d'), 'tags' => '', - 'user' => $this->job->user_id, + 'user' => $this->importJob->user_id, // all custom fields: 'internal_reference' => null, @@ -103,7 +95,7 @@ class StageFinalHandler 'type' => 'transfer', 'date' => '2017-02-28', 'tags' => '', - 'user' => $this->job->user_id, + 'user' => $this->importJob->user_id, // all custom fields: 'internal_reference' => null, @@ -145,4 +137,12 @@ class StageFinalHandler } + /** + * @param ImportJob $importJob + */ + public function setImportJob(ImportJob $importJob): void + { + $this->importJob = $importJob; + } + } diff --git a/app/Support/Import/Routine/File/CSVProcessor.php b/app/Support/Import/Routine/File/CSVProcessor.php index d67aedaa0a..2161569c2c 100644 --- a/app/Support/Import/Routine/File/CSVProcessor.php +++ b/app/Support/Import/Routine/File/CSVProcessor.php @@ -80,11 +80,11 @@ class CSVProcessor implements FileProcessorInterface } /** - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setImportJob(ImportJob $job): void + public function setImportJob(ImportJob $importJob): void { - Log::debug('Now in setJob()'); - $this->importJob = $job; + Log::debug('Now in setImportJob()'); + $this->importJob = $importJob; } } diff --git a/app/Support/Import/Routine/File/FileProcessorInterface.php b/app/Support/Import/Routine/File/FileProcessorInterface.php index 195d8d5d2b..7ae615e008 100644 --- a/app/Support/Import/Routine/File/FileProcessorInterface.php +++ b/app/Support/Import/Routine/File/FileProcessorInterface.php @@ -43,7 +43,7 @@ interface FileProcessorInterface /** * Set values. * - * @param ImportJob $job + * @param ImportJob $importJob */ - public function setImportJob(ImportJob $job): void; + public function setImportJob(ImportJob $importJob): void; } diff --git a/app/Support/Import/Routine/File/ImportableConverter.php b/app/Support/Import/Routine/File/ImportableConverter.php index c59c70415f..845fd63ee8 100644 --- a/app/Support/Import/Routine/File/ImportableConverter.php +++ b/app/Support/Import/Routine/File/ImportableConverter.php @@ -31,6 +31,7 @@ use FireflyIII\Models\ImportJob; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Support\Import\Placeholder\ImportTransaction; +use InvalidArgumentException; use Log; /** @@ -112,6 +113,8 @@ class ImportableConverter } /** + * @codeCoverageIgnore + * * @param array $mappedValues */ public function setMappedValues(array $mappedValues): void @@ -179,18 +182,27 @@ class ImportableConverter Log::debug('Destination is an expense account. This is a withdrawal.'); $transactionType = 'withdrawal'; } - if ($transactionType === 'unknown') { - Log::error( + if ($destination->id === $source->id) { + throw new FireflyException( sprintf( - 'Cannot determine transaction type. Source account is a %s, destination is a %s', - $source->accountType->type, $destination->accountType->type - ), ['source' => $source->toArray(), 'dest' => $destination->toArray()] + 'Source ("%s", #%d) and destination ("%s", #%d) are the same account.', $source->name, $source->id, $destination->name, $destination->id + ) ); } + if ($transactionType === 'unknown') { + $message = sprintf( + 'Cannot determine transaction type. Source account is a %s, destination is a %s', $source->accountType->type, $destination->accountType->type + ); + Log::error($message, ['source' => $source->toArray(), 'dest' => $destination->toArray()]); + throw new FireflyException($message); + } + + // throw error when both are he same + try { $date = Carbon::createFromFormat($this->config['date-format'] ?? 'Ymd', $importable->date); - } catch (InvalidDateException $e) { + } catch (InvalidDateException|InvalidArgumentException $e) { Log::error($e->getMessage()); Log::error($e->getTraceAsString()); $date = new Carbon; diff --git a/app/Support/Import/Routine/File/MappingConverger.php b/app/Support/Import/Routine/File/MappingConverger.php index 12a52a5279..d3a7af5ceb 100644 --- a/app/Support/Import/Routine/File/MappingConverger.php +++ b/app/Support/Import/Routine/File/MappingConverger.php @@ -82,6 +82,7 @@ class MappingConverger } /** + * @codeCoverageIgnore * @return array */ public function getMappedValues(): array diff --git a/tests/Feature/Controllers/Import/JobConfigurationControllerTest.php b/tests/Feature/Controllers/Import/JobConfigurationControllerTest.php index f9fb5b9950..07e9769075 100644 --- a/tests/Feature/Controllers/Import/JobConfigurationControllerTest.php +++ b/tests/Feature/Controllers/Import/JobConfigurationControllerTest.php @@ -64,7 +64,7 @@ class JobConfigurationControllerTest extends TestCase $configurator = $this->mock(FakeJobConfiguration::class); // mock calls: - $configurator->shouldReceive('setJob')->once(); + $configurator->shouldReceive('setImportJob')->once(); $configurator->shouldReceive('configurationComplete')->once()->andReturn(false); $configurator->shouldReceive('getNextView')->once()->andReturn('import.fake.apply-rules'); $configurator->shouldReceive('getNextData')->once() @@ -122,7 +122,7 @@ class JobConfigurationControllerTest extends TestCase $configurator = $this->mock(FakeJobConfiguration::class); // mock calls: - $configurator->shouldReceive('setJob')->once(); + $configurator->shouldReceive('setImportJob')->once(); $configurator->shouldReceive('configurationComplete')->once()->andReturn(true); $repository->shouldReceive('updateStatus')->withArgs([Mockery::any(), 'ready_to_run']); @@ -154,7 +154,7 @@ class JobConfigurationControllerTest extends TestCase $configurator = $this->mock(FakeJobConfiguration::class); // mock calls: - $configurator->shouldReceive('setJob')->once(); + $configurator->shouldReceive('setImportJob')->once(); $configurator->shouldReceive('configurationComplete')->once()->andReturn(false); $configurator->shouldReceive('configureJob')->withArgs([[]])->once()->andReturn($messages); @@ -214,7 +214,7 @@ class JobConfigurationControllerTest extends TestCase $configurator = $this->mock(FakeJobConfiguration::class); // mock calls: - $configurator->shouldReceive('setJob')->once(); + $configurator->shouldReceive('setImportJob')->once(); $configurator->shouldReceive('configurationComplete')->once()->andReturn(true); $repository->shouldReceive('updateStatus')->withArgs([Mockery::any(), 'ready_to_run']); @@ -247,7 +247,7 @@ class JobConfigurationControllerTest extends TestCase $configurator = $this->mock(FakeJobConfiguration::class); // mock calls: - $configurator->shouldReceive('setJob')->once(); + $configurator->shouldReceive('setImportJob')->once(); $configurator->shouldReceive('configurationComplete')->once()->andReturn(false); $configurator->shouldReceive('configureJob')->once()->andReturn($messages); $repository->shouldReceive('storeFileUpload')->once()->andReturn(new MessageBag); diff --git a/tests/Feature/Controllers/Import/JobStatusControllerTest.php b/tests/Feature/Controllers/Import/JobStatusControllerTest.php index 486100077c..19e5f76289 100644 --- a/tests/Feature/Controllers/Import/JobStatusControllerTest.php +++ b/tests/Feature/Controllers/Import/JobStatusControllerTest.php @@ -198,7 +198,7 @@ class JobStatusControllerTest extends TestCase // mock calls: $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']); - $routine->shouldReceive('setJob')->once(); + $routine->shouldReceive('setImportJob')->once(); $routine->shouldReceive('run')->once(); // call thing. @@ -229,7 +229,7 @@ class JobStatusControllerTest extends TestCase // mock calls: $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']); $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']); - $routine->shouldReceive('setJob')->once(); + $routine->shouldReceive('setImportJob')->once(); $routine->shouldReceive('run')->andThrow(new Exception('Unknown exception')); // call thing. @@ -260,7 +260,7 @@ class JobStatusControllerTest extends TestCase // mock calls: $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']); $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']); - $routine->shouldReceive('setJob')->once(); + $routine->shouldReceive('setImportJob')->once(); $routine->shouldReceive('run')->andThrow(new FireflyException('Unknown exception')); // call thing. @@ -312,7 +312,7 @@ class JobStatusControllerTest extends TestCase // mock calls: $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storing_data']); $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storage_finished']); - $storage->shouldReceive('setJob')->once(); + $storage->shouldReceive('setImportJob')->once(); $storage->shouldReceive('store')->once(); @@ -343,7 +343,7 @@ class JobStatusControllerTest extends TestCase // mock calls: $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storing_data']); $repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']); - $storage->shouldReceive('setJob')->once(); + $storage->shouldReceive('setImportJob')->once(); $storage->shouldReceive('store')->once()->andThrow(new FireflyException('Some storage exception.')); diff --git a/tests/Unit/Import/JobConfiguration/FakeJobConfigurationTest.php b/tests/Unit/Import/JobConfiguration/FakeJobConfigurationTest.php index a6111b24cb..c0ed25f96c 100644 --- a/tests/Unit/Import/JobConfiguration/FakeJobConfigurationTest.php +++ b/tests/Unit/Import/JobConfiguration/FakeJobConfigurationTest.php @@ -53,7 +53,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -76,7 +76,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -101,7 +101,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -129,7 +129,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -156,7 +156,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -184,7 +184,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertTrue($configurator->configurationComplete()); } @@ -211,7 +211,7 @@ class FakeJobConfigurationTest extends TestCase // should be false: $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertTrue($configurator->configurationComplete()); } @@ -245,7 +245,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -280,7 +280,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -315,7 +315,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -350,7 +350,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -385,7 +385,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -420,7 +420,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -455,7 +455,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -490,7 +490,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $messages = $configurator->configureJob($data); $this->assertTrue($messages->has('some_key')); } @@ -514,7 +514,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $view = $configurator->getNextView(); $this->assertEquals('import.fake.enter-album', $view); } @@ -538,7 +538,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $view = $configurator->getNextView(); $this->assertEquals('import.fake.enter-artist', $view); } @@ -562,7 +562,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $view = $configurator->getNextView(); $this->assertEquals('import.fake.apply-rules', $view); } @@ -586,7 +586,7 @@ class FakeJobConfigurationTest extends TestCase // call configuration $configurator = new FakeJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $view = $configurator->getNextView(); $this->assertEquals('import.fake.enter-song', $view); } diff --git a/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php b/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php index ca7af58510..107952cf85 100644 --- a/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php +++ b/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php @@ -59,7 +59,7 @@ class FileJobConfigurationTest extends TestCase // should be false: $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertFalse($configurator->configurationComplete()); } @@ -82,7 +82,7 @@ class FileJobConfigurationTest extends TestCase // should be false: $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $this->assertTrue($configurator->configurationComplete()); } @@ -107,10 +107,10 @@ class FileJobConfigurationTest extends TestCase $result = null; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $handler = $this->mock(ConfigureMappingHandler::class); - $handler->shouldReceive('setJob')->once()->withArgs([Mockery::any()]); + $handler->shouldReceive('setImportJob')->once()->withArgs([Mockery::any()]); $handler->shouldReceive('configureJob')->withArgs([['c' => 'd']])->andReturn($bag)->once(); try { @@ -140,10 +140,10 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $handler = $this->mock(ConfigureUploadHandler::class); - $handler->shouldReceive('setJob')->once()->withArgs([Mockery::any()]); + $handler->shouldReceive('setImportJob')->once()->withArgs([Mockery::any()]); $handler->shouldReceive('getNextData')->andReturn(['a' => 'b'])->withNoArgs()->once(); try { @@ -173,10 +173,10 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $handler = $this->mock(ConfigureMappingHandler::class); - $handler->shouldReceive('setJob')->once()->withArgs([Mockery::any()]); + $handler->shouldReceive('setImportJob')->once()->withArgs([Mockery::any()]); $handler->shouldReceive('getNextData')->andReturn(['a' => 'b'])->withNoArgs()->once(); try { @@ -206,10 +206,10 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $handler = $this->mock(NewFileJobHandler::class); - $handler->shouldReceive('setJob')->once()->withArgs([Mockery::any()]); + $handler->shouldReceive('setImportJob')->once()->withArgs([Mockery::any()]); $handler->shouldReceive('getNextData')->andReturn(['a' => 'b'])->withNoArgs()->once(); try { @@ -239,10 +239,10 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); $handler = $this->mock(ConfigureRolesHandler::class); - $handler->shouldReceive('setJob')->once()->withArgs([Mockery::any()]); + $handler->shouldReceive('setImportJob')->once()->withArgs([Mockery::any()]); $handler->shouldReceive('getNextData')->andReturn(['a' => 'b'])->withNoArgs()->once(); try { @@ -272,7 +272,7 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); try { $result = $configurator->getNextView(); } catch (FireflyException $e) { @@ -300,7 +300,7 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); try { $result = $configurator->getNextView(); } catch (FireflyException $e) { @@ -328,7 +328,7 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); try { $result = $configurator->getNextView(); } catch (FireflyException $e) { @@ -356,7 +356,7 @@ class FileJobConfigurationTest extends TestCase $result = 'x'; $configurator = new FileJobConfiguration; - $configurator->setJob($job); + $configurator->setImportJob($job); try { $result = $configurator->getNextView(); } catch (FireflyException $e) { diff --git a/tests/Unit/Import/Routine/FakeRoutineTest.php b/tests/Unit/Import/Routine/FakeRoutineTest.php index b8b2511b36..a3f71d7053 100644 --- a/tests/Unit/Import/Routine/FakeRoutineTest.php +++ b/tests/Unit/Import/Routine/FakeRoutineTest.php @@ -65,7 +65,7 @@ class FakeRoutineTest extends TestCase $routine = new FakeRoutine; - $routine->setJob($job); + $routine->setImportJob($job); try { $routine->run(); } catch (FireflyException $e) { @@ -98,10 +98,10 @@ class FakeRoutineTest extends TestCase $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'final'])->once(); $repository->shouldReceive('setTransactions')->withArgs([Mockery::any(), []])->once(); $handler->shouldReceive('getTransactions')->once()->andReturn([]); - $handler->shouldReceive('setJob')->once(); + $handler->shouldReceive('setImportJob')->once(); $routine = new FakeRoutine; - $routine->setJob($job); + $routine->setImportJob($job); try { $routine->run(); } catch (FireflyException $e) { @@ -136,7 +136,7 @@ class FakeRoutineTest extends TestCase $routine = new FakeRoutine; - $routine->setJob($job); + $routine->setImportJob($job); try { $routine->run(); } catch (FireflyException $e) { diff --git a/tests/Unit/Import/Routine/FileRoutineTest.php b/tests/Unit/Import/Routine/FileRoutineTest.php index 90056bc034..6eace44458 100644 --- a/tests/Unit/Import/Routine/FileRoutineTest.php +++ b/tests/Unit/Import/Routine/FileRoutineTest.php @@ -63,12 +63,12 @@ class FileRoutineTest extends TestCase $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'final'])->once(); $repository->shouldReceive('setTransactions')->withArgs([Mockery::any(), ['a' => 'b']])->once(); $repository->shouldReceive('getConfiguration')->withArgs([Mockery::any()])->once()->andReturn([]); - $processor->shouldReceive('setJob')->once(); + $processor->shouldReceive('setImportJob')->once(); $processor->shouldReceive('run')->once()->andReturn(['a' => 'b']); $routine = new FileRoutine; - $routine->setJob($job); + $routine->setImportJob($job); try { $routine->run(); } catch (FireflyException $e) { diff --git a/tests/Unit/Import/Storage/ImportArrayStorageTest.php b/tests/Unit/Import/Storage/ImportArrayStorageTest.php index 6392ec730f..ec6f4ff6c8 100644 --- a/tests/Unit/Import/Storage/ImportArrayStorageTest.php +++ b/tests/Unit/Import/Storage/ImportArrayStorageTest.php @@ -76,7 +76,7 @@ class ImportArrayStorageTest extends TestCase $journalRepos->shouldReceive('setUser')->once(); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); } /** @@ -131,7 +131,7 @@ class ImportArrayStorageTest extends TestCase ->withArgs([Mockery::any(), 'Entry #1 ("' . $transactions[1]['description'] . '") could not be imported. It already exists.']); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -170,7 +170,7 @@ class ImportArrayStorageTest extends TestCase $journalRepos->shouldReceive('setUser')->once(); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -214,7 +214,7 @@ class ImportArrayStorageTest extends TestCase $journalRepos->shouldReceive('setUser')->once(); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -265,7 +265,7 @@ class ImportArrayStorageTest extends TestCase $journalRepos->shouldReceive('findByHash')->andReturn(null)->once(); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -320,7 +320,7 @@ class ImportArrayStorageTest extends TestCase $journalRepos->shouldReceive('findByHash')->andReturn(null)->once(); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -393,7 +393,7 @@ class ImportArrayStorageTest extends TestCase $collector->shouldReceive('getJournals')->andReturn($transferCollection); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); @@ -472,7 +472,7 @@ class ImportArrayStorageTest extends TestCase $collector->shouldReceive('getJournals')->andReturn($transferCollection); $storage = new ImportArrayStorage; - $storage->setJob($job); + $storage->setImportJob($job); $result = new Collection; try { $result = $storage->store(); diff --git a/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php index 8e13a445c6..50aa783fc7 100644 --- a/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php +++ b/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php @@ -74,7 +74,7 @@ class ConfigureMappingHandlerTest extends TestCase ]; $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); $result = $handler->applySpecifics($config, []); $this->assertEquals($expected, $result); @@ -139,7 +139,7 @@ class ConfigureMappingHandlerTest extends TestCase $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); $handler->configureJob($input); } @@ -197,7 +197,7 @@ class ConfigureMappingHandlerTest extends TestCase ]; $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $result = $handler->doColumnConfig($input); } catch (FireflyException $e) { @@ -231,7 +231,7 @@ class ConfigureMappingHandlerTest extends TestCase ]; $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); foreach ($combinations as $info) { $this->assertEquals($info['expected'], $handler->doMapOfColumn($info['role'], $info['requested'])); } @@ -298,7 +298,7 @@ class ConfigureMappingHandlerTest extends TestCase $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $result = $handler->getNextData(); } catch (FireflyException $e) { @@ -340,7 +340,7 @@ class ConfigureMappingHandlerTest extends TestCase ]; $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); foreach ($combinations as $info) { $this->assertEquals($info['expected'], $handler->getPreProcessorName($info['role'])); } @@ -386,7 +386,7 @@ class ConfigureMappingHandlerTest extends TestCase $attachments->shouldReceive('getAttachmentContent')->withArgs([Mockery::any()])->andReturn($fileContent); $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $reader = $handler->getReader(); } catch (Exception $e) { @@ -449,7 +449,7 @@ class ConfigureMappingHandlerTest extends TestCase $job->save(); $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); $result = []; try { $result = $handler->getValuesForMapping($reader, $config, $columnConfig); @@ -478,7 +478,7 @@ class ConfigureMappingHandlerTest extends TestCase $job->save(); $handler = new ConfigureMappingHandler; - $handler->setJob($job); + $handler->setImportJob($job); $keys = array_keys(config('csv.import_roles')); foreach ($keys as $key) { $this->assertEquals($key, $handler->sanitizeColumnName($key)); diff --git a/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php index de87957d27..a0da33e888 100644 --- a/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php +++ b/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php @@ -159,7 +159,7 @@ class ConfigureRolesHandlerTest extends TestCase $repository->shouldReceive('setConfiguration')->once()->withArgs([Mockery::any(), $expected]); $handler = new ConfigureRolesHandler(); - $handler->setJob($job); + $handler->setImportJob($job); $handler->configureJob($data); } @@ -207,7 +207,7 @@ class ConfigureRolesHandlerTest extends TestCase $file = "one,two,,three\nfour,five,,six\none,three,X,three"; $reader = Reader::createFromString($file); $handler = new ConfigureRolesHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $handler->getExamplesFromFile($reader, $job->configuration); } catch (Exception $e) { @@ -321,7 +321,7 @@ class ConfigureRolesHandlerTest extends TestCase ]; $handler = new ConfigureRolesHandler(); - $handler->setJob($job); + $handler->setImportJob($job); try { $result = $handler->getNextData(); } catch (Exception $e) { @@ -373,7 +373,7 @@ class ConfigureRolesHandlerTest extends TestCase $attachments->shouldReceive('getAttachmentContent')->withArgs([Mockery::any()])->andReturn($fileContent); $handler = new ConfigureRolesHandler(); - $handler->setJob($job); + $handler->setImportJob($job); try { $reader = $handler->getReader(); } catch (Exception $e) { @@ -518,7 +518,7 @@ class ConfigureRolesHandlerTest extends TestCase ->withArgs([Mockery::any(), ['column-count' => 0]]); $handler = new ConfigureRolesHandler(); - $handler->setJob($job); + $handler->setImportJob($job); $handler->saveColumCount(); } diff --git a/tests/Unit/Support/Import/Configuration/File/ConfigureUploadHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/ConfigureUploadHandlerTest.php index baab7a6b62..f3dcee739e 100644 --- a/tests/Unit/Support/Import/Configuration/File/ConfigureUploadHandlerTest.php +++ b/tests/Unit/Support/Import/Configuration/File/ConfigureUploadHandlerTest.php @@ -80,7 +80,7 @@ class ConfigureUploadHandlerTest extends TestCase $repository->shouldReceive('setStage')->once()->withArgs([Mockery::any(), 'roles']); $handler = new ConfigureUploadHandler; - $handler->setJob($job); + $handler->setImportJob($job); $result = $handler->configureJob($data); $this->assertCount(0, $result); } @@ -127,7 +127,7 @@ class ConfigureUploadHandlerTest extends TestCase $repository->shouldReceive('setConfiguration')->once()->withArgs([Mockery::any(), $expectedConfig]); $handler = new ConfigureUploadHandler; - $handler->setJob($job); + $handler->setImportJob($job); $result = $handler->configureJob($data); $this->assertCount(1, $result); $this->assertEquals('You have selected an invalid account to import into.', $result->get('account')[0]); @@ -153,7 +153,7 @@ class ConfigureUploadHandlerTest extends TestCase $repository->shouldReceive('setConfiguration')->once()->withArgs([Mockery::any(), ['date-format' => 'Ymd']]); $handler = new ConfigureUploadHandler; - $handler->setJob($job); + $handler->setImportJob($job); $result = $handler->getNextData(); $expected = [ 'accounts' => [], diff --git a/tests/Unit/Support/Import/Configuration/File/NewFileJobHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/NewFileJobHandlerTest.php index 5315090034..1ed4f9e32b 100644 --- a/tests/Unit/Support/Import/Configuration/File/NewFileJobHandlerTest.php +++ b/tests/Unit/Support/Import/Configuration/File/NewFileJobHandlerTest.php @@ -85,7 +85,7 @@ class NewFileJobHandlerTest extends TestCase ]; $handler = new NewFileJobHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $messages = $handler->configureJob($data); } catch (FireflyException $e) { @@ -139,7 +139,7 @@ class NewFileJobHandlerTest extends TestCase ]; $handler = new NewFileJobHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $messages = $handler->configureJob($data); } catch (FireflyException $e) { @@ -190,7 +190,7 @@ class NewFileJobHandlerTest extends TestCase $repository->shouldReceive('setConfiguration')->withArgs([Mockery::any(), ['a' => 'b']])->once(); $handler = new NewFileJobHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $handler->storeConfiguration(); @@ -237,7 +237,7 @@ class NewFileJobHandlerTest extends TestCase $handler = new NewFileJobHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $result = $handler->validateAttachments(); @@ -288,7 +288,7 @@ class NewFileJobHandlerTest extends TestCase $handler = new NewFileJobHandler; - $handler->setJob($job); + $handler->setImportJob($job); try { $result = $handler->validateAttachments(); diff --git a/tests/Unit/Support/Import/Routine/File/ImportableConverterTest.php b/tests/Unit/Support/Import/Routine/File/ImportableConverterTest.php new file mode 100644 index 0000000000..9e8e32f608 --- /dev/null +++ b/tests/Unit/Support/Import/Routine/File/ImportableConverterTest.php @@ -0,0 +1,535 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Support\Import\Routine\File; + + +use Amount; +use Carbon\Carbon; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Support\Import\Placeholder\ImportTransaction; +use FireflyIII\Support\Import\Routine\File\AssetAccountMapper; +use FireflyIII\Support\Import\Routine\File\CurrencyMapper; +use FireflyIII\Support\Import\Routine\File\ImportableConverter; +use FireflyIII\Support\Import\Routine\File\OpposingAccountMapper; +use Mockery; +use Tests\TestCase; + +/** + * todo test foreign currency + * todo test budget (known and unknown) + * todo test category (known and unknown) + * todo test foreign currency + * + * Class ImportableConverterTest + */ +class ImportableConverterTest extends TestCase +{ + /** + * Basic test. Should match a withdrawal. Amount is negative. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasic(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '-45.67'; + $importable->date = '20180917'; + $importable->tags = ['a', 'b', 'c']; + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + $expense = $this->user()->accounts()->where('account_type_id', 4)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '-45.67', $nullAccount])->andReturn($expense); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + + // verify content of $result + $this->assertEquals('withdrawal', $result[0]['type']); + $this->assertEquals('2018-09-17', $result[0]['date']); + $this->assertEquals($importable->tags, $result[0]['tags']); + $this->assertEquals($usd->id, $result[0]['transactions'][0]['currency_id']); + } + + /** + * Two asset accounts mean its a transfer. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicDefaultCurrency(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '45.67'; + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + $other = $this->user()->accounts()->where('account_type_id', 3)->where('id', '!=', $asset->id)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '45.67', $nullAccount])->andReturn($other); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn(null); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + + // verify content of $result + $today = new Carbon(); + $this->assertEquals('transfer', $result[0]['type']); + $this->assertEquals($today->format('Y-m-d'), $result[0]['date']); + $this->assertEquals([], $result[0]['tags']); + $this->assertEquals($euro->id, $result[0]['transactions'][0]['currency_id']); + } + + /** + * Positive amount, so transaction is a deposit. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicDeposit(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '45.67'; + $importable->date = '20180917'; + $importable->meta['date-book'] = '2018-01-02'; + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + $revenue = $this->user()->accounts()->where('account_type_id', 5)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '45.67', $nullAccount])->andReturn($revenue); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + + // verify content of $result + $this->assertEquals('deposit', $result[0]['type']); + $this->assertEquals('2018-09-17', $result[0]['date']); + $this->assertEquals([], $result[0]['tags']); + $this->assertEquals($usd->id, $result[0]['transactions'][0]['currency_id']); + $this->assertEquals($revenue->id, $result[0]['transactions'][0]['source_id']); + $this->assertEquals($asset->id, $result[0]['transactions'][0]['destination_id']); + $this->assertEquals($importable->meta['date-book'], $result[0]['book_date']); + + } + + /** + * Source and destination are the same. Should result in error message. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicSameAssets(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '-45.67'; + $importable->date = '20180917'; + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '-45.67', $nullAccount])->andReturn($asset); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + $repository->shouldReceive('addErrorMessage')->withArgs( + [Mockery::any(), + 'Row #1: Source ("' . $asset->name . '", #' . $asset->id . ') and destination ("' . $asset->name . '", #' . $asset->id . ') are the same account.'] + )->once(); + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + $this->assertEquals([], $result); + } + + /** + * Two asset accounts mean its a transfer. This has a positive amount. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicTransfer(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '45.67'; + $importable->date = '20180917'; + $importable->billId = 2; // will be ignored because it's not valid. + $importable->billName = 'Some Bill'; // will be included because bill ID is not valid. + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + $other = $this->user()->accounts()->where('account_type_id', 3)->where('id', '!=', $asset->id)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '45.67', $nullAccount])->andReturn($other); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + + // verify content of $result + $this->assertEquals('transfer', $result[0]['type']); + $this->assertEquals('2018-09-17', $result[0]['date']); + $this->assertEquals([], $result[0]['tags']); + $this->assertNull($result[0]['bill_id']); + $this->assertEquals($importable->billName, $result[0]['bill_name']); + $this->assertEquals($usd->id, $result[0]['transactions'][0]['currency_id']); + // since amount is positive, $asset recieves the money + $this->assertEquals($other->id, $result[0]['transactions'][0]['source_id']); + $this->assertEquals($asset->id, $result[0]['transactions'][0]['destination_id']); + } + + /** + * Transfer with negative amount flows the other direction. See source_id and destination_id + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicTransferNegative(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '-45.67'; + $importable->date = '20180917'; + $importable->billId = 3; // is added to array of valid values, see below. + $importable->billName = 'Some bill'; // will be ignored because ID is valid. + $importables = [$importable]; + + $validMappings = [ + 'bill-id' => [3], + ]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 3)->first(); + $other = $this->user()->accounts()->where('account_type_id', 3)->where('id', '!=', $asset->id)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '-45.67', $nullAccount])->andReturn($other); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $converter->setMappedValues($validMappings); + $result = $converter->convert($importables); + + // verify content of $result + $this->assertEquals('transfer', $result[0]['type']); + $this->assertEquals('2018-09-17', $result[0]['date']); + $this->assertEquals([], $result[0]['tags']); + $this->assertEquals(3, $result[0]['bill_id']); + $this->assertNull($result[0]['bill_name']); + $this->assertEquals($usd->id, $result[0]['transactions'][0]['currency_id']); + // since amount is negative, $asset sends the money + $this->assertEquals($asset->id, $result[0]['transactions'][0]['source_id']); + $this->assertEquals($other->id, $result[0]['transactions'][0]['destination_id']); + } + + /** + * When source and dest are weird account types, will give error. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testBasicWeirdAccounts(): void + { + $nullAccount = ['name' => null, 'iban' => null, 'number' => null, 'bic' => null]; + $importable = new ImportTransaction; + $importable->amount = '-45.67'; + $importable->date = '20180917'; + $importables = [$importable]; + + $job = $this->user()->importJobs()->first(); + $job->configuration = [ + 'date-format' => 'Ymd', + ]; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + + // get default currency + $euro = TransactionCurrency::whereCode('EUR')->first(); + $usd = TransactionCurrency::whereCode('USD')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + + // set user and config: + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + // respond to mapping call: + $asset = $this->user()->accounts()->where('account_type_id', 6)->first(); + $other = $this->user()->accounts()->where('account_type_id', 2)->where('id', '!=', $asset)->first(); + + $assetMapper->shouldReceive('map')->once()->withArgs([null, $nullAccount])->andReturn($asset); + $opposingMapper->shouldReceive('map')->once()->withArgs([null, '-45.67', $nullAccount])->andReturn($other); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['name' => null, 'code' => null, 'symbol' => null]])->andReturn($usd); + $currencyMapper->shouldReceive('map')->once()->withArgs([null, ['code' => null]])->andReturn(null); + $repository->shouldReceive('addErrorMessage')->withArgs( + [Mockery::any(), 'Row #1: Cannot determine transaction type. Source account is a Initial balance account, destination is a Cash account'] + )->once(); + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + $this->assertEquals([], $result); + } + + /** + * Submit no amount information. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testEmpty(): void + { + + $job = $this->user()->importJobs()->first(); + $job->configuration = []; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + $euro = TransactionCurrency::whereCode('EUR')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + + $converter = new ImportableConverter; + $converter->setImportJob($job); + } + + /** + * Basic input until it stops crashing. + * + * @covers \FireflyIII\Support\Import\Routine\File\ImportableConverter + */ + public function testNoAmount(): void + { + $importable = new ImportTransaction; + $importables = [$importable]; + $job = $this->user()->importJobs()->first(); + $job->configuration = []; + $job->save(); + + // mock used classes: + $repository = $this->mock(ImportJobRepositoryInterface::class); + $assetMapper = $this->mock(AssetAccountMapper::class); + $opposingMapper = $this->mock(OpposingAccountMapper::class); + $currencyMapper = $this->mock(CurrencyMapper::class); + $euro = TransactionCurrency::whereCode('EUR')->first(); + Amount::shouldReceive('getDefaultCurrencyByUser')->andReturn($euro)->once(); + $repository->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setUser')->once(); + $opposingMapper->shouldReceive('setUser')->once(); + $currencyMapper->shouldReceive('setUser')->once(); + $assetMapper->shouldReceive('setDefaultAccount')->withArgs([0])->once(); + $repository->shouldReceive('addErrorMessage')->withArgs([Mockery::any(), 'Row #1: No transaction amount information.'])->once(); + + $converter = new ImportableConverter; + $converter->setImportJob($job); + $result = $converter->convert($importables); + $this->assertEquals([], $result); + } + +} \ No newline at end of file