Fix meta services for recurrences.

This commit is contained in:
James Cole 2021-03-15 10:31:11 +01:00
parent 1783f0beb1
commit a0b46d9d8a
No known key found for this signature in database
GPG Key ID: B5669F9493CDE38D
14 changed files with 553 additions and 525 deletions

View File

@ -76,28 +76,6 @@ class AccountDestroyService
}
}
/**
* @param Account $account
*/
private function destroyJournals(Account $account): void
{
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
Log::debug('Now trigger account delete response #' . $account->id);
/** @var Transaction $transaction */
foreach ($account->transactions()->get() as $transaction) {
Log::debug('Now at transaction #' . $transaction->id);
/** @var TransactionJournal $journal */
$journal = $transaction->transactionJournal()->first();
if (null !== $journal) {
Log::debug('Call for deletion of journal #' . $journal->id);
$service->destroy($journal);
}
}
}
/**
* @param Account $account
*/
@ -135,6 +113,47 @@ class AccountDestroyService
}
}
/**
* @param Account $account
* @param Account $moveTo
*/
private function moveTransactions(Account $account, Account $moveTo): void
{
DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]);
}
/**
* @param Account $account
* @param Account $moveTo
*/
private function updateRecurrences(Account $account, Account $moveTo): void
{
DB::table('recurrences_transactions')->where('source_id', $account->id)->update(['source_id' => $moveTo->id]);
DB::table('recurrences_transactions')->where('destination_id', $account->id)->update(['destination_id' => $moveTo->id]);
}
/**
* @param Account $account
*/
private function destroyJournals(Account $account): void
{
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
Log::debug('Now trigger account delete response #' . $account->id);
/** @var Transaction $transaction */
foreach ($account->transactions()->get() as $transaction) {
Log::debug('Now at transaction #' . $transaction->id);
/** @var TransactionJournal $journal */
$journal = $transaction->transactionJournal()->first();
if (null !== $journal) {
Log::debug('Call for deletion of journal #' . $journal->id);
$service->destroy($journal);
}
}
}
/**
* @param Account $account
*/
@ -155,24 +174,4 @@ class AccountDestroyService
}
}
/**
* @param Account $account
* @param Account $moveTo
*/
private function moveTransactions(Account $account, Account $moveTo): void
{
DB::table('transactions')->where('account_id', $account->id)->update(['account_id' => $moveTo->id]);
}
/**
* @param Account $account
* @param Account $moveTo
*/
private function updateRecurrences(Account $account, Account $moveTo): void
{
DB::table('recurrences_transactions')->where('source_id', $account->id)->update(['source_id' => $moveTo->id]);
DB::table('recurrences_transactions')->where('destination_id', $account->id)->update(['destination_id' => $moveTo->id]);
}
}

View File

@ -30,6 +30,7 @@ use Log;
/**
* Class BudgetDestroyService
*
* @codeCoverageIgnore
*/
class BudgetDestroyService

View File

@ -30,6 +30,7 @@ use Log;
/**
* Class CategoryDestroyService
*
* @codeCoverageIgnore
*/
class CategoryDestroyService

View File

@ -29,6 +29,7 @@ use Log;
/**
* Class CurrencyDestroyService
*
* @codeCoverageIgnore
*/
class CurrencyDestroyService

View File

@ -34,6 +34,21 @@ use Log;
*/
class RecurrenceDestroyService
{
/**
* Delete recurrence by ID
*
* @param int $recurrenceId
*/
public function destroyById(int $recurrenceId): void
{
$recurrence = Recurrence::find($recurrenceId);
if (null === $recurrence) {
return;
}
$this->destroy($recurrence);
}
/**
* Delete recurrence.
*
@ -69,19 +84,4 @@ class RecurrenceDestroyService
}
}
/**
* Delete recurrence by ID
*
* @param int $recurrenceId
*/
public function destroyById(int $recurrenceId): void
{
$recurrence = Recurrence::find($recurrenceId);
if (null === $recurrence) {
return;
}
$this->destroy($recurrence);
}
}

View File

@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionGroup;
/**
* Class TransactionGroupDestroyService
*
* @codeCoverageIgnore
*/
class TransactionGroupDestroyService

View File

@ -71,6 +71,32 @@ trait AccountServiceTrait
return $iban;
}
/**
* Returns true if the data in the array is submitted but empty.
*
* @param array $data
*
* @return bool
*/
public function isEmptyOBData(array $data): bool
{
if (!array_key_exists('opening_balance', $data)
&& !array_key_exists('opening_balance_date', $data)
) {
// not set, so false.
return false;
}
// if isset, but is empty:
if (
(array_key_exists('opening_balance', $data) && '' === $data['opening_balance'])
|| (array_key_exists('opening_balance_date', $data) && '' === $data['opening_balance_date'])
) {
return true;
}
return false;
}
/**
* Update meta data for account. Depends on type which fields are valid.
*
@ -173,29 +199,122 @@ trait AccountServiceTrait
}
/**
* Returns true if the data in the array is submitted but empty.
* Delete TransactionGroup with opening balance in it.
*
* @param array $data
*
* @return bool
* @param Account $account
*/
public function isEmptyOBData(array $data): bool
protected function deleteOBGroup(Account $account): void
{
if (!array_key_exists('opening_balance', $data)
&& !array_key_exists('opening_balance_date', $data)
) {
// not set, so false.
return false;
Log::debug(sprintf('deleteOB() for account #%d', $account->id));
$openingBalanceGroup = $this->getOBGroup($account);
// opening balance data? update it!
if (null !== $openingBalanceGroup) {
Log::debug('Opening balance journal found, delete journal.');
/** @var TransactionGroupDestroyService $service */
$service = app(TransactionGroupDestroyService::class);
$service->destroy($openingBalanceGroup);
}
// if isset, but is empty:
if (
(array_key_exists('opening_balance', $data) && '' === $data['opening_balance'])
|| (array_key_exists('opening_balance_date', $data) && '' === $data['opening_balance_date'])
) {
return true;
}
/**
* Returns the opening balance group, or NULL if it does not exist.
*
* @param Account $account
*
* @return TransactionGroup|null
*/
protected function getOBGroup(Account $account): ?TransactionGroup
{
return $this->accountRepository->getOpeningBalanceGroup($account);
}
/**
* @param int $currencyId
* @param string $currencyCode
*
* @return TransactionCurrency
*/
protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency
{
// find currency, or use default currency instead.
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
/** @var TransactionCurrency $currency */
$currency = $factory->find($currencyId, $currencyCode);
if (null === $currency) {
// use default currency:
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
$currency->enabled = true;
$currency->save();
return $currency;
}
/**
* Update or create the opening balance group. Assumes valid data in $data.
*
* Returns null if this fails.
*
* @param Account $account
* @param array $data
*
* @return TransactionGroup|null
*/
protected function updateOBGroup(Account $account, array $data): ?TransactionGroup
{
$obGroup = $this->getOBGroup($account);
if (null === $obGroup) {
return $this->createOBGroup($account, $data);
}
return false;
// $data['currency_id'] is empty so creating a new journal may break.
if (!array_key_exists('currency_id', $data)) {
$currency = $this->accountRepository->getAccountCurrency($account);
if (null === $currency) {
$currency = app('default')->getDefaultCurrencyByUser($account->user);
}
$data['currency_id'] = $currency->id;
}
/** @var TransactionJournal $journal */
$journal = $obGroup->transactionJournals()->first();
$journal->date = $data['opening_balance_date'] ?? $journal->date;
$journal->transaction_currency_id = $data['currency_id'];
/** @var Transaction $obTransaction */
$obTransaction = $journal->transactions()->where('account_id', '!=', $account->id)->first();
/** @var Transaction $accountTransaction */
$accountTransaction = $journal->transactions()->where('account_id', $account->id)->first();
// if amount is negative:
if (1 === bccomp('0', $data['opening_balance'])) {
// account transaction loses money:
$accountTransaction->amount = app('steam')->negative($data['opening_balance']);
$accountTransaction->transaction_currency_id = $data['currency_id'];
// OB account transaction gains money
$obTransaction->amount = app('steam')->positive($data['opening_balance']);
$obTransaction->transaction_currency_id = $data['currency_id'];
}
if (-1 === bccomp('0', $data['opening_balance'])) {
// account gains money:
$accountTransaction->amount = app('steam')->positive($data['opening_balance']);
$accountTransaction->transaction_currency_id = $data['currency_id'];
// OB account loses money:
$obTransaction->amount = app('steam')->negative($data['opening_balance']);
$obTransaction->transaction_currency_id = $data['currency_id'];
}
// save both
$accountTransaction->save();
$obTransaction->save();
$journal->save();
$obGroup->refresh();
return $obGroup;
}
/**
@ -288,123 +407,4 @@ trait AccountServiceTrait
return $group;
}
/**
* Delete TransactionGroup with opening balance in it.
*
* @param Account $account
*/
protected function deleteOBGroup(Account $account): void
{
Log::debug(sprintf('deleteOB() for account #%d', $account->id));
$openingBalanceGroup = $this->getOBGroup($account);
// opening balance data? update it!
if (null !== $openingBalanceGroup) {
Log::debug('Opening balance journal found, delete journal.');
/** @var TransactionGroupDestroyService $service */
$service = app(TransactionGroupDestroyService::class);
$service->destroy($openingBalanceGroup);
}
}
/**
* @param int $currencyId
* @param string $currencyCode
*
* @return TransactionCurrency
*/
protected function getCurrency(int $currencyId, string $currencyCode): TransactionCurrency
{
// find currency, or use default currency instead.
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
/** @var TransactionCurrency $currency */
$currency = $factory->find($currencyId, $currencyCode);
if (null === $currency) {
// use default currency:
$currency = app('amount')->getDefaultCurrencyByUser($this->user);
}
$currency->enabled = true;
$currency->save();
return $currency;
}
/**
* Returns the opening balance group, or NULL if it does not exist.
*
* @param Account $account
*
* @return TransactionGroup|null
*/
protected function getOBGroup(Account $account): ?TransactionGroup
{
return $this->accountRepository->getOpeningBalanceGroup($account);
}
/**
* Update or create the opening balance group. Assumes valid data in $data.
*
* Returns null if this fails.
*
* @param Account $account
* @param array $data
*
* @return TransactionGroup|null
*/
protected function updateOBGroup(Account $account, array $data): ?TransactionGroup
{
$obGroup = $this->getOBGroup($account);
if (null === $obGroup) {
return $this->createOBGroup($account, $data);
}
// $data['currency_id'] is empty so creating a new journal may break.
if (!array_key_exists('currency_id', $data)) {
$currency = $this->accountRepository->getAccountCurrency($account);
if (null === $currency) {
$currency = app('default')->getDefaultCurrencyByUser($account->user);
}
$data['currency_id'] = $currency->id;
}
/** @var TransactionJournal $journal */
$journal = $obGroup->transactionJournals()->first();
$journal->date = $data['opening_balance_date'] ?? $journal->date;
$journal->transaction_currency_id = $data['currency_id'];
/** @var Transaction $obTransaction */
$obTransaction = $journal->transactions()->where('account_id', '!=', $account->id)->first();
/** @var Transaction $accountTransaction */
$accountTransaction = $journal->transactions()->where('account_id', $account->id)->first();
// if amount is negative:
if (1 === bccomp('0', $data['opening_balance'])) {
// account transaction loses money:
$accountTransaction->amount = app('steam')->negative($data['opening_balance']);
$accountTransaction->transaction_currency_id = $data['currency_id'];
// OB account transaction gains money
$obTransaction->amount = app('steam')->positive($data['opening_balance']);
$obTransaction->transaction_currency_id = $data['currency_id'];
}
if (-1 === bccomp('0', $data['opening_balance'])) {
// account gains money:
$accountTransaction->amount = app('steam')->positive($data['opening_balance']);
$accountTransaction->transaction_currency_id = $data['currency_id'];
// OB account loses money:
$obTransaction->amount = app('steam')->negative($data['opening_balance']);
$obTransaction->transaction_currency_id = $data['currency_id'];
}
// save both
$accountTransaction->save();
$obTransaction->save();
$journal->save();
$obGroup->refresh();
return $obGroup;
}
}

View File

@ -32,6 +32,7 @@ use Log;
/**
* Trait BillServiceTrait
*
* @codeCoverageIgnore
*/
trait BillServiceTrait

View File

@ -49,35 +49,6 @@ trait JournalServiceTrait
private CategoryRepositoryInterface $categoryRepository;
private TagFactory $tagFactory;
/**
* @param string|null $amount
*
* @return string
* @codeCoverageIgnore
*/
protected function getForeignAmount(?string $amount): ?string
{
if (null === $amount) {
Log::debug('No foreign amount info in array. Return NULL');
return null;
}
if ('' === $amount) {
Log::debug('Foreign amount is empty string, return NULL.');
return null;
}
if (0 === bccomp('0', $amount)) {
Log::debug('Foreign amount is 0.0, return NULL.');
return null;
}
Log::debug(sprintf('Foreign amount is %s', $amount));
return $amount;
}
/**
* @param string $transactionType
* @param string $direction
@ -113,6 +84,165 @@ trait JournalServiceTrait
return $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
}
/**
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountById(array $data, array $types): ?Account
{
$search = null;
// first attempt, find by ID.
if (null !== $data['id']) {
$search = $this->accountRepository->findNull($data['id']);
if (null !== $search && in_array($search->accountType->type, $types, true)) {
Log::debug(
sprintf('Found "account_id" object: #%d, "%s" of type %s', $search->id, $search->name, $search->accountType->type)
);
}
}
return $search;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByName(?Account $account, array $data, array $types): ?Account
{
// second attempt, find by name.
if (null === $account && null !== $data['name']) {
Log::debug('Found nothing by account ID.');
// find by preferred type.
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByName($data['name'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByIban(?Account $account, array $data, array $types): ?Account
{
// third attempt, find by IBAN
if (null === $account && null !== $data['iban']) {
Log::debug(sprintf('Found nothing by account iban "%s".', $data['iban']));
// find by preferred type.
$source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param string $preferredType
*
* @return Account
* @throws FireflyException
*/
private function createAccount(?Account $account, array $data, string $preferredType): Account
{
Log::debug('Now in createAccount()', $data);
// return new account.
if (null !== $account) {
Log::debug(
sprintf(
'Was also given %s account #%d ("%s") so will simply return that.',
$account->accountType->type, $account->id, $account->name
)
);
}
if (null === $account) {
// final attempt, create it.
if (AccountType::ASSET === $preferredType) {
throw new FireflyException('TransactionFactory: Cannot create asset account with these values', $data);
}
// fix name of account if only IBAN is given:
if ('' === (string)$data['name'] && '' !== (string)$data['iban']) {
Log::debug(sprintf('Account name is now IBAN ("%s")', $data['iban']));
$data['name'] = $data['iban'];
}
$data['name'] = $data['name'] ?? '(no name)';
$account = $this->accountRepository->store(
[
'account_type_id' => null,
'account_type' => $preferredType,
'name' => $data['name'],
'virtual_balance' => null,
'active' => true,
'iban' => $data['iban'],
'currency_id' => $data['currency_id'] ?? null,
'order' => $this->accountRepository->maxOrder($preferredType),
]
);
// store BIC
if (null !== $data['bic']) {
/** @var AccountMetaFactory $metaFactory */
$metaFactory = app(AccountMetaFactory::class);
$metaFactory->create(['account_id' => $account->id, 'name' => 'BIC', 'data' => $data['bic']]);
}
// store account number
if (null !== $data['number']) {
/** @var AccountMetaFactory $metaFactory */
$metaFactory = app(AccountMetaFactory::class);
$metaFactory->create(['account_id' => $account->id, 'name' => 'account_number', 'data' => $data['bic']]);
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function getCashAccount(?Account $account, array $data, array $types): ?Account
{
// return cash account.
if (null === $account && null === $data['name']
&& in_array(AccountType::CASH, $types, true)) {
$account = $this->accountRepository->getCashAccount();
}
return $account;
}
/**
* @param string $amount
*
@ -132,6 +262,34 @@ trait JournalServiceTrait
return $amount;
}
/**
* @param string|null $amount
*
* @return string
* @codeCoverageIgnore
*/
protected function getForeignAmount(?string $amount): ?string
{
if (null === $amount) {
Log::debug('No foreign amount info in array. Return NULL');
return null;
}
if ('' === $amount) {
Log::debug('Foreign amount is empty string, return NULL.');
return null;
}
if (0 === bccomp('0', $amount)) {
Log::debug('Foreign amount is 0.0, return NULL.');
return null;
}
Log::debug(sprintf('Foreign amount is %s', $amount));
return $amount;
}
/**
* @param TransactionJournal $journal
* @param NullArrayObject $data
@ -242,163 +400,4 @@ trait JournalServiceTrait
$journal->tags()->sync($set);
Log::debug('Done!');
}
/**
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountById(array $data, array $types): ?Account
{
$search = null;
// first attempt, find by ID.
if (null !== $data['id']) {
$search = $this->accountRepository->findNull($data['id']);
if (null !== $search && in_array($search->accountType->type, $types, true)) {
Log::debug(
sprintf('Found "account_id" object: #%d, "%s" of type %s', $search->id, $search->name, $search->accountType->type)
);
}
}
return $search;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByName(?Account $account, array $data, array $types): ?Account
{
// second attempt, find by name.
if (null === $account && null !== $data['name']) {
Log::debug('Found nothing by account ID.');
// find by preferred type.
$source = $this->accountRepository->findByName($data['name'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByName($data['name'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_name" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function findAccountByIban(?Account $account, array $data, array $types): ?Account
{
// third attempt, find by IBAN
if (null === $account && null !== $data['iban']) {
Log::debug(sprintf('Found nothing by account iban "%s".', $data['iban']));
// find by preferred type.
$source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]);
// or any expected type.
$source = $source ?? $this->accountRepository->findByIbanNull($data['iban'], $types);
if (null !== $source) {
Log::debug(sprintf('Found "account_iban" object: #%d, %s', $source->id, $source->name));
$account = $source;
}
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param array $types
*
* @return Account|null
*/
private function getCashAccount(?Account $account, array $data, array $types): ?Account
{
// return cash account.
if (null === $account && null === $data['name']
&& in_array(AccountType::CASH, $types, true)) {
$account = $this->accountRepository->getCashAccount();
}
return $account;
}
/**
* @param Account|null $account
* @param array $data
* @param string $preferredType
*
* @return Account
* @throws FireflyException
*/
private function createAccount(?Account $account, array $data, string $preferredType): Account
{
Log::debug('Now in createAccount()', $data);
// return new account.
if (null !== $account) {
Log::debug(
sprintf(
'Was also given %s account #%d ("%s") so will simply return that.',
$account->accountType->type, $account->id, $account->name
)
);
}
if (null === $account) {
// final attempt, create it.
if (AccountType::ASSET === $preferredType) {
throw new FireflyException('TransactionFactory: Cannot create asset account with these values', $data);
}
// fix name of account if only IBAN is given:
if ('' === (string)$data['name'] && '' !== (string)$data['iban']) {
Log::debug(sprintf('Account name is now IBAN ("%s")', $data['iban']));
$data['name'] = $data['iban'];
}
$data['name'] = $data['name'] ?? '(no name)';
$account = $this->accountRepository->store(
[
'account_type_id' => null,
'account_type' => $preferredType,
'name' => $data['name'],
'virtual_balance' => null,
'active' => true,
'iban' => $data['iban'],
'currency_id' => $data['currency_id'] ?? null,
'order' => $this->accountRepository->maxOrder($preferredType),
]
);
// store BIC
if (null !== $data['bic']) {
/** @var AccountMetaFactory $metaFactory */
$metaFactory = app(AccountMetaFactory::class);
$metaFactory->create(['account_id' => $account->id, 'name' => 'BIC', 'data' => $data['bic']]);
}
// store account number
if (null !== $data['number']) {
/** @var AccountMetaFactory $metaFactory */
$metaFactory = app(AccountMetaFactory::class);
$metaFactory->create(['account_id' => $account->id, 'name' => 'account_number', 'data' => $data['bic']]);
}
}
return $account;
}
}

View File

@ -51,29 +51,7 @@ trait RecurringTransactionTrait
{
/**
* @param Recurrence $recurrence
* @param array $repetitions
*/
protected function createRepetitions(Recurrence $recurrence, array $repetitions): void
{
/** @var array $array */
foreach ($repetitions as $array) {
RecurrenceRepetition::create(
[
'recurrence_id' => $recurrence->id,
'repetition_type' => $array['type'],
'repetition_moment' => $array['moment'] ?? '',
'repetition_skip' => $array['skip'] ?? 0,
'weekend' => $array['weekend'] ?? 1,
]
);
}
}
/**
* @param Recurrence $recurrence
* @param string $note
* @param string $note
*
* @return bool
*/
@ -102,6 +80,27 @@ trait RecurringTransactionTrait
return true;
}
/**
* @param Recurrence $recurrence
* @param array $repetitions
*/
protected function createRepetitions(Recurrence $recurrence, array $repetitions): void
{
/** @var array $array */
foreach ($repetitions as $array) {
RecurrenceRepetition::create(
[
'recurrence_id' => $recurrence->id,
'repetition_type' => $array['type'],
'repetition_moment' => $array['moment'] ?? '',
'repetition_skip' => $array['skip'] ?? 0,
'weekend' => $array['weekend'] ?? 1,
]
);
}
}
/**
* Store transactions of a recurring transactions. It's complex but readable.
*
@ -138,7 +137,7 @@ trait RecurringTransactionTrait
if (!$validator->validateDestination($destination->id, null, null)) {
throw new FireflyException(sprintf('Destination invalid: %s', $validator->destError)); // @codeCoverageIgnore
}
if(array_key_exists('foreign_amount', $array) && '' === (string)$array['foreign_amount']) {
if (array_key_exists('foreign_amount', $array) && '' === (string)$array['foreign_amount']) {
unset($array['foreign_amount']);
}
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
@ -156,20 +155,11 @@ trait RecurringTransactionTrait
);
$transaction->save();
$budget = null;
if (array_key_exists('budget_id', $array)) {
/** @var BudgetFactory $budgetFactory */
$budgetFactory = app(BudgetFactory::class);
$budgetFactory->setUser($recurrence->user);
$budget = $budgetFactory->find($array['budget_id'], null);
$this->setBudget($transaction, (int)$array['budget_id']);
}
$category = null;
if (array_key_exists('category_id', $array)) {
/** @var CategoryFactory $categoryFactory */
$categoryFactory = app(CategoryFactory::class);
$categoryFactory->setUser($recurrence->user);
$category = $categoryFactory->findOrCreate($array['category_id'], null);
$this->setCategory($transaction, (int)$array['category_id']);
}
// same for piggy bank
@ -177,57 +167,10 @@ trait RecurringTransactionTrait
$this->updatePiggyBank($transaction, (int)$array['piggy_bank_id']);
}
if(array_key_exists('tags', $array)) {
if (array_key_exists('tags', $array)) {
$this->updateTags($transaction, $array['tags']);
}
// create recurrence transaction meta:
if (null !== $budget) {
RecurrenceTransactionMeta::create(
[
'rt_id' => $transaction->id,
'name' => 'budget_id',
'value' => $budget->id,
]
);
}
if (null !== $category) {
RecurrenceTransactionMeta::create(
[
'rt_id' => $transaction->id,
'name' => 'category_name',
'value' => $category->name,
]
);
}
}
}
/**
* @param Recurrence $recurrence
*
* @codeCoverageIgnore
*/
protected function deleteRepetitions(Recurrence $recurrence): void
{
$recurrence->recurrenceRepetitions()->delete();
}
/**
* @param Recurrence $recurrence
*
* @codeCoverageIgnore
*/
protected function deleteTransactions(Recurrence $recurrence): void
{
/** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) {
$transaction->recurrenceTransactionMeta()->delete();
try {
$transaction->delete();
} catch (Exception $e) {
Log::debug($e->getMessage());
}
}
}
@ -283,6 +226,52 @@ trait RecurringTransactionTrait
return $result ?? $repository->getCashAccount();
}
/**
* @param RecurrenceTransaction $transaction
* @param int $budgetId
*/
private function setBudget(RecurrenceTransaction $transaction, int $budgetId): void
{
$budgetFactory = app(BudgetFactory::class);
$budgetFactory->setUser($transaction->recurrence->user);
$budget = $budgetFactory->find($budgetId, null);
if (null === $budget) {
return;
}
$meta = $transaction->recurrenceTransactionMeta()->where('name', 'budget_id')->first();
if (null === $meta) {
$meta = new RecurrenceTransactionMeta;
$meta->rt_id = $transaction->id;
$meta->name = 'budget_id';
}
$meta->value = $budget->id;
$meta->save();
}
/**
* @param RecurrenceTransaction $transaction
* @param int $categoryId
*/
private function setCategory(RecurrenceTransaction $transaction, int $categoryId): void
{
$categoryFactory = app(CategoryFactory::class);
$categoryFactory->setUser($transaction->recurrence->user);
$category = $categoryFactory->findOrCreate($categoryId, null);
if (null === $category) {
return;
}
$meta = $transaction->recurrenceTransactionMeta()->where('name', 'category_id')->first();
if (null === $meta) {
$meta = new RecurrenceTransactionMeta;
$meta->rt_id = $transaction->id;
$meta->name = 'category_id';
}
$meta->value = $category->id;
$meta->save();
}
/**
* @param RecurrenceTransaction $transaction
* @param int $piggyId
@ -328,4 +317,32 @@ trait RecurringTransactionTrait
$transaction->recurrenceTransactionMeta()->where('name', 'tags')->delete();
}
}
/**
* @param Recurrence $recurrence
*
* @codeCoverageIgnore
*/
protected function deleteRepetitions(Recurrence $recurrence): void
{
$recurrence->recurrenceRepetitions()->delete();
}
/**
* @param Recurrence $recurrence
*
* @codeCoverageIgnore
*/
protected function deleteTransactions(Recurrence $recurrence): void
{
/** @var RecurrenceTransaction $transaction */
foreach ($recurrence->recurrenceTransactions as $transaction) {
$transaction->recurrenceTransactionMeta()->delete();
try {
$transaction->delete();
} catch (Exception $e) {
Log::debug($e->getMessage());
}
}
}
}

View File

@ -56,7 +56,8 @@ class BillUpdateService
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
$factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find((int) ($data['currency_id'] ?? null), $data['currency_code'] ?? null) ?? app('amount')->getDefaultCurrencyByUser($bill->user);
$currency = $factory->find((int)($data['currency_id'] ?? null), $data['currency_code'] ?? null) ??
app('amount')->getDefaultCurrencyByUser($bill->user);
// enable the currency if it isn't.
$currency->enabled = true;

View File

@ -67,8 +67,8 @@ class CategoryUpdateService
*/
public function update(Category $category, array $data): Category
{
$oldName = $category->name;
if(array_key_exists('name', $data)) {
$oldName = $category->name;
if (array_key_exists('name', $data)) {
$category->name = $data['name'];
$category->save();
// update triggers and actions

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Update;
use FireflyIII\Models\TransactionCurrency;
use Log;
/**
* Class CurrencyUpdateService

View File

@ -114,63 +114,6 @@ class RecurrenceUpdateService
return $recurrence;
}
/**
* TODO this method is way too complex.
*
* @param Recurrence $recurrence
* @param array $transactions
*
* @throws FireflyException
*/
private function updateTransactions(Recurrence $recurrence, array $transactions): void
{
$originalCount = $recurrence->recurrenceTransactions()->count();
if (0 === count($transactions)) {
// wont drop transactions, rather avoid.
return;
}
// user added or removed repetitions, delete all and recreate:
if ($originalCount !== count($transactions)) {
Log::debug('Del + recreate');
$this->deleteTransactions($recurrence);
$this->createTransactions($recurrence, $transactions);
return;
}
// loop all and try to match them:
if ($originalCount === count($transactions)) {
Log::debug('Loop and find');
foreach ($transactions as $current) {
$match = $this->matchTransaction($recurrence, $current);
if (null === $match) {
throw new FireflyException('Cannot match recurring transaction to existing transaction. Not sure what to do. Break.');
}
// TODO find currency
// TODO find foreign currency
// update fields
$fields = [
'source_id' => 'source_id',
'destination_id' => 'destination_id',
'amount' => 'amount',
'foreign_amount' => 'foreign_amount',
'description' => 'description',
];
foreach ($fields as $field => $column) {
if (array_key_exists($field, $current)) {
$match->$column = $current[$field];
$match->save();
}
}
// update meta data
// budget_id
// category_id
// tags
// piggy_bank_id
}
}
}
/**
* @param Recurrence $recurrence
* @param string $text
@ -272,6 +215,71 @@ class RecurrenceUpdateService
return $query->first();
}
/**
* TODO this method is way too complex.
*
* @param Recurrence $recurrence
* @param array $transactions
*
* @throws FireflyException
*/
private function updateTransactions(Recurrence $recurrence, array $transactions): void
{
$originalCount = $recurrence->recurrenceTransactions()->count();
if (0 === count($transactions)) {
// wont drop transactions, rather avoid.
return;
}
// user added or removed repetitions, delete all and recreate:
if ($originalCount !== count($transactions)) {
Log::debug('Del + recreate');
$this->deleteTransactions($recurrence);
$this->createTransactions($recurrence, $transactions);
return;
}
// loop all and try to match them:
if ($originalCount === count($transactions)) {
Log::debug('Loop and find');
foreach ($transactions as $current) {
$match = $this->matchTransaction($recurrence, $current);
if (null === $match) {
throw new FireflyException('Cannot match recurring transaction to existing transaction. Not sure what to do. Break.');
}
// TODO find currency
// TODO find foreign currency
// update fields
$fields = [
'source_id' => 'source_id',
'destination_id' => 'destination_id',
'amount' => 'amount',
'foreign_amount' => 'foreign_amount',
'description' => 'description',
];
foreach ($fields as $field => $column) {
if (array_key_exists($field, $current)) {
$match->$column = $current[$field];
$match->save();
}
}
// update meta data
if (array_key_exists('budget_id', $current)) {
$this->setBudget($match, (int)$current['budget_id']);
}
if (array_key_exists('category_id', $current)) {
$this->setCategory($match, (int)$current['category_id']);
}
if (array_key_exists('tags', $current)) {
$this->updateTags($match, $current['tags']);
}
if (array_key_exists('piggy_bank_id', $current)) {
$this->updatePiggyBank($match, (int)$current['piggy_bank_id']);
}
}
}
}
/**
* @param array $data
*