mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-15 16:57:09 +00:00
Do not try to correct transactions between asset / liability with foreign amount info.
This commit is contained in:
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Console\Commands\Correction;
|
namespace FireflyIII\Console\Commands\Correction;
|
||||||
|
|
||||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||||
|
use FireflyIII\Enums\AccountTypeEnum;
|
||||||
use FireflyIII\Enums\TransactionTypeEnum;
|
use FireflyIII\Enums\TransactionTypeEnum;
|
||||||
use FireflyIII\Models\Transaction;
|
use FireflyIII\Models\Transaction;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
@@ -37,8 +38,8 @@ class CorrectsUnevenAmount extends Command
|
|||||||
{
|
{
|
||||||
use ShowsFriendlyMessages;
|
use ShowsFriendlyMessages;
|
||||||
|
|
||||||
protected $description = 'Fix journals with uneven amounts.';
|
protected $description = 'Fix journals with uneven amounts.';
|
||||||
protected $signature = 'correction:uneven-amounts';
|
protected $signature = 'correction:uneven-amounts';
|
||||||
private int $count;
|
private int $count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,7 +48,10 @@ class CorrectsUnevenAmount extends Command
|
|||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
$this->count = 0;
|
$this->count = 0;
|
||||||
|
// convert transfers with foreign currency info where the amount is NOT uneven (it should be)
|
||||||
$this->convertOldStyleTransfers();
|
$this->convertOldStyleTransfers();
|
||||||
|
|
||||||
|
|
||||||
$this->fixUnevenAmounts();
|
$this->fixUnevenAmounts();
|
||||||
$this->matchCurrencies();
|
$this->matchCurrencies();
|
||||||
if (true === config('firefly.feature_flags.running_balance_column')) {
|
if (true === config('firefly.feature_flags.running_balance_column')) {
|
||||||
@@ -64,12 +68,11 @@ class CorrectsUnevenAmount extends Command
|
|||||||
Log::debug('convertOldStyleTransfers()');
|
Log::debug('convertOldStyleTransfers()');
|
||||||
// select transactions with a foreign amount and a foreign currency. and it's a transfer. and they are different.
|
// select transactions with a foreign amount and a foreign currency. and it's a transfer. and they are different.
|
||||||
$transactions = Transaction::distinct()
|
$transactions = Transaction::distinct()
|
||||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||||
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
->leftJoin('transaction_types', 'transaction_types.id', 'transaction_journals.transaction_type_id')
|
||||||
->where('transaction_types.type', TransactionTypeEnum::TRANSFER->value)
|
->where('transaction_types.type', TransactionTypeEnum::TRANSFER->value)
|
||||||
->whereNotNull('foreign_currency_id')
|
->whereNotNull('foreign_currency_id')
|
||||||
->whereNotNull('foreign_amount')->get(['transactions.transaction_journal_id'])
|
->whereNotNull('foreign_amount')->get(['transactions.transaction_journal_id']);
|
||||||
;
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
Log::debug(sprintf('Found %d potential journal(s)', $transactions->count()));
|
Log::debug(sprintf('Found %d potential journal(s)', $transactions->count()));
|
||||||
@@ -77,7 +80,7 @@ class CorrectsUnevenAmount extends Command
|
|||||||
/** @var Transaction $transaction */
|
/** @var Transaction $transaction */
|
||||||
foreach ($transactions as $transaction) {
|
foreach ($transactions as $transaction) {
|
||||||
/** @var null|TransactionJournal $journal */
|
/** @var null|TransactionJournal $journal */
|
||||||
$journal = TransactionJournal::find($transaction->transaction_journal_id);
|
$journal = TransactionJournal::find($transaction->transaction_journal_id);
|
||||||
if (null === $journal) {
|
if (null === $journal) {
|
||||||
Log::debug('Found no journal, continue.');
|
Log::debug('Found no journal, continue.');
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ class CorrectsUnevenAmount extends Command
|
|||||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||||
|
|
||||||
/** @var null|Transaction $source */
|
/** @var null|Transaction $source */
|
||||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
if (null === $destination || null === $source) {
|
if (null === $destination || null === $source) {
|
||||||
Log::debug('Source or destination transaction is NULL, continue.');
|
Log::debug('Source or destination transaction is NULL, continue.');
|
||||||
|
|
||||||
@@ -119,11 +122,11 @@ class CorrectsUnevenAmount extends Command
|
|||||||
|
|
||||||
private function fixUnevenAmounts(): void
|
private function fixUnevenAmounts(): void
|
||||||
{
|
{
|
||||||
|
Log::debug('fixUnevenAmounts()');
|
||||||
$journals = DB::table('transactions')
|
$journals = DB::table('transactions')
|
||||||
->groupBy('transaction_journal_id')
|
->groupBy('transaction_journal_id')
|
||||||
->whereNull('deleted_at')
|
->whereNull('deleted_at')
|
||||||
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')])
|
->get(['transaction_journal_id', DB::raw('SUM(amount) AS the_sum')]);
|
||||||
;
|
|
||||||
|
|
||||||
/** @var \stdClass $entry */
|
/** @var \stdClass $entry */
|
||||||
foreach ($journals as $entry) {
|
foreach ($journals as $entry) {
|
||||||
@@ -161,13 +164,13 @@ class CorrectsUnevenAmount extends Command
|
|||||||
private function fixJournal(int $param): void
|
private function fixJournal(int $param): void
|
||||||
{
|
{
|
||||||
// one of the transactions is bad.
|
// one of the transactions is bad.
|
||||||
$journal = TransactionJournal::find($param);
|
$journal = TransactionJournal::find($param);
|
||||||
if (null === $journal) {
|
if (null === $journal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var null|Transaction $source */
|
/** @var null|Transaction $source */
|
||||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
|
|
||||||
if (null === $source) {
|
if (null === $source) {
|
||||||
$this->friendlyError(
|
$this->friendlyError(
|
||||||
@@ -184,11 +187,11 @@ class CorrectsUnevenAmount extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$amount = bcmul('-1', (string) $source->amount);
|
$amount = bcmul('-1', (string) $source->amount);
|
||||||
|
|
||||||
// fix amount of destination:
|
// fix amount of destination:
|
||||||
/** @var null|Transaction $destination */
|
/** @var null|Transaction $destination */
|
||||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||||
|
|
||||||
if (null === $destination) {
|
if (null === $destination) {
|
||||||
$this->friendlyError(
|
$this->friendlyError(
|
||||||
@@ -207,13 +210,13 @@ class CorrectsUnevenAmount extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
// may still be able to salvage this journal if it is a transfer with foreign currency info
|
// may still be able to salvage this journal if it is a transfer with foreign currency info
|
||||||
if ($this->isForeignCurrencyTransfer($journal)) {
|
if ($this->isForeignCurrencyTransfer($journal) || $this->isBetweenAssetAndLiability($journal)) {
|
||||||
Log::debug(sprintf('Can skip foreign currency transfer #%d.', $journal->id));
|
Log::debug(sprintf('Can skip foreign currency transfer #%d.', $journal->id));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$message = sprintf('Sum of journal #%d is not zero, journal is broken and now fixed.', $journal->id);
|
$message = sprintf('Sum of journal #%d is not zero, journal is broken and now fixed.', $journal->id);
|
||||||
|
|
||||||
$this->friendlyWarning($message);
|
$this->friendlyWarning($message);
|
||||||
app('log')->warning($message);
|
app('log')->warning($message);
|
||||||
@@ -221,7 +224,7 @@ class CorrectsUnevenAmount extends Command
|
|||||||
$destination->amount = $amount;
|
$destination->amount = $amount;
|
||||||
$destination->save();
|
$destination->save();
|
||||||
|
|
||||||
$message = sprintf('Corrected amount in transaction journal #%d', $param);
|
$message = sprintf('Corrected amount in transaction journal #%d', $param);
|
||||||
$this->friendlyInfo($message);
|
$this->friendlyInfo($message);
|
||||||
++$this->count;
|
++$this->count;
|
||||||
}
|
}
|
||||||
@@ -236,7 +239,7 @@ class CorrectsUnevenAmount extends Command
|
|||||||
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
$destination = $journal->transactions()->where('amount', '>', 0)->first();
|
||||||
|
|
||||||
/** @var Transaction $source */
|
/** @var Transaction $source */
|
||||||
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
$source = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
|
|
||||||
// safety catch on NULL should not be necessary, we just had that catch.
|
// safety catch on NULL should not be necessary, we just had that catch.
|
||||||
// source amount = dest foreign amount
|
// source amount = dest foreign amount
|
||||||
@@ -263,11 +266,10 @@ class CorrectsUnevenAmount extends Command
|
|||||||
private function matchCurrencies(): void
|
private function matchCurrencies(): void
|
||||||
{
|
{
|
||||||
$journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
$journals = TransactionJournal::leftJoin('transactions', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||||
->where('transactions.transaction_currency_id', '!=', DB::raw('transaction_journals.transaction_currency_id'))
|
->where('transactions.transaction_currency_id', '!=', DB::raw('transaction_journals.transaction_currency_id'))
|
||||||
->get(['transaction_journals.*'])
|
->get(['transaction_journals.*']);
|
||||||
;
|
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
/** @var TransactionJournal $journal */
|
/** @var TransactionJournal $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
@@ -285,4 +287,42 @@ class CorrectsUnevenAmount extends Command
|
|||||||
|
|
||||||
$this->friendlyPositive(sprintf('Fixed %d journal(s) with mismatched currencies.', $journals->count()));
|
$this->friendlyPositive(sprintf('Fixed %d journal(s) with mismatched currencies.', $journals->count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function isBetweenAssetAndLiability(TransactionJournal $journal): bool
|
||||||
|
{
|
||||||
|
/** @var Transaction $sourceTransaction */
|
||||||
|
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
|
||||||
|
/** @var Transaction $destinationTransaction */
|
||||||
|
$destinationTransaction = $journal->transactions()->where('amount', '>', 0)->first();
|
||||||
|
if (null === $sourceTransaction || null === $destinationTransaction) {
|
||||||
|
Log::warning('Either transaction is false, stop.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null === $sourceTransaction->foreign_amount || null === $destinationTransaction->foreign_amount) {
|
||||||
|
Log::warning('Either foreign amount is false, stop.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = $sourceTransaction->account;
|
||||||
|
$destination = $destinationTransaction->account;
|
||||||
|
|
||||||
|
if (null === $source || null === $destination) {
|
||||||
|
Log::warning('Either is false, stop.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sourceTypes = [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
|
||||||
|
|
||||||
|
// source is liability, destination is asset
|
||||||
|
if (in_array($source->accountType->type, $sourceTypes, true) && AccountTypeEnum::ASSET->value === $destination->accountType->type) {
|
||||||
|
Log::debug('Source is a liability account, destination is an asset account, return TRUE.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// source is asset, destination is liability
|
||||||
|
if (in_array($destination->accountType->type, $sourceTypes, true) && AccountTypeEnum::ASSET->value === $source->accountType->type) {
|
||||||
|
Log::debug('Destination is a liability account, source is an asset account, return TRUE.');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Log::debug('Not between asset and liability, return FALSE');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user