mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-25 14:58:40 +00:00
Clean up liability overview.
This commit is contained in:
@@ -206,7 +206,7 @@ class AccountFactory
|
||||
if ('' === (string) $databaseData['virtual_balance']) {
|
||||
$databaseData['virtual_balance'] = null;
|
||||
}
|
||||
// remove virtual balance when not an asset account or a liability
|
||||
// remove virtual balance when not an asset account
|
||||
if (!in_array($type->type, $this->canHaveVirtual, true)) {
|
||||
$databaseData['virtual_balance'] = null;
|
||||
}
|
||||
@@ -217,14 +217,14 @@ class AccountFactory
|
||||
$data = $this->cleanMetaDataArray($account, $data);
|
||||
$this->storeMetaData($account, $data);
|
||||
|
||||
// create opening balance
|
||||
// create opening balance (only asset accounts)
|
||||
try {
|
||||
$this->storeOpeningBalance($account, $data);
|
||||
} catch (FireflyException $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
|
||||
// create credit liability data (if relevant)
|
||||
// create credit liability data (only liabilities)
|
||||
try {
|
||||
$this->storeCreditLiability($account, $data);
|
||||
} catch (FireflyException $e) {
|
||||
@@ -352,16 +352,17 @@ class AccountFactory
|
||||
$accountType = $account->accountType->type;
|
||||
$direction = $this->accountRepository->getMetaValue($account, 'liability_direction');
|
||||
$valid = config('firefly.valid_liabilities');
|
||||
if (in_array($accountType, $valid, true) && 'credit' === $direction) {
|
||||
Log::debug('Is a liability with credit direction.');
|
||||
if (in_array($accountType, $valid, true)) {
|
||||
Log::debug('Is a liability with credit ("i am owed") direction.');
|
||||
if ($this->validOBData($data)) {
|
||||
Log::debug('Has valid CL data.');
|
||||
$openingBalance = $data['opening_balance'];
|
||||
$openingBalanceDate = $data['opening_balance_date'];
|
||||
$this->updateCreditTransaction($account, $openingBalance, $openingBalanceDate);
|
||||
// store credit transaction.
|
||||
$this->updateCreditTransaction($account, $direction, $openingBalance, $openingBalanceDate);
|
||||
}
|
||||
if (!$this->validOBData($data)) {
|
||||
Log::debug('Has NOT valid CL data.');
|
||||
Log::debug('Does NOT have valid CL data, deletr any CL transaction.');
|
||||
$this->deleteCreditTransaction($account);
|
||||
}
|
||||
}
|
||||
|
@@ -231,7 +231,7 @@ class BoxController extends Controller
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
$allAccounts = $accountRepository->getActiveAccountsByType(
|
||||
[AccountType::DEFAULT, AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD]
|
||||
[AccountType::DEFAULT, AccountType::ASSET]
|
||||
);
|
||||
Log::debug(sprintf('Found %d accounts.', $allAccounts->count()));
|
||||
|
||||
|
@@ -364,7 +364,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
$journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT])
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $journal) {
|
||||
return null;
|
||||
@@ -388,7 +388,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
{
|
||||
$journal = TransactionJournal::leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('transactions.account_id', $account->id)
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE])
|
||||
->transactionTypes([TransactionType::OPENING_BALANCE, TransactionType::LIABILITY_CREDIT])
|
||||
->first(['transaction_journals.*']);
|
||||
if (null === $journal) {
|
||||
return null;
|
||||
|
@@ -407,13 +407,22 @@ trait AccountServiceTrait
|
||||
* @return TransactionGroup
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function updateCreditTransaction(Account $account, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup
|
||||
protected function updateCreditTransaction(Account $account, string $direction, string $openingBalance, Carbon $openingBalanceDate): TransactionGroup
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
if (0 === bccomp($openingBalance, '0')) {
|
||||
Log::debug('Amount is zero, so will not update liability credit group.');
|
||||
throw new FireflyException('Amount for update liability credit was unexpectedly 0.');
|
||||
Log::debug('Amount is zero, so will not update liability credit/debit group.');
|
||||
throw new FireflyException('Amount for update liability credit/debit was unexpectedly 0.');
|
||||
}
|
||||
// if direction is "debit" (i owe this debt), amount is negative.
|
||||
// which means the liability will have a negative balance which the user must fill.
|
||||
$openingBalance = app('steam')->negative($openingBalance);
|
||||
|
||||
// if direction is "credit" (I am owed this debt), amount is positive.
|
||||
// which means the liability will have a positive balance which is drained when its paid back into any asset.
|
||||
if ('credit' === $direction) {
|
||||
$openingBalance = app('steam')->positive($openingBalance);
|
||||
}
|
||||
|
||||
// create if not exists:
|
||||
@@ -468,6 +477,23 @@ trait AccountServiceTrait
|
||||
}
|
||||
|
||||
$language = app('preferences')->getForUser($account->user, 'language', 'en_US')->data;
|
||||
|
||||
// set source and/or destination based on whether the amount is positive or negative.
|
||||
// first, assume the amount is positive and go from there:
|
||||
// if amount is positive ("I am owed this debt"), source is special account, destination is the liability.
|
||||
$sourceId = null;
|
||||
$sourceName = trans('firefly.liability_credit_description', ['account' => $account->name], $language);
|
||||
$destId = $account->id;
|
||||
$destName = null;
|
||||
if(-1 === bccomp($openingBalance, '0')) {
|
||||
// amount is negative, reverse it
|
||||
$sourceId = $account->id;
|
||||
$sourceName = null;
|
||||
$destId = null;
|
||||
$destName = trans('firefly.liability_credit_description', ['account' => $account->name], $language);
|
||||
}
|
||||
|
||||
// amount must be positive for the transaction to work.
|
||||
$amount = app('steam')->positive($openingBalance);
|
||||
|
||||
// get or grab currency:
|
||||
@@ -484,10 +510,10 @@ trait AccountServiceTrait
|
||||
[
|
||||
'type' => 'Liability credit',
|
||||
'date' => $openingBalanceDate,
|
||||
'source_id' => null,
|
||||
'source_name' => trans('firefly.liability_credit_description', ['account' => $account->name], $language),
|
||||
'destination_id' => $account->id,
|
||||
'destination_name' => null,
|
||||
'source_id' => $sourceId,
|
||||
'source_name' => $sourceName,
|
||||
'destination_id' => $destId,
|
||||
'destination_name' => $destName,
|
||||
'user' => $account->user_id,
|
||||
'currency_id' => $currency->id,
|
||||
'order' => 0,
|
||||
|
@@ -62,9 +62,11 @@ class CreditRecalculateService
|
||||
return;
|
||||
}
|
||||
if (null !== $this->group && null === $this->account) {
|
||||
Log::debug('Have to handle a group.');
|
||||
$this->processGroup();
|
||||
}
|
||||
if (null !== $this->account && null === $this->group) {
|
||||
Log::debug('Have to handle an account.');
|
||||
// work based on account.
|
||||
$this->processAccount();
|
||||
}
|
||||
@@ -213,7 +215,6 @@ class CreditRecalculateService
|
||||
}
|
||||
$factory->crud($account, 'current_debt', $leftOfDebt);
|
||||
|
||||
|
||||
Log::debug(sprintf('Done with %s(#%d)', __METHOD__, $account->id));
|
||||
}
|
||||
|
||||
@@ -252,16 +253,16 @@ class CreditRecalculateService
|
||||
Log::debug(sprintf('Processing group #%d, journal #%d of type "%s"', $journal->id, $groupId, $type));
|
||||
|
||||
// it's a withdrawal into this liability (from asset).
|
||||
// if it's a credit, we don't care, because sending more money
|
||||
// to a credit-liability doesn't increase the amount (yet)
|
||||
// if it's a credit ("I am owed"), this increases the amount due,
|
||||
// because we're lending person X more money
|
||||
if (
|
||||
$type === TransactionType::WITHDRAWAL
|
||||
&& (int)$account->id === (int)$transaction->account_id
|
||||
&& 1 === bccomp($usedAmount, '0')
|
||||
&& 'credit' === $direction
|
||||
) {
|
||||
Log::debug(sprintf('Is withdrawal into credit liability #%d, does not influence the amount due.', $transaction->account_id));
|
||||
|
||||
$amount = bcadd($amount, app('steam')->positive($usedAmount));
|
||||
Log::debug(sprintf('Is withdrawal (%s) into credit liability #%d, will increase amount due to %s.', $transaction->account_id, $usedAmount, $amount));
|
||||
return $amount;
|
||||
}
|
||||
|
||||
|
@@ -324,7 +324,7 @@ class AccountUpdateService
|
||||
$openingBalance = $data['opening_balance'];
|
||||
$openingBalanceDate = $data['opening_balance_date'];
|
||||
if ('credit' === $direction) {
|
||||
$this->updateCreditTransaction($account, $openingBalance, $openingBalanceDate);
|
||||
$this->updateCreditTransaction($account, $direction, $openingBalance, $openingBalanceDate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -43,35 +43,34 @@ trait LiabilityValidation
|
||||
Log::debug('Now in validateLCDestination', $array);
|
||||
$result = null;
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
$validTypes = config('firefly.valid_liabilities');
|
||||
|
||||
if (null === $accountId) {
|
||||
$this->sourceError = (string) trans('validation.lc_destination_need_data');
|
||||
$result = false;
|
||||
// if the ID is not null the source account should be a dummy account of the type liability credit.
|
||||
// the ID of the destination must belong to a liability.
|
||||
if (null !== $accountId) {
|
||||
if (AccountType::LIABILITY_CREDIT !== $this?->source?->accountType?->type) {
|
||||
Log::error('Source account is not a liability.');
|
||||
return false;
|
||||
}
|
||||
$result = $this->findExistingAccount($validTypes, $array);
|
||||
if (null === $result) {
|
||||
Log::error('Destination account is not a liability.');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Log::debug('Destination ID is not null.');
|
||||
$search = $this->accountRepository->find($accountId);
|
||||
|
||||
// the source resulted in an account, but it's not of a valid type.
|
||||
if (null !== $search && !in_array($search->accountType->type, $validTypes, true)) {
|
||||
$message = sprintf('User submitted only an ID (#%d), which is a "%s", so this is not a valid destination.', $accountId, $search->accountType->type);
|
||||
Log::debug($message);
|
||||
$this->sourceError = $message;
|
||||
$result = false;
|
||||
if (null !== $accountName && '' !== $accountName) {
|
||||
Log::debug('Destination ID is null, now we can assume the destination is a (new) liability credit account.');
|
||||
return true;
|
||||
}
|
||||
// the source resulted in an account, AND it's of a valid type.
|
||||
if (null !== $search && in_array($search->accountType->type, $validTypes, true)) {
|
||||
Log::debug(sprintf('Found account of correct type: #%d, "%s"', $search->id, $search->name));
|
||||
$this->source = $search;
|
||||
$result = true;
|
||||
}
|
||||
|
||||
return $result ?? false;
|
||||
Log::error('Destination ID is null, but destination name is also NULL.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source of an liability credit must be a liability.
|
||||
* Source of a liability credit must be a liability or liability credit account.
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
@@ -79,13 +78,33 @@ trait LiabilityValidation
|
||||
*/
|
||||
protected function validateLCSource(array $array): bool
|
||||
{
|
||||
Log::debug('Now in validateLCSource', $array);
|
||||
// if the array has an ID and ID is not null, try to find it and check type.
|
||||
// this account must be a liability
|
||||
$accountId = array_key_exists('id', $array) ? $array['id'] : null;
|
||||
if (null !== $accountId) {
|
||||
Log::debug('Source ID is not null, assume were looking for a liability.');
|
||||
// find liability credit:
|
||||
$result = $this->findExistingAccount(config('firefly.valid_liabilities'), $array);
|
||||
if (null === $result) {
|
||||
Log::error('Did not find a liability account, return false.');
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Return true, found #%d ("%s")', $result->id, $result->name));
|
||||
$this->source = $result;
|
||||
return true;
|
||||
}
|
||||
|
||||
// if array has name and is not null, return true.
|
||||
$accountName = array_key_exists('name', $array) ? $array['name'] : null;
|
||||
|
||||
$result = true;
|
||||
Log::debug('Now in validateLCDestination', $array);
|
||||
if ('' === $accountName || null === $accountName) {
|
||||
Log::error('Array must have a name, is not the case, return false.');
|
||||
$result = false;
|
||||
}
|
||||
if (true === $result) {
|
||||
Log::error('Array has a name, return true.');
|
||||
// set the source to be a (dummy) revenue account.
|
||||
$account = new Account();
|
||||
$accountType = AccountType::whereType(AccountType::LIABILITY_CREDIT)->first();
|
||||
|
@@ -749,7 +749,7 @@ return [
|
||||
'max_attempts' => env('WEBHOOK_MAX_ATTEMPTS', 3),
|
||||
],
|
||||
'can_have_virtual_amounts' => [AccountType::ASSET],
|
||||
'can_have_opening_balance' => [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD],
|
||||
'can_have_opening_balance' => [AccountType::ASSET],
|
||||
'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
|
||||
'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
|
||||
'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'],
|
||||
|
@@ -15,7 +15,9 @@
|
||||
<th>{{ trans('list.interest') }} ({{ trans('list.interest_period') }})</th>
|
||||
{% endif %}
|
||||
<th class="hidden-sm hidden-xs">{{ trans('form.account_number') }}</th>
|
||||
{% if objectType != 'liabilities' %}
|
||||
<th style="text-align: right;">{{ trans('list.currentBalance') }}</th>
|
||||
{% endif %}
|
||||
{% if objectType == 'liabilities' %}
|
||||
<th style="text-align: right;">
|
||||
{{ trans('firefly.left_in_debt') }}
|
||||
@@ -61,11 +63,13 @@
|
||||
<td>{{ account.interest }}% ({{ account.interestPeriod|lower }})</td>
|
||||
{% endif %}
|
||||
<td class="hidden-sm hidden-xs">{{ account.iban }}{% if account.iban == '' %}{{ accountGetMetaField(account, 'account_number') }}{% endif %}</td>
|
||||
{% if objectType != 'liabilities' %}
|
||||
<td style="text-align: right;">
|
||||
<span style="margin-right:5px;">
|
||||
{{ formatAmountByAccount(account, account.endBalance) }}
|
||||
</span>
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if objectType == 'liabilities' %}
|
||||
<td style="text-align: right;">
|
||||
{% if '-' != account.current_debt %}
|
||||
|
Reference in New Issue
Block a user