mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-17 09:51:40 +00:00
User can submit new journal through API.
This commit is contained in:
@@ -31,10 +31,10 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\User;
|
||||
use FireflyIII\Validation\AccountValidator;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@@ -46,6 +46,8 @@ class TransactionFactory
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $accountRepository;
|
||||
/** @var AccountValidator */
|
||||
private $accountValidator;
|
||||
/** @var TransactionJournal */
|
||||
private $journal;
|
||||
/** @var User */
|
||||
@@ -60,6 +62,7 @@ class TransactionFactory
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
|
||||
}
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->accountValidator = app(AccountValidator::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,16 +113,17 @@ class TransactionFactory
|
||||
*/
|
||||
public function createPair(NullArrayObject $data, TransactionCurrency $currency, ?TransactionCurrency $foreignCurrency): Collection
|
||||
{
|
||||
$sourceAccount = $this->getAccount('source', $data['source'], (int)$data['source_id'], $data['source_name']);
|
||||
$destinationAccount = $this->getAccount('destination', $data['destination'], (int)$data['destination_id'], $data['destination_name']);
|
||||
$amount = $this->getAmount($data['amount']);
|
||||
$foreignAmount = $this->getForeignAmount($data['foreign_amount']);
|
||||
// validate source and destination using a new Validator.
|
||||
$this->validateAccounts($data);
|
||||
|
||||
$this->makeDramaOverAccountTypes($sourceAccount, $destinationAccount);
|
||||
// create or get source and destination accounts:
|
||||
$sourceAccount = $this->getAccount('source', (int)$data['source_id'], $data['source_name']);
|
||||
$destinationAccount = $this->getAccount('destination', (int)$data['destination_id'], $data['destination_name']);
|
||||
|
||||
|
||||
$one = $this->create($sourceAccount, $currency, app('steam')->negative($amount));
|
||||
$two = $this->create($destinationAccount, $currency, app('steam')->positive($amount));
|
||||
$amount = $this->getAmount($data['amount']);
|
||||
$foreignAmount = $this->getForeignAmount($data['foreign_amount']);
|
||||
$one = $this->create($sourceAccount, $currency, app('steam')->negative($amount));
|
||||
$two = $this->create($destinationAccount, $currency, app('steam')->positive($amount));
|
||||
|
||||
$one->reconciled = $data['reconciled'] ?? false;
|
||||
$two->reconciled = $data['reconciled'] ?? false;
|
||||
@@ -128,8 +132,8 @@ class TransactionFactory
|
||||
if (null !== $foreignCurrency) {
|
||||
$one->foreign_currency_id = $foreignCurrency->id;
|
||||
$two->foreign_currency_id = $foreignCurrency->id;
|
||||
$one->foreign_amount = $foreignAmount;
|
||||
$two->foreign_amount = $foreignAmount;
|
||||
$one->foreign_amount = app('steam')->negative($foreignAmount);
|
||||
$two->foreign_amount = app('steam')->positive($foreignAmount);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,18 +145,21 @@ class TransactionFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $direction
|
||||
* @param Account|null $source
|
||||
* @param int|null $sourceId
|
||||
* @param string|null $sourceName
|
||||
* @param string $direction
|
||||
* @param int|null $accountId
|
||||
* @param string|null $accountName
|
||||
*
|
||||
* @return Account
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function getAccount(string $direction, ?Account $source, ?int $sourceId, ?string $sourceName): Account
|
||||
public function getAccount(string $direction, ?int $accountId, ?string $accountName): Account
|
||||
{
|
||||
Log::debug(sprintf('Now in getAccount(%s)', $direction));
|
||||
Log::debug(sprintf('Parameters: ((account), %s, %s)', var_export($sourceId, true), var_export($sourceName, true)));
|
||||
// some debug logging:
|
||||
Log::debug(sprintf('Now in getAccount(%s, %d, %s)', $direction, $accountId, $accountName));
|
||||
|
||||
// final result:
|
||||
$result = null;
|
||||
|
||||
// expected type of source account, in order of preference
|
||||
/** @var array $array */
|
||||
$array = config('firefly.expected_source_types');
|
||||
@@ -161,64 +168,62 @@ class TransactionFactory
|
||||
|
||||
// and now try to find it, based on the type of transaction.
|
||||
$transactionType = $this->journal->transactionType->type;
|
||||
Log::debug(
|
||||
sprintf(
|
||||
'Based on the fact that the transaction is a %s, the %s account should be in: %s', $transactionType, $direction,
|
||||
implode(', ', $expectedTypes[$transactionType])
|
||||
)
|
||||
);
|
||||
$message = 'Based on the fact that the transaction is a %s, the %s account should be in: %s';
|
||||
Log::debug(sprintf($message, $transactionType, $direction, implode(', ', $expectedTypes[$transactionType])));
|
||||
|
||||
// first attempt, check the "source" object.
|
||||
if (null !== $source && $source->user_id === $this->user->id && \in_array($source->accountType->type, $expectedTypes[$transactionType], true)) {
|
||||
Log::debug(sprintf('Found "account" object for %s: #%d, %s', $direction, $source->id, $source->name));
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
// second attempt, find by ID.
|
||||
if (null !== $sourceId) {
|
||||
$source = $this->accountRepository->findNull($sourceId);
|
||||
if (null !== $source && \in_array($source->accountType->type, $expectedTypes[$transactionType], true)) {
|
||||
// first attempt, find by ID.
|
||||
if (null !== $accountId) {
|
||||
$search = $this->accountRepository->findNull($accountId);
|
||||
if (null !== $search && in_array($search->accountType->type, $expectedTypes[$transactionType], true)) {
|
||||
Log::debug(
|
||||
sprintf('Found "account_id" object for %s: #%d, "%s" of type %s', $direction, $source->id, $source->name, $source->accountType->type)
|
||||
sprintf('Found "account_id" object for %s: #%d, "%s" of type %s', $direction, $search->id, $search->name, $search->accountType->type)
|
||||
);
|
||||
|
||||
return $source;
|
||||
$result = $search;
|
||||
}
|
||||
}
|
||||
|
||||
// third attempt, find by name.
|
||||
if (null !== $sourceName) {
|
||||
// second attempt, find by name.
|
||||
if (null === $result && null !== $accountName) {
|
||||
Log::debug('Found nothing by account ID.');
|
||||
// find by preferred type.
|
||||
$source = $this->accountRepository->findByName($sourceName, [$expectedTypes[$transactionType][0]]);
|
||||
// or any type.
|
||||
$source = $source ?? $this->accountRepository->findByName($sourceName, $expectedTypes[$transactionType]);
|
||||
$source = $this->accountRepository->findByName($accountName, [$expectedTypes[$transactionType][0]]);
|
||||
// or any expected type.
|
||||
$source = $source ?? $this->accountRepository->findByName($accountName, $expectedTypes[$transactionType]);
|
||||
|
||||
if (null !== $source) {
|
||||
Log::debug(sprintf('Found "account_name" object for %s: #%d, %s', $direction, $source->id, $source->name));
|
||||
|
||||
return $source;
|
||||
$result = $source;
|
||||
}
|
||||
}
|
||||
if (null === $sourceName && \in_array(AccountType::CASH, $expectedTypes[$transactionType], true)) {
|
||||
return $this->accountRepository->getCashAccount();
|
||||
}
|
||||
$sourceName = $sourceName ?? '(no name)';
|
||||
// final attempt, create it.
|
||||
$preferredType = $expectedTypes[$transactionType][0];
|
||||
if (AccountType::ASSET === $preferredType) {
|
||||
throw new FireflyException(sprintf('TransactionFactory: Cannot create asset account with ID #%d or name "%s".', $sourceId, $sourceName));
|
||||
|
||||
// return cash account.
|
||||
if (null === $result && null === $accountName
|
||||
&& in_array(AccountType::CASH, $expectedTypes[$transactionType], true)) {
|
||||
$result = $this->accountRepository->getCashAccount();
|
||||
}
|
||||
|
||||
return $this->accountRepository->store(
|
||||
[
|
||||
'account_type_id' => null,
|
||||
'accountType' => $preferredType,
|
||||
'name' => $sourceName,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
]
|
||||
);
|
||||
// return new account.
|
||||
if (null === $result) {
|
||||
$accountName = $accountName ?? '(no name)';
|
||||
// final attempt, create it.
|
||||
$preferredType = $expectedTypes[$transactionType][0];
|
||||
if (AccountType::ASSET === $preferredType) {
|
||||
throw new FireflyException(sprintf('TransactionFactory: Cannot create asset account with ID #%d or name "%s".', $accountId, $accountName));
|
||||
}
|
||||
|
||||
$result = $this->accountRepository->store(
|
||||
[
|
||||
'account_type_id' => null,
|
||||
'accountType' => $preferredType,
|
||||
'name' => $accountName,
|
||||
'active' => true,
|
||||
'iban' => null,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,6 +251,7 @@ class TransactionFactory
|
||||
*/
|
||||
public function getForeignAmount(?string $amount): ?string
|
||||
{
|
||||
$result = null;
|
||||
if (null === $amount) {
|
||||
Log::debug('No foreign amount info in array. Return NULL');
|
||||
|
||||
@@ -266,31 +272,6 @@ class TransactionFactory
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will throw a Firefly III Exception of the source and destination account types are not OK.
|
||||
*
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @param Account $source
|
||||
* @param Account $destination
|
||||
*/
|
||||
public function makeDramaOverAccountTypes(Account $source, Account $destination): void
|
||||
{
|
||||
// if the source is X, then Y is allowed as destination.
|
||||
$combinations = config('firefly.source_dests');
|
||||
$sourceType = $source->accountType->type;
|
||||
$destType = $destination->accountType->type;
|
||||
$journalType = $this->journal->transactionType->type;
|
||||
$allowed = $combinations[$journalType][$sourceType] ?? [];
|
||||
if (!\in_array($destType, $allowed, true)) {
|
||||
throw new FireflyException(
|
||||
sprintf(
|
||||
'Journal of type "%s" has a source account of type "%s" and cannot accept a "%s"-account as destination, but only accounts of: %s', $journalType, $sourceType,
|
||||
$destType, implode(', ', $combinations[$journalType][$sourceType])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
@@ -308,4 +289,33 @@ class TransactionFactory
|
||||
$this->user = $user;
|
||||
$this->accountRepository->setUser($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullArrayObject $data
|
||||
*
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateAccounts(NullArrayObject $data): void
|
||||
{
|
||||
$transactionType = $data['type'] ?? 'invalid';
|
||||
$this->accountValidator->setTransactionType($transactionType);
|
||||
|
||||
// validate source account.
|
||||
$sourceId = isset($data['source_id']) ? (int)$data['source_id'] : null;
|
||||
$sourceName = $data['source_name'] ?? null;
|
||||
$validSource = $this->accountValidator->validateSource($sourceId, $sourceName);
|
||||
|
||||
// do something with result:
|
||||
if (false === $validSource) {
|
||||
throw new FireflyException($this->accountValidator->sourceError);
|
||||
}
|
||||
// validate destination account
|
||||
$destinationId = isset($data['destination_id']) ? (int)$data['destination_id'] : null;
|
||||
$destinationName = $data['destination_name'] ?? null;
|
||||
$validDestination = $this->accountValidator->validateDestination($destinationId, $destinationName);
|
||||
// do something with result:
|
||||
if (false === $validDestination) {
|
||||
throw new FireflyException($this->accountValidator->destError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
84
app/Factory/TransactionGroupFactory.php
Normal file
84
app/Factory/TransactionGroupFactory.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* TransactionGroupFactory.php
|
||||
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This file is part of Firefly III.
|
||||
*
|
||||
* Firefly III is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Firefly III is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Factory;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\User;
|
||||
|
||||
/**
|
||||
* Class TransactionGroupFactory
|
||||
*/
|
||||
class TransactionGroupFactory
|
||||
{
|
||||
/** @var TransactionJournalFactory */
|
||||
private $journalFactory;
|
||||
/** @var User The user */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* TransactionGroupFactory constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->journalFactory = app(TransactionJournalFactory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new transaction journal.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function create(array $data): TransactionGroup
|
||||
{
|
||||
$this->journalFactory->setUser($this->user);
|
||||
$collection = $this->journalFactory->create($data);
|
||||
$title = $data['group_title'] ?? null;
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $collection->first();
|
||||
$group = new TransactionGroup;
|
||||
$group->user()->associate($first->user);
|
||||
$group->title = $title ?? $first->description;
|
||||
$group->save();
|
||||
|
||||
$group->transactionJournals()->saveMany($collection);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function setUser(User $user): void
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@@ -26,9 +26,9 @@ namespace FireflyIII\Factory;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
@@ -77,14 +77,25 @@ class TransactionJournalFactory
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date',
|
||||
'due_date', 'recurrence_id', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id', 'importHash', 'importHashV2',
|
||||
'external_id', 'sepa-batch-id', 'original-source'];
|
||||
$this->fields = [
|
||||
// sepa
|
||||
'sepa_cc', 'sepa_ct_op', 'sepa_ct_id',
|
||||
'sepa_db', 'sepa_country', 'sepa_ep',
|
||||
'sepa_ci', 'sepa_batch_id',
|
||||
|
||||
// dates
|
||||
'interest_date', 'book_date', 'process_date',
|
||||
'due_date', 'payment_date', 'invoice_date',
|
||||
|
||||
// others
|
||||
'recurrence_id', 'internal_reference', 'bunq_payment_id',
|
||||
'import_hash', 'import_hash_v2', 'external_id', 'original_source'];
|
||||
|
||||
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this)));
|
||||
}
|
||||
|
||||
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
|
||||
$this->typeRepository = app(TransactionTypeRepositoryInterface::class);
|
||||
$this->transactionFactory = app(TransactionFactory::class);
|
||||
@@ -97,26 +108,22 @@ class TransactionJournalFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a new transaction journal.
|
||||
* Store a new (set of) transaction journals.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return TransactionGroup
|
||||
* @throws Exception
|
||||
* @return Collection
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function create(array $data): TransactionGroup
|
||||
public function create(array $data): Collection
|
||||
{
|
||||
// convert to special object.
|
||||
$data = new NullArrayObject($data);
|
||||
|
||||
Log::debug('Start of TransactionJournalFactory::create()');
|
||||
$collection = new Collection;
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
$type = $this->typeRepository->findTransactionType(null, $data['type']);
|
||||
$carbon = $data['date'] ?? new Carbon;
|
||||
$carbon->setTimezone(config('app.timezone'));
|
||||
|
||||
Log::debug(sprintf('Going to store a %s.', $type->type));
|
||||
|
||||
if (0 === \count($transactions)) {
|
||||
if (0 === count($transactions)) {
|
||||
Log::error('There are no transactions in the array, the TransactionJournalFactory cannot continue.');
|
||||
|
||||
return new Collection;
|
||||
@@ -124,79 +131,15 @@ class TransactionJournalFactory
|
||||
|
||||
/** @var array $row */
|
||||
foreach ($transactions as $index => $row) {
|
||||
$transaction = new NullArrayObject($row);
|
||||
Log::debug(sprintf('Now creating journal %d/%d', $index + 1, \count($transactions)));
|
||||
/** Get basic fields */
|
||||
Log::debug(sprintf('Now creating journal %d/%d', $index + 1, count($transactions)));
|
||||
|
||||
$currency = $this->currencyRepository->findCurrency(
|
||||
$transaction['currency'], (int)$transaction['currency_id'], $transaction['currency_code']
|
||||
);
|
||||
$foreignCurrency = $this->findForeignCurrency($transaction);
|
||||
|
||||
$bill = $this->billRepository->findBill($transaction['bill'], (int)$transaction['bill_id'], $transaction['bill_name']);
|
||||
$billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null;
|
||||
$description = app('steam')->cleanString((string)$transaction['description']);
|
||||
|
||||
/** Create a basic journal. */
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $type->id,
|
||||
'bill_id' => $billId,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'description' => '' === $description ? '(empty description)' : $description,
|
||||
'date' => $carbon->format('Y-m-d H:i:s'),
|
||||
'order' => 0,
|
||||
'tag_count' => 0,
|
||||
'completed' => 0,
|
||||
]
|
||||
);
|
||||
Log::debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description));
|
||||
|
||||
/** Create two transactions. */
|
||||
$this->transactionFactory->setJournal($journal);
|
||||
$this->transactionFactory->createPair($transaction, $currency, $foreignCurrency);
|
||||
|
||||
// verify that journal has two transactions. Otherwise, delete and cancel.
|
||||
$count = $journal->transactions()->count();
|
||||
if (2 !== $count) {
|
||||
// @codeCoverageIgnoreStart
|
||||
Log::error(sprintf('The journal unexpectedly has %d transaction(s). This is not OK. Cancel operation.', $count));
|
||||
$journal->delete();
|
||||
|
||||
return new Collection;
|
||||
// @codeCoverageIgnoreEnd
|
||||
$journal = $this->createJournal(new NullArrayObject($row));
|
||||
if (null !== $journal) {
|
||||
$collection->push($journal);
|
||||
}
|
||||
$journal->completed =true;
|
||||
$journal->save();
|
||||
|
||||
/** Link all other data to the journal. */
|
||||
|
||||
/** Link budget */
|
||||
$this->storeBudget($journal, $transaction);
|
||||
|
||||
/** Link category */
|
||||
$this->storeCategory($journal, $transaction);
|
||||
|
||||
/** Set notes */
|
||||
$this->storeNote($journal, $transaction['notes']);
|
||||
|
||||
/** Set piggy bank */
|
||||
$this->storePiggyEvent($journal, $transaction);
|
||||
|
||||
/** Set tags */
|
||||
$this->storeTags($journal, $transaction['tags']);
|
||||
|
||||
/** set all meta fields */
|
||||
$this->storeMetaFields($journal, $transaction);
|
||||
|
||||
$collection->push($journal);
|
||||
}
|
||||
|
||||
$group = $this->storeGroup($collection, $data['group_title']);
|
||||
|
||||
return $group;
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,31 +158,6 @@ class TransactionJournalFactory
|
||||
$this->piggyRepository->setUser($this->user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join multiple journals in a group.
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @param string|null $title
|
||||
*
|
||||
* @return TransactionGroup|null
|
||||
*/
|
||||
public function storeGroup(Collection $collection, ?string $title): ?TransactionGroup
|
||||
{
|
||||
if ($collection->count() < 2) {
|
||||
return null; // @codeCoverageIgnore
|
||||
}
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $collection->first();
|
||||
$group = new TransactionGroup;
|
||||
$group->user()->associate($first->user);
|
||||
$group->title = $title ?? $first->description;
|
||||
$group->save();
|
||||
|
||||
$group->transactionJournals()->saveMany($collection);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a piggy bank to this journal.
|
||||
*
|
||||
@@ -332,6 +250,88 @@ class TransactionJournalFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullArrayObject $row
|
||||
*
|
||||
* @return TransactionJournal|null
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function createJournal(NullArrayObject $row): ?TransactionJournal
|
||||
{
|
||||
$row['import_hash_v2'] = $this->hashArray($row);
|
||||
|
||||
/** Get basic fields */
|
||||
$type = $this->typeRepository->findTransactionType(null, $row['type']);
|
||||
$carbon = $row['date'] ?? new Carbon;
|
||||
$currency = $this->currencyRepository->findCurrency((int)$row['currency_id'], $row['currency_code']);
|
||||
$foreignCurrency = $this->findForeignCurrency($row);
|
||||
$bill = $this->billRepository->findBill((int)$row['bill_id'], $row['bill_name']);
|
||||
$billId = TransactionType::WITHDRAWAL === $type->type && null !== $bill ? $bill->id : null;
|
||||
$description = app('steam')->cleanString((string)$row['description']);
|
||||
|
||||
/** Manipulate basic fields */
|
||||
$carbon->setTimezone(config('app.timezone'));
|
||||
|
||||
/** Create a basic journal. */
|
||||
$journal = TransactionJournal::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_type_id' => $type->id,
|
||||
'bill_id' => $billId,
|
||||
'transaction_currency_id' => $currency->id,
|
||||
'description' => '' === $description ? '(empty description)' : $description,
|
||||
'date' => $carbon->format('Y-m-d H:i:s'),
|
||||
'order' => 0,
|
||||
'tag_count' => 0,
|
||||
'completed' => 0,
|
||||
]
|
||||
);
|
||||
Log::debug(sprintf('Created new journal #%d: "%s"', $journal->id, $journal->description));
|
||||
|
||||
/** Create two transactions. */
|
||||
$this->transactionFactory->setJournal($journal);
|
||||
$this->transactionFactory->createPair($row, $currency, $foreignCurrency);
|
||||
|
||||
// verify that journal has two transactions. Otherwise, delete and cancel.
|
||||
$count = $journal->transactions()->count();
|
||||
if (2 !== $count) {
|
||||
// @codeCoverageIgnoreStart
|
||||
Log::error(sprintf('The journal unexpectedly has %d transaction(s). This is not OK. Cancel operation.', $count));
|
||||
try {
|
||||
$journal->delete();
|
||||
} catch (Exception $e) {
|
||||
Log::debug(sprintf('Dont care: %s.', $e->getMessage()));
|
||||
}
|
||||
|
||||
return null;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
$journal->completed = true;
|
||||
$journal->save();
|
||||
|
||||
/** Link all other data to the journal. */
|
||||
|
||||
/** Link budget */
|
||||
$this->storeBudget($journal, $row);
|
||||
|
||||
/** Link category */
|
||||
$this->storeCategory($journal, $row);
|
||||
|
||||
/** Set notes */
|
||||
$this->storeNote($journal, $row['notes']);
|
||||
|
||||
/** Set piggy bank */
|
||||
$this->storePiggyEvent($journal, $row);
|
||||
|
||||
/** Set tags */
|
||||
$this->storeTags($journal, $row['tags']);
|
||||
|
||||
/** set all meta fields */
|
||||
$this->storeMetaFields($journal, $row);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a separate function because "findCurrency" will default to EUR and that may not be what we want.
|
||||
*
|
||||
@@ -345,17 +345,38 @@ class TransactionJournalFactory
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->currencyRepository->findCurrency(
|
||||
$transaction['foreign_currency'], (int)$transaction['foreign_currency_id'], $transaction['foreign_currency_code']
|
||||
);
|
||||
return $this->currencyRepository->findCurrency((int)$transaction['foreign_currency_id'], $transaction['foreign_currency_code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NullArrayObject $row
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function hashArray(NullArrayObject $row): string
|
||||
{
|
||||
$row['import_hash_v2'] = null;
|
||||
$row['original_source'] = null;
|
||||
$json = json_encode($row);
|
||||
if (false === $json) {
|
||||
$json = json_encode((string)microtime());
|
||||
}
|
||||
$hash = hash('sha256', $json);
|
||||
Log::debug(sprintf('The hash is: %s', $hash));
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param TransactionJournal $journal
|
||||
* @param NullArrayObject $data
|
||||
*/
|
||||
private function storeBudget(TransactionJournal $journal, NullArrayObject $data): void
|
||||
{
|
||||
if (TransactionType::WITHDRAWAL !== $journal->transactionType->type) {
|
||||
return;
|
||||
}
|
||||
$budget = $this->budgetRepository->findBudget($data['budget'], $data['budget_id'], $data['budget_name']);
|
||||
if (null !== $budget) {
|
||||
Log::debug(sprintf('Link budget #%d to journal #%d', $budget->id, $journal->id));
|
||||
|
Reference in New Issue
Block a user