mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Fix meta services for recurrences.
This commit is contained in:
parent
1783f0beb1
commit
a0b46d9d8a
@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ use Log;
|
||||
|
||||
/**
|
||||
* Class BudgetDestroyService
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class BudgetDestroyService
|
||||
|
@ -30,6 +30,7 @@ use Log;
|
||||
|
||||
/**
|
||||
* Class CategoryDestroyService
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CategoryDestroyService
|
||||
|
@ -29,6 +29,7 @@ use Log;
|
||||
|
||||
/**
|
||||
* Class CurrencyDestroyService
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class CurrencyDestroyService
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionGroup;
|
||||
|
||||
/**
|
||||
* Class TransactionGroupDestroyService
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class TransactionGroupDestroyService
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ use Log;
|
||||
|
||||
/**
|
||||
* Trait BillServiceTrait
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
trait BillServiceTrait
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Services\Internal\Update;
|
||||
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class CurrencyUpdateService
|
||||
|
@ -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
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user