diff --git a/app/Import/Storage/ImportStorage.php b/app/Import/Storage/ImportStorage.php index 929f5c5cb8..1069bedcbe 100644 --- a/app/Import/Storage/ImportStorage.php +++ b/app/Import/Storage/ImportStorage.php @@ -13,6 +13,8 @@ namespace FireflyIII\Import\Storage; use Amount; use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Import\Object\ImportAccount; use FireflyIII\Import\Object\ImportJournal; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; @@ -99,150 +101,11 @@ class ImportStorage * @var ImportJournal $object */ foreach ($this->objects as $index => $object) { - sleep(1); - Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $object->description)); - - $errors = new MessageBag; - - // create the asset account - $asset = $object->asset->getAccount(); - $opposing = new Account; - $amount = $object->getAmount(); - $currency = $object->getCurrency()->getTransactionCurrency(); - $date = $object->getDate($this->dateFormat); - $transactionType = new TransactionType; - - if (is_null($currency->id)) { - $currency = $this->defaultCurrency; + try { + $this->storeImportJournal($index, $object); + } catch (FireflyException $e) { + $this->errors->push($e->getMessage()); } - - if (bccomp($amount, '0') === -1) { - // amount is negative, it's a withdrawal, opposing is an expense: - Log::debug(sprintf('%s is negative, create opposing expense account.', $amount)); - $object->opposing->setExpectedType(AccountType::EXPENSE); - $opposing = $object->opposing->getAccount(); - $transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); - } - if (bccomp($amount, '0') === 1) { - Log::debug(sprintf('%s is positive, create opposing revenue account.', $amount)); - // amount is positive, it's a deposit, opposing is an revenue: - $object->opposing->setExpectedType(AccountType::REVENUE); - $opposing = $object->opposing->getAccount(); - $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); - } - - // if opposing is an asset account, it's a transfer: - if ($opposing->accountType->type === AccountType::ASSET) { - Log::debug(sprintf('Opposing account #%d %s is an asset account, make transfer.', $opposing->id, $opposing->name)); - $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); - } - - $this->job->addStepsDone(1); - - $journal = new TransactionJournal; - $journal->user_id = $this->job->user_id; - $journal->transaction_type_id = $transactionType->id; - $journal->transaction_currency_id = $currency->id; - $journal->description = $object->description; - $journal->date = $date->format('Y-m-d'); - $journal->order = 0; - $journal->tag_count = 0; - $journal->encrypted = 0; - $journal->completed = false; - - if (rand(1, 10) === 3) { - $journal->date = null; - $journal->description = null; - } - - if (!$journal->save()) { - $errorText = join(', ', $journal->getErrors()->all()); - $this->addErrorToJob($index, sprintf('Error storing line #%d: %s', $index, $errorText)); - Log::error(sprintf('Could not store line #%d: %s', $index, $errorText)); - // add the rest of the steps: - $this->job->addStepsDone(3); - - continue; - } - $journal->setMeta('importHash', $object->hash); - Log::debug(sprintf('Created journal with ID #%d', $journal->id)); - - // create transactions: - $one = new Transaction; - $one->account_id = $asset->id; - $one->transaction_journal_id = $journal->id; - $one->transaction_currency_id = $currency->id; - $one->amount = $amount; - $one->save(); - if (is_null($one->id)) { - $errorText = join(', ', $one->getErrors()->all()); - $errors->add('no-key', sprintf('Error storing transaction one for journal %d: %s', $journal->id, $errorText)); - } - Log::debug(sprintf('Created transaction with ID #%d and account #%d', $one->id, $asset->id)); - - $two = new Transaction; - $two->account_id = $opposing->id; - $two->transaction_journal_id = $journal->id; - $two->transaction_currency_id = $currency->id; - $two->amount = Steam::opposite($amount); - $two->save(); - if (is_null($two->id)) { - $errorText = join(', ', $two->getErrors()->all()); - $errors->add('no-key', sprintf('Error storing transaction one for journal %d: %s', $journal->id, $errorText)); - } - Log::debug(sprintf('Created transaction with ID #%d and account #%d', $two->id, $opposing->id)); - - $this->job->addStepsDone(1); - - // category - $category = $object->category->getCategory(); - if (!is_null($category->id)) { - Log::debug(sprintf('Linked category #%d to journal #%d', $category->id, $journal->id)); - $journal->categories()->save($category); - } - - // budget - $budget = $object->budget->getBudget(); - if (!is_null($budget->id)) { - Log::debug(sprintf('Linked budget #%d to journal #%d', $budget->id, $journal->id)); - $journal->budgets()->save($budget); - } - // bill - $bill = $object->bill->getBill(); - if (!is_null($bill->id)) { - Log::debug(sprintf('Linked bill #%d to journal #%d', $bill->id, $journal->id)); - $journal->bill()->associate($bill); - $journal->save(); - } - - // all other date fields as meta thing: - foreach ($object->metaDates as $name => $value) { - try { - $date = new Carbon($value); - $journal->setMeta($name, $date); - } catch (\Exception $e) { - // don't care, ignore: - Log::warning(sprintf('Could not parse "%s" into a valid Date object for field %s', $value, $name)); - } - } - - // sepa thing as note: - if (strlen($object->notes) > 0) { - $journal->setMeta('notes', $object->notes); - } - - // set journal completed: - $journal->completed = true; - $journal->save(); - - $this->job->addStepsDone(1); - - // run rules: - $this->applyRules($journal); - $this->job->addStepsDone(1); - - $this->journals->push($journal); - $this->errors->push($errors); } @@ -273,15 +136,85 @@ class ImportStorage } /** - * @param int $index - * @param string $error + * @param int $journalId + * @param int $accountId + * @param int $currencyId + * @param string $amount + * + * @return bool + * @throws FireflyException */ - private function addErrorToJob(int $index, string $error) + private function createTransaction(int $journalId, int $accountId, int $currencyId, string $amount): bool { - $extended = $this->job->extended_status; - $extended['errors'][$index][] = $error; - $this->job->extended_status = $extended; - $this->job->save(); + $transaction = new Transaction; + $transaction->account_id = $accountId; + $transaction->transaction_journal_id = $journalId; + $transaction->transaction_currency_id = $currencyId; + $transaction->amount = $amount; + $transaction->save(); + if (is_null($transaction->id)) { + $errorText = join(', ', $one->getErrors()->all()); + throw new FireflyException($errorText); + } + Log::debug(sprintf('Created transaction with ID #%d and account #%d', $transaction->id, $accountId)); + + return true; + } + + /** + * @param ImportJournal $importJournal + * + * @return TransactionCurrency + */ + private function getCurrency(ImportJournal $importJournal): TransactionCurrency + { + $currency = $importJournal->getCurrency()->getTransactionCurrency(); + if (is_null($currency->id)) { + $currency = $this->defaultCurrency; + } + + return $currency; + } + + /** + * @param ImportAccount $account + * @param $amount + * + * @return Account + */ + private function getOpposingAccount(ImportAccount $account, $amount): Account + { + if (bccomp($amount, '0') === -1) { + Log::debug(sprintf('%s is negative, create opposing expense account.', $amount)); + $account->setExpectedType(AccountType::EXPENSE); + + return $account->getAccount(); + } + Log::debug(sprintf('%s is positive, create opposing revenue account.', $amount)); + // amount is positive, it's a deposit, opposing is an revenue: + $account->setExpectedType(AccountType::REVENUE); + + return $account->getAccount(); + + } + + /** + * @param string $amount + * + * @return TransactionType + */ + private function getTransactionType(string $amount): TransactionType + { + $transactionType = new TransactionType(); + // amount is negative, it's a withdrawal, opposing is an expense: + if (bccomp($amount, '0') === -1) { + $transactionType = TransactionType::whereType(TransactionType::WITHDRAWAL)->first(); + } + if (bccomp($amount, '0') === 1) { + $transactionType = TransactionType::whereType(TransactionType::DEPOSIT)->first(); + } + + return $transactionType; } /** @@ -306,5 +239,110 @@ class ImportStorage } + private function storeImportJournal(int $index, ImportJournal $importJournal): bool + { + sleep(1); + Log::debug(sprintf('Going to store object #%d with description "%s"', $index, $importJournal->description)); + + $errors = new MessageBag; + $asset = $importJournal->asset->getAccount(); + $amount = $importJournal->getAmount(); + $currency = $this->getCurrency($importJournal); + $date = $importJournal->getDate($this->dateFormat); + $transactionType = $this->getTransactionType($amount); + $opposing = $this->getOpposingAccount($importJournal->opposing, $amount); + + // if opposing is an asset account, it's a transfer: + if ($opposing->accountType->type === AccountType::ASSET) { + Log::debug(sprintf('Opposing account #%d %s is an asset account, make transfer.', $opposing->id, $opposing->name)); + $transactionType = TransactionType::whereType(TransactionType::TRANSFER)->first(); + } + + /*** First step done! */ + $this->job->addStepsDone(1); + + // create a journal: + $journal = new TransactionJournal; + $journal->user_id = $this->job->user_id; + $journal->transaction_type_id = $transactionType->id; + $journal->transaction_currency_id = $currency->id; + $journal->description = $importJournal->description; + $journal->date = $date->format('Y-m-d'); + $journal->order = 0; + $journal->tag_count = 0; + $journal->encrypted = 0; + $journal->completed = false; + + if (!$journal->save()) { + $errorText = join(', ', $journal->getErrors()->all()); + $this->job->addStepsDone(3); + throw new FireflyException($errorText); + } + + // save meta data: + $journal->setMeta('importHash', $importJournal->hash); + Log::debug(sprintf('Created journal with ID #%d', $journal->id)); + + // create transactions: + $this->createTransaction($journal->id, $asset->id, $currency->id, $amount); + $this->createTransaction($journal->id, $opposing->id, $currency->id, Steam::opposite($amount)); + + /*** Another step done! */ + $this->job->addStepsDone(1); + + // store meta object things: + // category + $category = $object->category->getCategory(); + if (!is_null($category->id)) { + Log::debug(sprintf('Linked category #%d to journal #%d', $category->id, $journal->id)); + $journal->categories()->save($category); + } + + // budget + $budget = $object->budget->getBudget(); + if (!is_null($budget->id)) { + Log::debug(sprintf('Linked budget #%d to journal #%d', $budget->id, $journal->id)); + $journal->budgets()->save($budget); + } + // bill + $bill = $object->bill->getBill(); + if (!is_null($bill->id)) { + Log::debug(sprintf('Linked bill #%d to journal #%d', $bill->id, $journal->id)); + $journal->bill()->associate($bill); + $journal->save(); + } + + // all other date fields as meta thing: + foreach ($object->metaDates as $name => $value) { + try { + $date = new Carbon($value); + $journal->setMeta($name, $date); + } catch (\Exception $e) { + // don't care, ignore: + Log::warning(sprintf('Could not parse "%s" into a valid Date object for field %s', $value, $name)); + } + } + + // sepa thing as note: + if (strlen($object->notes) > 0) { + $journal->setMeta('notes', $object->notes); + } + + // set journal completed: + $journal->completed = true; + $journal->save(); + + $this->job->addStepsDone(1); + + // run rules: + $this->applyRules($journal); + $this->job->addStepsDone(1); + + $this->journals->push($journal); + $this->errors->push($errors); + + return true; + } + } \ No newline at end of file