Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop

# Conflicts:
#	app/Providers/EventServiceProvider.php
This commit is contained in:
James Cole
2026-01-24 16:46:08 +01:00
50 changed files with 771 additions and 715 deletions

View File

@@ -79,28 +79,28 @@ class ShowController extends Controller
}
Log::channel('audit')->info('User views all webhooks.');
$manager = $this->getManager();
$collection = $this->repository->all();
$pageSize = $this->parameters->get('limit');
$count = $collection->count();
$webhooks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
$manager = $this->getManager();
$collection = $this->repository->all();
$pageSize = $this->parameters->get('limit');
$count = $collection->count();
$webhooks = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.webhooks.index') . $this->buildParams());
$paginator = new LengthAwarePaginator($webhooks, $count, $pageSize, $this->parameters->get('page'));
$paginator->setPath(route('api.v1.webhooks.index').$this->buildParams());
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
$webhooks = $enrichment->enrich($webhooks);
$webhooks = $enrichment->enrich($webhooks);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new FractalCollection($webhooks, $transformer, self::RESOURCE_KEY);
$resource = new FractalCollection($webhooks, $transformer, self::RESOURCE_KEY);
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
@@ -121,19 +121,19 @@ class ShowController extends Controller
}
Log::channel('audit')->info(sprintf('User views webhook #%d.', $webhook->id));
$manager = $this->getManager();
$manager = $this->getManager();
// enrich
/** @var User $admin */
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$admin = auth()->user();
$enrichment = new WebhookEnrichment();
$enrichment->setUser($admin);
$webhook = $enrichment->enrichSingle($webhook);
$webhook = $enrichment->enrichSingle($webhook);
/** @var WebhookTransformer $transformer */
$transformer = app(WebhookTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($webhook, $transformer, self::RESOURCE_KEY);
$resource = new Item($webhook, $transformer, self::RESOURCE_KEY);
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}
@@ -148,10 +148,10 @@ class ShowController extends Controller
{
if (false === FireflyConfig::get('allow_webhooks', config('firefly.allow_webhooks'))->data) {
Log::channel('audit')->info(sprintf(
'User tries to trigger webhook #%d on transaction group #%d, but webhooks are DISABLED.',
$webhook->id,
$group->id
));
'User tries to trigger webhook #%d on transaction group #%d, but webhooks are DISABLED.',
$webhook->id,
$group->id
));
throw new NotFoundHttpException('Webhooks are not enabled.');
}
@@ -166,7 +166,7 @@ class ShowController extends Controller
$engine->setUser(auth()->user());
// tell the generator which trigger it should look for
$engine->setTrigger(WebhookTrigger::tryFrom((int)$trigger->key));
$engine->setTrigger(WebhookTrigger::tryFrom((int) $trigger->key));
// tell the generator which objects to process
$engine->setObjects(new Collection()->push($group));
// set the webhook to trigger

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* TransactionGroupRequestsAuditLogEntry.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -29,5 +32,11 @@ class TransactionGroupRequestsAuditLogEntry extends Event
{
use SerializesModels;
public function __construct(public Model $changer, public Model $auditable, public string $field, public mixed $before, public mixed $after) {}
public function __construct(
public Model $changer,
public Model $auditable,
public string $field,
public mixed $before,
public mixed $after
) {}
}

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* TransactionGroupsRequestedReporting.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -32,6 +35,8 @@ class TransactionGroupsRequestedReporting extends Event
/**
* Create a new event instance.
*/
public function __construct(public int $userId, public Collection $groups) {}
public function __construct(
public int $userId,
public Collection $groups
) {}
}

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* WebhookMessagesRequestSending.php
* Copyright (c) 2026 james@firefly-iii.org

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* SystemRequestedVersionCheck.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -29,5 +32,7 @@ class SystemRequestedVersionCheck extends Event
{
use SerializesModels;
public function __construct(public User $user) {}
public function __construct(
public User $user
) {}
}

View File

@@ -47,8 +47,8 @@ class DestroyedGroupEventHandler
private function triggerWebhooks(DestroyedTransactionGroup $destroyedGroupEvent): void
{
Log::debug('DestroyedTransactionGroup:triggerWebhooks');
$group = $destroyedGroupEvent->transactionGroup;
$user = $group->user;
$group = $destroyedGroupEvent->transactionGroup;
$user = $group->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);

View File

@@ -71,14 +71,14 @@ class StoredGroupEventHandler
}
Log::debug('Now in StoredGroupEventHandler::processRules()');
$journals = $storedGroupEvent->transactionGroup->transactionJournals;
$array = [];
$journals = $storedGroupEvent->transactionGroup->transactionJournals;
$array = [];
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$array[] = $journal->id;
}
$journalIds = implode(',', $array);
$journalIds = implode(',', $array);
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
@@ -97,16 +97,16 @@ class StoredGroupEventHandler
}
// create and fire rule engine.
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($storedGroupEvent->transactionGroup->user);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->setRuleGroups($groups);
$newRuleEngine->fire();
}
private function recalculateCredit(StoredTransactionGroup $event): void
{
$group = $event->transactionGroup;
$group = $event->transactionGroup;
/** @var CreditRecalculateService $object */
$object = app(CreditRecalculateService::class);
@@ -122,10 +122,10 @@ class StoredGroupEventHandler
/** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) {
/** @var null|Transaction $source */
$source = $journal->transactions()->where('amount', '<', '0')->first();
$source = $journal->transactions()->where('amount', '<', '0')->first();
/** @var null|Transaction $dest */
$dest = $journal->transactions()->where('amount', '>', '0')->first();
$dest = $journal->transactions()->where('amount', '>', '0')->first();
if (null !== $source) {
$repository->deleteStatisticsForModel($source->account, $journal->date);
@@ -160,14 +160,14 @@ class StoredGroupEventHandler
private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void
{
Log::debug(__METHOD__);
$group = $storedGroupEvent->transactionGroup;
$group = $storedGroupEvent->transactionGroup;
if (false === $storedGroupEvent->fireWebhooks) {
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
return;
}
$user = $group->user;
$user = $group->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);

View File

@@ -67,8 +67,8 @@ class UpdatedGroupEventHandler
/** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) {
$source = $journal->transactions()->where('amount', '<', '0')->first();
$dest = $journal->transactions()->where('amount', '>', '0')->first();
$source = $journal->transactions()->where('amount', '<', '0')->first();
$dest = $journal->transactions()->where('amount', '>', '0')->first();
if (null !== $source) {
$repository->deleteStatisticsForModel($source->account, $journal->date);
}
@@ -103,20 +103,21 @@ class UpdatedGroupEventHandler
*/
public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void
{
$group = $updatedGroupEvent->transactionGroup;
$group = $updatedGroupEvent->transactionGroup;
if (1 === $group->transactionJournals->count()) {
return;
}
// first journal:
/** @var null|TransactionJournal $first */
$first = $group
$first = $group
->transactionJournals()
->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->first();
->first()
;
if (null === $first) {
Log::warning(sprintf('Group #%d has no transaction journals.', $group->id));
@@ -124,15 +125,15 @@ class UpdatedGroupEventHandler
return;
}
$all = $group->transactionJournals()->get()->pluck('id')->toArray();
$all = $group->transactionJournals()->get()->pluck('id')->toArray();
/** @var Account $sourceAccount */
$sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account;
/** @var Account $destAccount */
$destAccount = $first->transactions()->where('amount', '>', '0')->first()->account;
$destAccount = $first->transactions()->where('amount', '>', '0')->first()->account;
$type = $first->transactionType->type;
$type = $first->transactionType->type;
if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::WITHDRAWAL->value === $type) {
// set all source transactions to source account:
Transaction::whereIn('transaction_journal_id', $all)->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]);
@@ -154,33 +155,33 @@ class UpdatedGroupEventHandler
return;
}
$journals = $updatedGroupEvent->transactionGroup->transactionJournals;
$array = [];
$journals = $updatedGroupEvent->transactionGroup->transactionJournals;
$array = [];
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$array[] = $journal->id;
}
$journalIds = implode(',', $array);
$journalIds = implode(',', $array);
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
$ruleGroupRepository->setUser($updatedGroupEvent->transactionGroup->user);
$groups = $ruleGroupRepository->getRuleGroupsWithRules('update-journal');
$groups = $ruleGroupRepository->getRuleGroupsWithRules('update-journal');
// file rule engine.
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($updatedGroupEvent->transactionGroup->user);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->setRuleGroups($groups);
$newRuleEngine->fire();
}
private function recalculateCredit(UpdatedTransactionGroup $event): void
{
$group = $event->transactionGroup;
$group = $event->transactionGroup;
/** @var CreditRecalculateService $object */
$object = app(CreditRecalculateService::class);
@@ -191,13 +192,13 @@ class UpdatedGroupEventHandler
private function triggerWebhooks(UpdatedTransactionGroup $updatedGroupEvent): void
{
Log::debug(__METHOD__);
$group = $updatedGroupEvent->transactionGroup;
$group = $updatedGroupEvent->transactionGroup;
if (false === $updatedGroupEvent->fireWebhooks) {
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
return;
}
$user = $group->user;
$user = $group->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);

View File

@@ -49,7 +49,7 @@ class BudgetLimitObserver
$singleton = PreferencesSingleton::getInstance();
if (true === $singleton->getPreference('fire_webhooks_bl_store')) {
$user = $budgetLimit->budget->user;
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
@@ -73,7 +73,7 @@ class BudgetLimitObserver
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($budgetLimit->budget->user->userGroup);
$budgetLimit->native_amount = null;
if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) {
$converter = new ExchangeRateConverter();
$converter = new ExchangeRateConverter();
$converter->setUserGroup($budgetLimit->budget->user->userGroup);
$converter->setIgnoreSettings(true);
$budgetLimit->native_amount = $converter->convert($budgetLimit->transactionCurrency, $userCurrency, today(), $budgetLimit->amount);
@@ -92,7 +92,7 @@ class BudgetLimitObserver
$singleton = PreferencesSingleton::getInstance();
if (true === $singleton->getPreference('fire_webhooks_bl_update')) {
$user = $budgetLimit->budget->user;
$user = $budgetLimit->budget->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);

View File

@@ -786,6 +786,7 @@ trait MetaCollection
Log::debug(sprintf('"%s" versus', strtolower((string) $tag['name'])), $list);
if (in_array(strtolower((string) $tag['name']), $list, true)) {
Log::debug(sprintf('Transaction has tag "%s" so return true.', $tag['name']));
return true;
}
}

View File

@@ -48,6 +48,7 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Override;
use Safe\Exceptions\JsonException;
use function Safe\json_decode;
/**
@@ -67,15 +68,15 @@ class GroupCollector implements GroupCollectorInterface
*/
public function __construct()
{
$this->sorting = [];
$this->postFilters = [];
$this->tags = [];
$this->user = null;
$this->userGroup = null;
$this->limit = null;
$this->page = null;
$this->startRow = null;
$this->endRow = null;
$this->sorting = [];
$this->postFilters = [];
$this->tags = [];
$this->user = null;
$this->userGroup = null;
$this->limit = null;
$this->page = null;
$this->startRow = null;
$this->endRow = null;
$this->hasAccountInfo = false;
$this->hasCatInformation = false;
@@ -273,9 +274,9 @@ class GroupCollector implements GroupCollectorInterface
foreach ($params as $param) {
$replace = sprintf('"%s"', $param);
if (is_int($param)) {
$replace = (string)$param;
$replace = (string) $param;
}
$pos = strpos($query, '?');
$pos = strpos($query, '?');
if (false !== $pos) {
$query = substr_replace($query, $replace, $pos, 1);
}
@@ -439,17 +440,17 @@ class GroupCollector implements GroupCollectorInterface
// add to query:
$this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds);
}
$result = $this->query->get($this->fields);
$result = $this->query->get($this->fields);
$this->dumpQueryInLogs();
Log::debug(sprintf('Count of result is %d', $result->count()));
// now to parse this into an array.
$collection = $this->parseArray($result);
$collection = $this->parseArray($result);
// filter the array using all available post filters:
$collection = $this->postFilterCollection($collection);
$collection = $this->postFilterCollection($collection);
// sort the collection, if sort instructions are present.
$collection = $this->sortCollection($collection);
$collection = $this->sortCollection($collection);
// count it and continue:
$this->total = $collection->count();
@@ -473,7 +474,8 @@ class GroupCollector implements GroupCollectorInterface
return $this->query
->get(['transaction_journals.transaction_group_id'])
->pluck('transaction_group_id')
->toArray();
->toArray()
;
}
/**
@@ -485,13 +487,13 @@ class GroupCollector implements GroupCollectorInterface
/** @var TransactionJournal $augumentedJournal */
foreach ($collection as $augumentedJournal) {
$groupId = (int)$augumentedJournal->transaction_group_id;
$groupId = (int) $augumentedJournal->transaction_group_id;
if (!array_key_exists($groupId, $groups)) {
// make new array
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
$groupArray = [
'id' => (int)$augumentedJournal->transaction_group_id,
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
$groupArray = [
'id' => (int) $augumentedJournal->transaction_group_id,
'user_id' => $augumentedJournal->user_id,
'user_group_id' => $augumentedJournal->user_group_id,
// Field transaction_group_title was added by the query.
@@ -504,7 +506,7 @@ class GroupCollector implements GroupCollectorInterface
'transactions' => [],
];
// Field transaction_journal_id was added by the query.
$journalId = (int)$augumentedJournal->transaction_journal_id;
$journalId = (int) $augumentedJournal->transaction_journal_id;
$groupArray['transactions'][$journalId] = $parsedGroup;
$groups[$groupId] = $groupArray;
@@ -512,7 +514,7 @@ class GroupCollector implements GroupCollectorInterface
}
// or parse the rest.
// Field transaction_journal_id was added by the query.
$journalId = (int)$augumentedJournal->transaction_journal_id;
$journalId = (int) $augumentedJournal->transaction_journal_id;
if (array_key_exists($journalId, $groups[$groupId]['transactions'])) {
// append data to existing group + journal (for multiple tags or multiple attachments)
$groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal);
@@ -562,27 +564,27 @@ class GroupCollector implements GroupCollectorInterface
}
// try to process meta date value (if present)
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
$dates = ['interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date'];
if (array_key_exists('meta_name', $result) && in_array($result['meta_name'], $dates, true)) {
$name = $result['meta_name'];
if (array_key_exists('meta_data', $result) && '' !== (string)$result['meta_data']) {
$result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string)json_decode((string)$result['meta_data']), 0, 10));
if (array_key_exists('meta_data', $result) && '' !== (string) $result['meta_data']) {
$result[$name] = Carbon::createFromFormat('!Y-m-d', substr((string) json_decode((string) $result['meta_data']), 0, 10));
}
}
// convert values to integers:
$result = $this->convertToInteger($result);
$result = $this->convertToInteger($result);
// convert to boolean
$result = $this->convertToBoolean($result);
$result = $this->convertToBoolean($result);
// convert back to strings because SQLite is dumb like that.
$result = $this->convertToStrings($result);
$result = $this->convertToStrings($result);
$result['reconciled'] = 1 === (int)$result['reconciled'];
$result['reconciled'] = 1 === (int) $result['reconciled'];
if (array_key_exists('tag_id', $result) && null !== $result['tag_id']) { // assume the other fields are present as well.
$tagId = (int)$augumentedJournal['tag_id'];
$tagDate = null;
$tagId = (int) $augumentedJournal['tag_id'];
$tagDate = null;
try {
$tagDate = Carbon::parse($augumentedJournal['tag_date']);
@@ -591,7 +593,7 @@ class GroupCollector implements GroupCollectorInterface
}
$result['tags'][$tagId] = [
'id' => (int)$result['tag_id'],
'id' => (int) $result['tag_id'],
'name' => $result['tag_name'],
'date' => $tagDate,
'description' => $result['tag_description'],
@@ -600,8 +602,8 @@ class GroupCollector implements GroupCollectorInterface
// also merge attachments:
if (array_key_exists('attachment_id', $result) && null !== $result['attachment_id']) {
$uploaded = 1 === (int)$result['attachment_uploaded'];
$attachmentId = (int)$augumentedJournal['attachment_id'];
$uploaded = 1 === (int) $result['attachment_uploaded'];
$attachmentId = (int) $augumentedJournal['attachment_id'];
$deleted = null !== $result['attachment_deleted_at'];
if (0 !== $attachmentId && $uploaded && !$deleted) {
$result['attachments'][$attachmentId] = [
@@ -635,7 +637,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToInteger(array $array): array
{
foreach ($this->integerFields as $field) {
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (int)$array[$field] : null;
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (int) $array[$field] : null;
}
return $array;
@@ -644,7 +646,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToBoolean(array $array): array
{
foreach ($this->booleanFields as $field) {
$array[$field] = array_key_exists($field, $array) ? (bool)$array[$field] : null;
$array[$field] = array_key_exists($field, $array) ? (bool) $array[$field] : null;
}
return $array;
@@ -653,7 +655,7 @@ class GroupCollector implements GroupCollectorInterface
private function convertToStrings(array $array): array
{
foreach ($this->stringFields as $field) {
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string)$array[$field] : null;
$array[$field] = array_key_exists($field, $array) && null !== $array[$field] ? (string) $array[$field] : null;
}
return $array;
@@ -663,9 +665,9 @@ class GroupCollector implements GroupCollectorInterface
{
$newArray = $newJournal->toArray();
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
$tagId = (int)$newJournal['tag_id'];
$tagId = (int) $newJournal['tag_id'];
$tagDate = null;
$tagDate = null;
try {
$tagDate = Carbon::parse($newArray['tag_date']);
@@ -674,7 +676,7 @@ class GroupCollector implements GroupCollectorInterface
}
$existingJournal['tags'][$tagId] = [
'id' => (int)$newArray['tag_id'],
'id' => (int) $newArray['tag_id'],
'name' => $newArray['tag_name'],
'date' => $tagDate,
'description' => $newArray['tag_description'],
@@ -688,7 +690,7 @@ class GroupCollector implements GroupCollectorInterface
{
$newArray = $newJournal->toArray();
if (array_key_exists('attachment_id', $newArray)) {
$attachmentId = (int)$newJournal['attachment_id'];
$attachmentId = (int) $newJournal['attachment_id'];
$existingJournal['attachments'][$attachmentId] = ['id' => $attachmentId];
}
@@ -705,13 +707,13 @@ class GroupCollector implements GroupCollectorInterface
foreach ($groups as $groudId => $group) {
/** @var array $transaction */
foreach ($group['transactions'] as $transaction) {
$currencyId = (int)$transaction['currency_id'];
$currencyId = (int) $transaction['currency_id'];
if (null === $transaction['amount']) {
throw new FireflyException(sprintf('Amount is NULL for a transaction in group #%d, please investigate.', $groudId));
}
$pcAmount = (string)('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
$pcForeignAmount = (string)('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
$foreignAmount = (string)('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
$pcAmount = (string) ('' === $transaction['pc_amount'] ? '0' : $transaction['pc_amount']);
$pcForeignAmount = (string) ('' === $transaction['pc_foreign_amount'] ? '0' : $transaction['pc_foreign_amount']);
$foreignAmount = (string) ('' === $transaction['foreign_amount'] ? '0' : $transaction['foreign_amount']);
// set default:
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
@@ -722,11 +724,11 @@ class GroupCollector implements GroupCollectorInterface
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount);
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $transaction['amount']);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['pc_amount'], $pcAmount);
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$currencyId = (int)$transaction['foreign_currency_id'];
$currencyId = (int) $transaction['foreign_currency_id'];
// set default:
if (!array_key_exists($currencyId, $groups[$groudId]['sums'])) {
@@ -737,7 +739,7 @@ class GroupCollector implements GroupCollectorInterface
$groups[$groudId]['sums'][$currencyId]['amount'] = '0';
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = '0';
}
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string)$groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount);
$groups[$groudId]['sums'][$currencyId]['amount'] = bcadd((string) $groups[$groudId]['sums'][$currencyId]['amount'], $foreignAmount);
$groups[$groudId]['sums'][$currencyId]['pc_amount'] = bcadd($groups[$groudId]['sums'][$currencyId]['amount'], $pcForeignAmount);
}
}
@@ -753,10 +755,15 @@ class GroupCollector implements GroupCollectorInterface
if (0 === $countFilters) {
return $currentCollection;
}
Log::debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection)));
Log::debug(sprintf(
'GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).',
count($this->postFilters),
count($currentCollection)
));
if (0 === $currentCollection->count()) {
Log::debug('Found nothing anyway, return empty collection.');
return $currentCollection;
}
@@ -765,7 +772,7 @@ class GroupCollector implements GroupCollectorInterface
*/
foreach ($this->postFilters as $function) {
Log::debug('Applying filter...');
$nextCollection = new Collection();
$nextCollection = new Collection();
// loop everything in the current collection
// and save it (or not) in the new collection.
@@ -778,18 +785,21 @@ class GroupCollector implements GroupCollectorInterface
if (false === $result) {
// skip other filters, continue to next item.
Log::debug('Result is false');
continue;
}
// if the result is a bool, use the unedited results.
if (true === $result) {
Log::debug('Result is true');
$nextCollection->push($item);
continue;
}
// if the result is an array, the filter has changed what's being returned.
if (is_array($result)) {
Log::debug('Result is array');
$nextCollection->push($result);
continue;
}
Log::debug('Result is something else!');
@@ -836,7 +846,7 @@ class GroupCollector implements GroupCollectorInterface
public function getPaginatedGroups(): LengthAwarePaginator
{
Log::debug('Now in getPaginatedGroups()');
$set = $this->getGroups();
$set = $this->getGroups();
if (0 === $this->limit) {
$this->setLimit(50);
}
@@ -1067,7 +1077,8 @@ class GroupCollector implements GroupCollectorInterface
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
->orderBy('source.amount', 'DESC')
;
}
/**
@@ -1112,7 +1123,8 @@ class GroupCollector implements GroupCollectorInterface
->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC')
->orderBy('source.amount', 'DESC');
->orderBy('source.amount', 'DESC')
;
}
/**
@@ -1128,7 +1140,8 @@ class GroupCollector implements GroupCollectorInterface
// include budget ID + name (if any)
->withBudgetInformation()
// include bill ID + name (if any)
->withBillInformation();
->withBillInformation()
;
return $this;
}

View File

@@ -63,11 +63,11 @@ class HomeController extends Controller
*/
public function dateRange(Request $request): JsonResponse
{
$stringStart = '';
$stringEnd = '';
$stringStart = '';
$stringEnd = '';
try {
$stringStart = e((string)$request->get('start'));
$stringStart = e((string) $request->get('start'));
$start = Carbon::createFromFormat('Y-m-d', $stringStart);
} catch (InvalidFormatException) {
Log::error(sprintf('Start: could not parse date string "%s" so ignore it.', $stringStart));
@@ -75,7 +75,7 @@ class HomeController extends Controller
}
try {
$stringEnd = e((string)$request->get('end'));
$stringEnd = e((string) $request->get('end'));
$end = Carbon::createFromFormat('Y-m-d', $stringEnd);
} catch (InvalidFormatException) {
Log::error(sprintf('End could not parse date string "%s" so ignore it.', $stringEnd));
@@ -91,18 +91,18 @@ class HomeController extends Controller
$label = $request->get('label');
$isCustomRange = false;
Log::debug('dateRange: Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
Log::debug('dateRange: Received dateRange', ['start' => $stringStart, 'end' => $stringEnd, 'label' => $request->get('label')]);
// check if the label is "everything" or "Custom range" which will betray
// a possible problem with the budgets.
if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) {
if ($label === (string) trans('firefly.everything') || $label === (string) trans('firefly.customRange')) {
$isCustomRange = true;
Log::debug('Range is now marked as "custom".');
}
$diff = $start->diffInDays($end, true) + 1;
$diff = $start->diffInDays($end, true) + 1;
if ($diff > 366) {
$request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => (int)$diff]));
$request->session()->flash('warning', (string) trans('firefly.warning_much_data', ['days' => (int) $diff]));
}
$request->session()->put('is_custom_range', $isCustomRange);
@@ -130,10 +130,10 @@ class HomeController extends Controller
return redirect(route('new-user.index'));
}
if ('v1' === (string)config('view.layout')) {
if ('v1' === (string) config('view.layout')) {
return $this->indexV1($repository);
}
if ('v2' === (string)config('view.layout')) {
if ('v2' === (string) config('view.layout')) {
return $this->indexV2();
}
@@ -143,9 +143,9 @@ class HomeController extends Controller
private function indexV1(AccountRepositoryInterface $repository): mixed
{
$types = config('firefly.accountTypesByIdentifier.asset');
$pageTitle = (string)trans('firefly.main_dashboard_page_title');
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
$count = $repository->count($types);
$subTitle = (string)trans('firefly.welcome_back');
$subTitle = (string) trans('firefly.welcome_back');
$transactions = [];
$frontpage = Preferences::getFresh('frontpageAccounts', $repository->getAccountsByType([AccountTypeEnum::ASSET->value])->pluck('id')->toArray());
$frontpageArray = $frontpage->data;
@@ -154,13 +154,13 @@ class HomeController extends Controller
}
/** @var Carbon $start */
$start = session('start', today(config('app.timezone'))->startOfMonth());
$start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth());
$accounts = $repository->getAccountsById($frontpageArray);
$today = today(config('app.timezone'));
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
$end = session('end', today(config('app.timezone'))->endOfMonth());
$accounts = $repository->getAccountsById($frontpageArray);
$today = today(config('app.timezone'));
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
Log::debug('Frontpage accounts are ', $frontpageArray);
@@ -170,14 +170,14 @@ class HomeController extends Controller
// collect groups for each transaction.
foreach ($accounts as $account) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection()->push($account))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getExtractedJournals();
$transactions[] = ['transactions' => $set, 'account' => $account];
$transactions[] = ['transactions' => $set, 'account' => $account];
}
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
event(new SystemRequestedVersionCheck($user));
return view('index', [
@@ -194,16 +194,16 @@ class HomeController extends Controller
private function indexV2(): mixed
{
$subTitle = (string)trans('firefly.welcome_back');
$pageTitle = (string)trans('firefly.main_dashboard_page_title');
$subTitle = (string) trans('firefly.welcome_back');
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
$start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
$start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
event(new SystemRequestedVersionCheck($user));
return view('index', ['subTitle' => $subTitle, 'start' => $start, 'end' => $end, 'pageTitle' => $pageTitle]);
return view('index', ['subTitle' => $subTitle, 'start' => $start, 'end' => $end, 'pageTitle' => $pageTitle]);
}
}

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* MailsNewTransactionsReport.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -33,7 +36,8 @@ use Illuminate\Support\Facades\Notification;
class MailsNewTransactionsReport
{
public function handle(TransactionGroupsRequestedReporting $event) {
public function handle(TransactionGroupsRequestedReporting $event): void
{
Log::debug('In MailsNewTransactionsReport.');
/** @var UserRepositoryInterface $repository */
@@ -85,5 +89,4 @@ class MailsNewTransactionsReport
}
Log::debug('If there is no error above this line, message was sent.');
}
}

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* StoresAuditLogEntry.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -28,7 +31,8 @@ use Illuminate\Support\Facades\Log;
class StoresAuditLogEntry
{
public function handle(TransactionGroupRequestsAuditLogEntry $event): void {
public function handle(TransactionGroupRequestsAuditLogEntry $event): void
{
$array = [
'auditable' => $event->auditable,
'changer' => $event->changer,

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* SendsWebhookMessages.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -29,7 +32,8 @@ use Illuminate\Support\Facades\Log;
class SendsWebhookMessages
{
public function handle(WebhookMessagesRequestSending $event): void {
public function handle(WebhookMessagesRequestSending $event): void
{
Log::debug(sprintf('Now in %s for %s', __METHOD__, get_class($event)));
if (false === config('firefly.feature_flags.webhooks') || false === FireflyConfig::get('allow_webhooks', config('firefly.allow_webhooks'))->data) {
Log::debug('Webhook event handler is disabled, do not run sendWebhookMessages().');
@@ -39,9 +43,9 @@ class SendsWebhookMessages
// kick off the job!
$messages = WebhookMessage::where('webhook_messages.sent', false)
->get(['webhook_messages.*'])
->filter(static fn (WebhookMessage $message): bool => $message->webhookAttempts()->count() <= 2)
->splice(0, 5)
->get(['webhook_messages.*'])
->filter(static fn (WebhookMessage $message): bool => $message->webhookAttempts()->count() <= 2)
->splice(0, 5)
;
Log::debug(sprintf('Found %d webhook message(s) ready to be send.', $messages->count()));
@@ -62,5 +66,4 @@ class SendsWebhookMessages
// clean up sent messages table:
WebhookMessage::where('webhook_messages.sent', true)->where('webhook_messages.created_at', '<', now()->subDays(14))->delete();
}
}

View File

@@ -1,4 +1,7 @@
<?php
declare(strict_types=1);
/*
* ChecksForNewVersion.php
* Copyright (c) 2026 james@firefly-iii.org
@@ -41,8 +44,8 @@ class ChecksForNewVersion
Log::debug(sprintf('Now in %s', __METHOD__));
// should not check for updates:
$permission = FireflyConfig::get('permission_update_check', -1);
$value = (int)$permission->data;
$permission = FireflyConfig::get('permission_update_check', -1);
$value = (int) $permission->data;
if (1 !== $value) {
Log::debug('Update check is not enabled.');
$this->warnToCheckForUpdates($event);
@@ -51,8 +54,8 @@ class ChecksForNewVersion
}
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
$user = $event->user;
$repository = app(UserRepositoryInterface::class);
$user = $event->user;
if (!$repository->hasRole($user, 'owner')) {
Log::debug('User is not admin, done.');
@@ -71,7 +74,7 @@ class ChecksForNewVersion
}
// last check time was more than a week ago.
Log::debug('Have not checked for a new version in a week!');
$release = $this->getLatestRelease();
$release = $this->getLatestRelease();
session()->flash($release['level'], $release['message']);
FireflyConfig::set('last_update_check', Carbon::now()->getTimestamp());
@@ -100,9 +103,9 @@ class ChecksForNewVersion
Log::debug(sprintf('Last warning time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
if ($diff < (604800 * 4)) {
Log::debug(sprintf(
'Warned about updates less than four weeks ago (on %s).',
Carbon::createFromTimestamp($lastCheckTime->data)->format('Y-m-d H:i:s')
));
'Warned about updates less than four weeks ago (on %s).',
Carbon::createFromTimestamp($lastCheckTime->data)->format('Y-m-d H:i:s')
));
return;
}

View File

@@ -56,7 +56,6 @@ class EventServiceProvider extends ServiceProvider
StoredAccount::class => ['FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit'],
UpdatedAccount::class => ['FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit'],
// preferences
UserGroupChangedPrimaryCurrency::class => ['FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts'],
];

View File

@@ -81,8 +81,8 @@ class GroupUpdateService
Log::debug('Going to update split group.');
$existing = $transactionGroup->transactionJournals->pluck('id')->toArray();
$updated = $this->updateTransactions($transactionGroup, $transactions);
$existing = $transactionGroup->transactionJournals->pluck('id')->toArray();
$updated = $this->updateTransactions($transactionGroup, $transactions);
Log::debug('Array of updated IDs: ', $updated);
if (0 === count($updated)) {
@@ -94,13 +94,13 @@ class GroupUpdateService
return $transactionGroup;
}
$result = array_diff($existing, $updated);
$result = array_diff($existing, $updated);
Log::debug('Result of DIFF: ', $result);
/** @var string $deletedId */
foreach ($result as $deletedId) {
/** @var TransactionJournal $journal */
$journal = $transactionGroup->transactionJournals()->find((int)$deletedId);
$journal = $transactionGroup->transactionJournals()->find((int) $deletedId);
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
@@ -151,10 +151,10 @@ class GroupUpdateService
*/
foreach ($transactions as $index => $transaction) {
Log::debug(sprintf('Now at #%d of %d', $index + 1, count($transactions)), $transaction);
$journalId = (int)($transaction['transaction_journal_id'] ?? 0);
$journalId = (int) ($transaction['transaction_journal_id'] ?? 0);
/** @var null|TransactionJournal $journal */
$journal = $transactionGroup->transactionJournals()->find($journalId);
$journal = $transactionGroup->transactionJournals()->find($journalId);
if (null === $journal) {
Log::debug('This entry has no existing journal: make a new split.');
// force the transaction type on the transaction data.
@@ -199,7 +199,7 @@ class GroupUpdateService
$submission = ['transactions' => [$data]];
/** @var TransactionJournalFactory $factory */
$factory = app(TransactionJournalFactory::class);
$factory = app(TransactionJournalFactory::class);
$factory->setUser($transactionGroup->user);
try {

View File

@@ -61,43 +61,41 @@ class JournalUpdateService
{
use JournalServiceTrait;
private BillRepositoryInterface $billRepository;
private CurrencyRepositoryInterface $currencyRepository;
private BillRepositoryInterface $billRepository;
private CurrencyRepositoryInterface $currencyRepository;
private TransactionGroupRepositoryInterface $transactionGroupRepository;
private array $data;
private ?Account $destinationAccount = null;
private ?Transaction $destinationTransaction = null;
private array $metaDate
= [
'interest_date',
'book_date',
'process_date',
'due_date',
'payment_date',
'invoice_date',
'_internal_previous_date',
];
private array $metaString
= [
'sepa_cc',
'sepa_ct_op',
'sepa_ct_id',
'sepa_db',
'sepa_country',
'sepa_ep',
'sepa_ci',
'sepa_batch_id',
'recurrence_id',
'internal_reference',
'bunq_payment_id',
'external_id',
'external_url',
];
private ?Account $sourceAccount = null;
private ?Transaction $sourceTransaction = null;
private ?TransactionGroup $transactionGroup = null;
private ?TransactionJournal $transactionJournal = null;
private string $startCompareHash = '';
private array $data;
private ?Account $destinationAccount = null;
private ?Transaction $destinationTransaction = null;
private array $metaDate = [
'interest_date',
'book_date',
'process_date',
'due_date',
'payment_date',
'invoice_date',
'_internal_previous_date',
];
private array $metaString = [
'sepa_cc',
'sepa_ct_op',
'sepa_ct_id',
'sepa_db',
'sepa_country',
'sepa_ep',
'sepa_ci',
'sepa_batch_id',
'recurrence_id',
'internal_reference',
'bunq_payment_id',
'external_id',
'external_url',
];
private ?Account $sourceAccount = null;
private ?Transaction $sourceTransaction = null;
private ?TransactionGroup $transactionGroup = null;
private ?TransactionJournal $transactionJournal = null;
private string $startCompareHash = '';
/**
* JournalUpdateService constructor.
@@ -120,7 +118,7 @@ class JournalUpdateService
public function setTransactionGroup(TransactionGroup $transactionGroup): void
{
$this->transactionGroup = $transactionGroup;
$this->transactionGroup = $transactionGroup;
$this->billRepository->setUser($transactionGroup->user);
$this->categoryRepository->setUser($transactionGroup->user);
$this->budgetRepository->setUser($transactionGroup->user);
@@ -191,8 +189,8 @@ class JournalUpdateService
private function hasValidSourceAccount(): bool
{
$sourceId = $this->data['source_id'] ?? null;
$sourceName = $this->data['source_name'] ?? null;
$sourceId = $this->data['source_id'] ?? null;
$sourceName = $this->data['source_name'] ?? null;
Log::debug(sprintf('Now in hasValidSourceAccount("%s","%s").', $sourceId, $sourceName));
if (!$this->hasFields(['source_id', 'source_name'])) {
@@ -207,11 +205,11 @@ class JournalUpdateService
// make a new validator.
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator = app(AccountValidator::class);
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$result = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
$result = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]);
Log::debug(sprintf('hasValidSourceAccount(%d, "%s") will return %s', $sourceId, $sourceName, var_export($result, true)));
// TODO type overrule the account validator may have a different opinion on the transaction type.
@@ -222,7 +220,7 @@ class JournalUpdateService
private function hasFields(array $fields): bool
{
return array_any($fields, fn($field): bool => array_key_exists($field, $this->data));
return array_any($fields, fn ($field): bool => array_key_exists($field, $this->data));
}
private function getOriginalSourceAccount(): Account
@@ -243,7 +241,8 @@ class JournalUpdateService
->transactions()
->with(['account'])
->where('amount', '<', 0)
->first();
->first()
;
$this->sourceTransaction = $result;
}
Log::debug(sprintf('getSourceTransaction: %s', $this->sourceTransaction->amount));
@@ -271,8 +270,8 @@ class JournalUpdateService
private function hasValidDestinationAccount(): bool
{
Log::debug('Now in hasValidDestinationAccount().');
$destId = $this->data['destination_id'] ?? null;
$destName = $this->data['destination_name'] ?? null;
$destId = $this->data['destination_id'] ?? null;
$destName = $this->data['destination_name'] ?? null;
if (!$this->hasFields(['destination_id', 'destination_name'])) {
Log::debug('No destination info submitted, grab the original data.');
@@ -282,16 +281,16 @@ class JournalUpdateService
}
// make new account validator.
$expectedType = $this->getExpectedType();
$expectedType = $this->getExpectedType();
Log::debug(sprintf('(b) Expected type (new or unchanged) is %s', $expectedType));
// make a new validator.
/** @var AccountValidator $validator */
$validator = app(AccountValidator::class);
$validator = app(AccountValidator::class);
$validator->setTransactionType($expectedType);
$validator->setUser($this->transactionJournal->user);
$validator->source = $this->getValidSourceAccount();
$result = $validator->validateDestination(['id' => $destId, 'name' => $destName]);
$result = $validator->validateDestination(['id' => $destId, 'name' => $destName]);
Log::debug(sprintf('hasValidDestinationAccount(%d, "%s") will return %s', $destId, $destName, var_export($result, true)));
// TODO typeOverrule: the account validator may have another opinion on the transaction type.
@@ -320,7 +319,8 @@ class JournalUpdateService
$result = $this->transactionJournal
->transactions()
->where('amount', '>', 0)
->first();
->first()
;
$this->destinationTransaction = $result;
}
@@ -338,8 +338,8 @@ class JournalUpdateService
return $this->getOriginalSourceAccount();
}
$sourceInfo = [
'id' => (int)($this->data['source_id'] ?? null),
$sourceInfo = [
'id' => (int) ($this->data['source_id'] ?? null),
'name' => $this->data['source_name'] ?? null,
'iban' => $this->data['source_iban'] ?? null,
'number' => $this->data['source_number'] ?? null,
@@ -366,8 +366,8 @@ class JournalUpdateService
*/
private function updateAccounts(): void
{
$source = $this->getValidSourceAccount();
$destination = $this->getValidDestinationAccount();
$source = $this->getValidSourceAccount();
$destination = $this->getValidDestinationAccount();
// cowardly refuse to update if both accounts are the same.
if ($source->id === $destination->id) {
@@ -380,7 +380,7 @@ class JournalUpdateService
$origSourceTransaction->account()->associate($source);
$origSourceTransaction->save();
$destTransaction = $this->getDestinationTransaction();
$destTransaction = $this->getDestinationTransaction();
$destTransaction->account()->associate($destination);
$destTransaction->save();
@@ -402,8 +402,8 @@ class JournalUpdateService
return $this->getOriginalDestinationAccount();
}
$destInfo = [
'id' => (int)($this->data['destination_id'] ?? null),
$destInfo = [
'id' => (int) ($this->data['destination_id'] ?? null),
'name' => $this->data['destination_name'] ?? null,
'iban' => $this->data['destination_iban'] ?? null,
'number' => $this->data['destination_number'] ?? null,
@@ -431,13 +431,13 @@ class JournalUpdateService
{
Log::debug('Now in updateType()');
if ($this->hasFields(['type'])) {
$type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type'];
$type = 'opening-balance' === $this->data['type'] ? 'opening balance' : $this->data['type'];
Log::debug(sprintf(
'Trying to change journal #%d from a %s to a %s.',
$this->transactionJournal->id,
$this->transactionJournal->transactionType->type,
$type
));
'Trying to change journal #%d from a %s to a %s.',
$this->transactionJournal->id,
$this->transactionJournal->transactionType->type,
$type
));
/** @var TransactionTypeFactory $typeFactory */
$typeFactory = app(TransactionTypeFactory::class);
@@ -462,8 +462,8 @@ class JournalUpdateService
{
$type = $this->transactionJournal->transactionType->type;
if ((array_key_exists('bill_id', $this->data) || array_key_exists('bill_name', $this->data)) && TransactionTypeEnum::WITHDRAWAL->value === $type) {
$billId = (int)($this->data['bill_id'] ?? 0);
$billName = (string)($this->data['bill_name'] ?? '');
$billId = (int) ($this->data['bill_id'] ?? 0);
$billName = (string) ($this->data['bill_name'] ?? '');
$bill = $this->billRepository->findBill($billId, $billName);
$this->transactionJournal->bill_id = $bill?->id;
Log::debug('Updated bill ID');
@@ -475,8 +475,8 @@ class JournalUpdateService
*/
private function updateField(string $fieldName): void
{
if (array_key_exists($fieldName, $this->data) && '' !== (string)$this->data[$fieldName]) {
$value = $this->data[$fieldName];
if (array_key_exists($fieldName, $this->data) && '' !== (string) $this->data[$fieldName]) {
$value = $this->data[$fieldName];
if ('date' === $fieldName) {
if (!$value instanceof Carbon) {
@@ -496,8 +496,8 @@ class JournalUpdateService
Log::debug(sprintf('Old date: %s, new date: %s', $this->transactionJournal->date->toW3cString(), $value->toW3cString()));
/** @var TransactionJournalMetaFactory $factory */
$factory = app(TransactionJournalMetaFactory::class);
$set = ['journal' => $this->transactionJournal, 'name' => '_internal_previous_date', 'data' => null];
$factory = app(TransactionJournalMetaFactory::class);
$set = ['journal' => $this->transactionJournal, 'name' => '_internal_previous_date', 'data' => null];
if ($res) {
Log::debug('Transaction is set to be AFTER its current date. Save also the "_internal_previous_date"-field.');
$set['data'] = clone $this->transactionJournal->date;
@@ -565,7 +565,7 @@ class JournalUpdateService
{
// update notes.
if ($this->hasFields(['notes'])) {
$notes = '' === (string)$this->data['notes'] ? null : $this->data['notes'];
$notes = '' === (string) $this->data['notes'] ? null : $this->data['notes'];
$this->storeNotes($this->transactionJournal, $notes);
}
}
@@ -595,7 +595,7 @@ class JournalUpdateService
if ($this->hasFields([$field])) {
$value = '' === $this->data[$field] ? null : $this->data[$field];
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = ['journal' => $this->transactionJournal, 'name' => $field, 'data' => $value];
$set = ['journal' => $this->transactionJournal, 'name' => $field, 'data' => $value];
$factory->updateOrCreate($set);
}
}
@@ -609,17 +609,17 @@ class JournalUpdateService
foreach ($this->metaDate as $field) {
if ($this->hasFields([$field])) {
try {
$value = '' === (string)$this->data[$field] ? null : new Carbon($this->data[$field]);
} catch (InvalidDateException | InvalidFormatException $e) { // @phpstan-ignore-line
$value = '' === (string) $this->data[$field] ? null : new Carbon($this->data[$field]);
} catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line
Log::debug(sprintf('%s is not a valid date value: %s', $this->data[$field], $e->getMessage()));
return;
}
Log::debug(sprintf('Field "%s" is present ("%s"), try to update it.', $field, $value));
$set = ['journal' => $this->transactionJournal, 'name' => $field, 'data' => $value];
$set = ['journal' => $this->transactionJournal, 'name' => $field, 'data' => $value];
$factory->updateOrCreate($set);
// also set date with timezone.
$set = ['journal' => $this->transactionJournal, 'name' => sprintf('%s_tz', $field), 'data' => $value?->format('e')];
$set = ['journal' => $this->transactionJournal, 'name' => sprintf('%s_tz', $field), 'data' => $value?->format('e')];
$factory->updateOrCreate($set);
}
}
@@ -631,19 +631,19 @@ class JournalUpdateService
if (!$this->hasFields(['currency_id', 'currency_code'])) {
return;
}
$currencyId = $this->data['currency_id'] ?? null;
$currencyCode = $this->data['currency_code'] ?? null;
$currency = $this->currencyRepository->findCurrency($currencyId, $currencyCode);
$currencyId = $this->data['currency_id'] ?? null;
$currencyCode = $this->data['currency_code'] ?? null;
$currency = $this->currencyRepository->findCurrency($currencyId, $currencyCode);
// update currency everywhere.
$this->transactionJournal->transaction_currency_id = $currency->id;
$this->transactionJournal->save();
$source = $this->getSourceTransaction();
$source->transaction_currency_id = $currency->id;
$source = $this->getSourceTransaction();
$source->transaction_currency_id = $currency->id;
$source->save();
$dest = $this->getDestinationTransaction();
$dest->transaction_currency_id = $currency->id;
$dest = $this->getDestinationTransaction();
$dest->transaction_currency_id = $currency->id;
$dest->save();
// refresh transactions.
@@ -659,7 +659,7 @@ class JournalUpdateService
return;
}
$value = $this->data['amount'] ?? '';
$value = $this->data['amount'] ?? '';
Log::debug(sprintf('[a] Amount is now "%s"', $value));
try {
@@ -670,9 +670,9 @@ class JournalUpdateService
return;
}
Log::debug(sprintf('[b] Amount is now "%s"', $value));
$origSourceTransaction = $this->getSourceTransaction();
$destTransaction = $this->getDestinationTransaction();
$originalSourceAmount = $origSourceTransaction->amount;
$origSourceTransaction = $this->getSourceTransaction();
$destTransaction = $this->getDestinationTransaction();
$originalSourceAmount = $origSourceTransaction->amount;
// $originalDestAmount = $destTransaction->amount;
$origSourceTransaction->amount = Steam::negative($amount);
$origSourceTransaction->balance_dirty = true;
@@ -686,7 +686,7 @@ class JournalUpdateService
$this->destinationTransaction->refresh();
Log::debug(sprintf('Updated amount to "%s"', $amount));
$group = $this->transactionGroup;
$group = $this->transactionGroup;
if (null === $group) {
$group = $this->transactionJournal?->transactionGroup;
}
@@ -699,13 +699,13 @@ class JournalUpdateService
return;
}
Log::debug('Amount was changed, needs audit log entry.');
$transfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
$transfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
// $withdrawal = TransactionTypeEnum::WITHDRAWAL->value === $this->transactionJournal->transactionType->type;
$deposit = TransactionTypeEnum::DEPOSIT->value === $this->transactionJournal->transactionType->type;
$makePositive = $transfer || $deposit ? true : false;
$recordCurrency = $origSourceTransaction->transactionCurrency;
$originalSourceAmount = $makePositive ? Steam::positive($originalSourceAmount) : Steam::negative($originalSourceAmount);
$value = $makePositive ? Steam::positive($value) : Steam::negative($value);
$deposit = TransactionTypeEnum::DEPOSIT->value === $this->transactionJournal->transactionType->type;
$makePositive = $transfer || $deposit ? true : false;
$recordCurrency = $origSourceTransaction->transactionCurrency;
$originalSourceAmount = $makePositive ? Steam::positive($originalSourceAmount) : Steam::negative($originalSourceAmount);
$value = $makePositive ? Steam::positive($value) : Steam::negative($value);
// should not return in NULL but seems to do.
event(
@@ -718,7 +718,7 @@ class JournalUpdateService
'decimal_places' => $recordCurrency->decimal_places,
'amount' => $originalSourceAmount,
],
['currency_symbol' => $recordCurrency->symbol, 'decimal_places' => $recordCurrency->decimal_places, 'amount' => $value]
['currency_symbol' => $recordCurrency->symbol, 'decimal_places' => $recordCurrency->decimal_places, 'amount' => $value]
)
);
}
@@ -738,9 +738,9 @@ class JournalUpdateService
$originalSourceAmount = $source->foreign_amount;
// find currency in data array
$newForeignId = $this->data['foreign_currency_id'] ?? null;
$newForeignCode = $this->data['foreign_currency_code'] ?? null;
$foreignCurrency = $this->currencyRepository->findCurrencyNull($newForeignId, $newForeignCode) ?? $foreignCurrency;
$newForeignId = $this->data['foreign_currency_id'] ?? null;
$newForeignCode = $this->data['foreign_currency_code'] ?? null;
$foreignCurrency = $this->currencyRepository->findCurrencyNull($newForeignId, $newForeignCode) ?? $foreignCurrency;
// not the same as normal currency
if (null !== $foreignCurrency && $foreignCurrency->id === $this->transactionJournal->transaction_currency_id) {
@@ -758,9 +758,9 @@ class JournalUpdateService
// if the transaction is a TRANSFER, and the foreign amount and currency are set (like they seem to be)
// the correct fields to update in the destination transaction are NOT the foreign amount and currency
// but rather the normal amount and currency. This is new behavior.
$isTransfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
$isTransfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
// also check if it is not between an asset account and a liability, because then the same rule applies.
$isBetween = $this->isBetweenAssetAndLiability();
$isBetween = $this->isBetweenAssetAndLiability();
if ($isTransfer || $isBetween) {
Log::debug('Switch amounts, store in amount and not foreign_amount');
@@ -785,7 +785,7 @@ class JournalUpdateService
// add audit log entry.
Log::debug(sprintf('Updated foreign amount to "%s"', $foreignAmount));
$group = $this->transactionGroup;
$group = $this->transactionGroup;
if (null === $group) {
$group = $this->transactionJournal?->transactionGroup;
}
@@ -798,13 +798,13 @@ class JournalUpdateService
return;
}
Log::debug('Amount was changed, needs audit log entry.');
$transfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
$transfer = TransactionTypeEnum::TRANSFER->value === $this->transactionJournal->transactionType->type;
// $withdrawal = TransactionTypeEnum::WITHDRAWAL->value === $this->transactionJournal->transactionType->type;
$deposit = TransactionTypeEnum::DEPOSIT->value === $this->transactionJournal->transactionType->type;
$makePositive = $transfer || $deposit ? true : false;
$recordCurrency = $source->foreignCurrency;
$originalSourceAmount = $makePositive ? Steam::positive($originalSourceAmount) : Steam::negative($originalSourceAmount);
$value = $makePositive ? Steam::positive($foreignAmount) : Steam::negative($foreignAmount);
$deposit = TransactionTypeEnum::DEPOSIT->value === $this->transactionJournal->transactionType->type;
$makePositive = $transfer || $deposit ? true : false;
$recordCurrency = $source->foreignCurrency;
$originalSourceAmount = $makePositive ? Steam::positive($originalSourceAmount) : Steam::negative($originalSourceAmount);
$value = $makePositive ? Steam::positive($foreignAmount) : Steam::negative($foreignAmount);
// should not return in NULL but seems to do.
event(
@@ -817,7 +817,7 @@ class JournalUpdateService
'decimal_places' => $recordCurrency->decimal_places,
'amount' => $originalSourceAmount,
],
['currency_symbol' => $recordCurrency->symbol, 'decimal_places' => $recordCurrency->decimal_places, 'amount' => $value]
['currency_symbol' => $recordCurrency->symbol, 'decimal_places' => $recordCurrency->decimal_places, 'amount' => $value]
)
);
}
@@ -826,8 +826,8 @@ class JournalUpdateService
$source->foreign_amount = null;
$source->save();
$dest->foreign_currency_id = null;
$dest->foreign_amount = null;
$dest->foreign_currency_id = null;
$dest->foreign_amount = null;
$dest->save();
Log::debug(sprintf('Foreign amount is "%s" so remove foreign amount info.', $amount));
@@ -843,16 +843,18 @@ class JournalUpdateService
private function isBetweenAssetAndLiability(): bool
{
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $this->transactionJournal
$sourceTransaction = $this->transactionJournal
->transactions()
->where('amount', '<', 0)
->first();
->first()
;
/** @var null|Transaction $destinationTransaction */
$destinationTransaction = $this->transactionJournal
->transactions()
->where('amount', '>', 0)
->first();
->first()
;
if (null === $sourceTransaction || null === $destinationTransaction) {
Log::warning('Either transaction is false, stop.');
@@ -864,15 +866,15 @@ class JournalUpdateService
return false;
}
$source = $sourceTransaction->account;
$destination = $destinationTransaction->account;
$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];
$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) {

File diff suppressed because it is too large Load Diff

View File

@@ -52,12 +52,12 @@ class AddTag implements ActionInterface
$factory = app(TagFactory::class);
/** @var User $user */
$user = User::find($journal['user_id']);
$user = User::find($journal['user_id']);
$factory->setUser($user);
$tagName = $this->action->getValue($journal);
$tag = $factory->findOrCreate($tagName);
$type = $journal['transaction_type_type'];
$type = $journal['transaction_type_type'];
if (
TransactionTypeEnum::OPENING_BALANCE->value === $type
|| TransactionTypeEnum::LIABILITY_CREDIT->value === $type
@@ -75,16 +75,17 @@ class AddTag implements ActionInterface
return false;
}
$count = DB::table('tag_transaction_journal')
->where('tag_id', $tag->id)
->where('transaction_journal_id', $journal['transaction_journal_id'])
->count();
$count = DB::table('tag_transaction_journal')
->where('tag_id', $tag->id)
->where('transaction_journal_id', $journal['transaction_journal_id'])
->count()
;
if (0 === $count) {
// add to journal:
DB::table('tag_transaction_journal')->insert([
'tag_id' => $tag->id,
'transaction_journal_id' => $journal['transaction_journal_id'],
]);
'tag_id' => $tag->id,
'transaction_journal_id' => $journal['transaction_journal_id'],
]);
Log::debug(sprintf('RuleAction AddTag. Added tag #%d ("%s") to journal %d.', $tag->id, $tag->tag, $journal['transaction_journal_id']));
/** @var TransactionJournal $object */
@@ -96,11 +97,11 @@ class AddTag implements ActionInterface
return true;
}
Log::debug(sprintf(
'RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.',
$tag->id,
$tag->tag,
$journal['transaction_journal_id']
));
'RuleAction AddTag fired but tag %d ("%s") was already added to journal %d.',
$tag->id,
$tag->tag,
$journal['transaction_journal_id']
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.tag_already_added', ['tag' => $tagName])));
return false;

View File

@@ -50,13 +50,14 @@ class AppendDescription implements ActionInterface
$append = $this->action->getValue($journal);
$description = sprintf('%s %s', $journal['description'], $append);
DB::table('transaction_journals')
->where('id', $journal['transaction_journal_id'])
->limit(1)
->update(['description' => $description]);
->where('id', $journal['transaction_journal_id'])
->limit(1)
->update(['description' => $description])
;
// event for audit log entry
/** @var TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'update_description', $journal['description'], $description));
return true;

View File

@@ -55,13 +55,13 @@ class AppendDescriptionToNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, (string)trans('rules.journal_other_user')));
event(new RuleActionFailedOnArray($this->action, $journal, (string) trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();
$note = $object->notes()->first();
if (null === $note) {
$note = new Note();
$note = new Note();
$note->noteable()->associate($object);
$note->text = '';
}
@@ -70,9 +70,9 @@ class AppendDescriptionToNotes implements ActionInterface
$note->text = trim(sprintf("%s \n%s", $note->text, $object->description));
}
if ('' === $note->text) {
$note->text = (string)$object->description;
$note->text = (string) $object->description;
}
$after = $note->text;
$after = $note->text;
// event for audit log entry
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'update_notes', $before, $after));

View File

@@ -48,10 +48,10 @@ class AppendNotes implements ActionInterface
public function actOnArray(array $journal): bool
{
$this->refreshNotes($journal);
$dbNote = Note::where('noteable_id', (int)$journal['transaction_journal_id'])->where('noteable_type', TransactionJournal::class)->first(['notes.*']);
$dbNote = Note::where('noteable_id', (int) $journal['transaction_journal_id'])->where('noteable_type', TransactionJournal::class)->first(['notes.*']);
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable_id = (int)$journal['transaction_journal_id'];
$dbNote->noteable_id = (int) $journal['transaction_journal_id'];
$dbNote->noteable_type = TransactionJournal::class;
$dbNote->text = '';
}
@@ -62,7 +62,7 @@ class AppendNotes implements ActionInterface
$dbNote->save();
/** @var TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
Log::debug(sprintf('RuleAction AppendNotes appended "%s" to "%s".', $append, $before));
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'update_notes', $before, $text));

View File

@@ -62,17 +62,17 @@ class AppendNotesToDescription implements ActionInterface
return false;
}
$note = $object->notes()->first();
$note = $object->notes()->first();
if (null === $note) {
Log::debug('Journal has no notes.');
$note = new Note();
$note = new Note();
$note->noteable()->associate($object);
$note->text = '';
}
// only append if there is something to append
if ('' !== $note->text) {
$before = $object->description;
$object->description = trim(sprintf('%s %s', $object->description, (string)$this->clearString($note->text)));
$object->description = trim(sprintf('%s %s', $object->description, (string) $this->clearString($note->text)));
$object->save();
Log::debug(sprintf('Journal description is updated to "%s".', $object->description));

View File

@@ -49,7 +49,7 @@ class ClearNotes implements ActionInterface
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
/** @var null|Note $notes */
$notes = $object->notes()->first();
$notes = $object->notes()->first();
if (null === $notes) {
Log::debug(sprintf('RuleAction ClearNotes, journal #%d has no notes.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_already_no_notes')));
@@ -59,9 +59,10 @@ class ClearNotes implements ActionInterface
$before = $notes->text;
DB::table('notes')
->where('noteable_id', $journal['transaction_journal_id'])
->where('noteable_type', TransactionJournal::class)
->delete();
->where('noteable_id', $journal['transaction_journal_id'])
->where('noteable_type', TransactionJournal::class)
->delete()
;
Log::debug(sprintf('RuleAction ClearNotes removed all notes from journal #%d.', $journal['transaction_journal_id']));
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'clear_notes', $before, null));

View File

@@ -57,14 +57,14 @@ class ConvertToDeposit implements ActionInterface
// make object from array (so the data is fresh).
/** @var null|TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to deposit.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to deposit.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
@@ -73,7 +73,7 @@ class ConvertToDeposit implements ActionInterface
}
Log::debug(sprintf('Convert journal #%d to deposit.', $journal['transaction_journal_id']));
$type = $object->transactionType->type;
$type = $object->transactionType->type;
if (TransactionTypeEnum::DEPOSIT->value === $type) {
Log::error(sprintf('Journal #%d is already a deposit (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_deposit')));
@@ -143,22 +143,22 @@ class ConvertToDeposit implements ActionInterface
*/
private function convertWithdrawalArray(TransactionJournal $journal, string $actionValue = ''): bool
{
$user = $journal->user;
$user = $journal->user;
// find or create revenue account.
/** @var AccountFactory $factory */
$factory = app(AccountFactory::class);
$factory = app(AccountFactory::class);
$factory->setUser($user);
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$destAccount = $this->getDestinationAccount($journal);
$sourceAccount = $this->getSourceAccount($journal);
$destAccount = $this->getDestinationAccount($journal);
$sourceAccount = $this->getSourceAccount($journal);
// get the action value, or use the original destination name in case the action value is empty:
// this becomes a new or existing (revenue) account, which is the source of the new deposit.
$opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
$opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
// we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.source.Deposit');
$opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -170,20 +170,22 @@ class ConvertToDeposit implements ActionInterface
// update the source transaction and put in the new revenue ID.
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposingAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposingAccount->id])
;
// update the destination transaction and put in the original source account ID.
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $sourceAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $sourceAccount->id])
;
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::DEPOSIT->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::DEPOSIT->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
Log::debug('Converted withdrawal to deposit.');
@@ -227,21 +229,21 @@ class ConvertToDeposit implements ActionInterface
*/
private function convertTransferArray(TransactionJournal $journal, string $actionValue = ''): bool
{
$user = $journal->user;
$user = $journal->user;
// find or create revenue account.
/** @var AccountFactory $factory */
$factory = app(AccountFactory::class);
$factory = app(AccountFactory::class);
$factory->setUser($user);
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$sourceAccount = $this->getSourceAccount($journal);
$sourceAccount = $this->getSourceAccount($journal);
// get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (revenue) account, which is the source of the new deposit.
$opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
$opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
// we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.source.Deposit');
$opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -253,14 +255,15 @@ class ConvertToDeposit implements ActionInterface
// update source transaction(s) to be revenue account
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposingAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposingAccount->id])
;
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::DEPOSIT->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::DEPOSIT->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
Log::debug('Converted transfer to deposit.');

View File

@@ -58,18 +58,18 @@ class ConvertToTransfer implements ActionInterface
*/
public function actOnArray(array $journal): bool
{
$accountName = $this->action->getValue($journal);
$accountName = $this->action->getValue($journal);
// make object from array (so the data is fresh).
/** @var null|TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to transfer.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
@@ -77,9 +77,9 @@ class ConvertToTransfer implements ActionInterface
return false;
}
$type = $object->transactionType->type;
$user = $object->user;
$journalId = $object->id;
$type = $object->transactionType->type;
$user = $object->user;
$journalId = $object->id;
if (TransactionTypeEnum::TRANSFER->value === $type) {
Log::error(sprintf('Journal #%d is already a transfer so cannot be converted (rule #%d).', $object->id, $this->action->rule_id));
// event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_transfer')));
@@ -94,7 +94,7 @@ class ConvertToTransfer implements ActionInterface
// find the asset account in the action value.
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$expectedType = null;
if (TransactionTypeEnum::WITHDRAWAL->value === $type) {
@@ -107,16 +107,16 @@ class ConvertToTransfer implements ActionInterface
// Deposit? Replace source with account with same type as destination.
}
$opposing = $repository->findByName($accountName, [$expectedType]);
$opposing = $repository->findByName($accountName, [$expectedType]);
if (null === $opposing) {
Log::error(sprintf(
'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).',
$expectedType,
$journalId,
$accountName,
$this->action->rule_id
));
'Journal #%d cannot be converted because no valid %s account with name "%s" exists (rule #%d).',
$expectedType,
$journalId,
$accountName,
$this->action->rule_id
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_valid_opposing', ['name' => $accountName])));
return false;
@@ -185,7 +185,7 @@ class ConvertToTransfer implements ActionInterface
return '';
}
return (string)$journal->transactions()->where('amount', '<', 0)->first()?->account?->accountType?->type;
return (string) $journal->transactions()->where('amount', '<', 0)->first()?->account?->accountType?->type;
}
private function getDestinationType(int $journalId): string
@@ -198,7 +198,7 @@ class ConvertToTransfer implements ActionInterface
return '';
}
return (string)$journal->transactions()->where('amount', '>', 0)->first()?->account?->accountType?->type;
return (string) $journal->transactions()->where('amount', '>', 0)->first()?->account?->accountType?->type;
}
/**
@@ -210,8 +210,8 @@ class ConvertToTransfer implements ActionInterface
*/
private function convertWithdrawalArray(TransactionJournal $journal, Account $opposing): bool
{
$repository = app(AccountRepositoryInterface::class);
$sourceAccount = $this->getSourceAccount($journal);
$repository = app(AccountRepositoryInterface::class);
$sourceAccount = $this->getSourceAccount($journal);
$repository->setUser($sourceAccount->user);
if ($sourceAccount->id === $opposing->id) {
Log::error(vsprintf('Journal #%d has already has "%s" as a source asset. ConvertToTransfer failed. (rule #%d).', [
@@ -225,37 +225,37 @@ class ConvertToTransfer implements ActionInterface
}
/** @var Transaction $sourceTransaction */
$sourceTransaction = Transaction::where('transaction_journal_id', '=', $journal->id)->where('amount', '<', 0)->first();
$sourceTransaction = Transaction::where('transaction_journal_id', '=', $journal->id)->where('amount', '<', 0)->first();
/** @var Transaction $destTransaction */
$destTransaction = Transaction::where('transaction_journal_id', '=', $journal->id)->where('amount', '>', 0)->first();
$destTransaction = Transaction::where('transaction_journal_id', '=', $journal->id)->where('amount', '>', 0)->first();
// update destination transaction:
$destTransaction->account_id = $opposing->id;
$destTransaction->save();
// check if the currencies are a match.
/** @var TransactionCurrency $sourceCurrency */
$sourceCurrency = $repository->getAccountCurrency($sourceAccount);
$sourceCurrency = $repository->getAccountCurrency($sourceAccount);
/** @var TransactionCurrency $destCurrency */
$destCurrency = $repository->getAccountCurrency($opposing);
$destCurrency = $repository->getAccountCurrency($opposing);
// if the currencies do not match, need to be smart about the involved amounts:
if ($sourceCurrency->id !== $destCurrency->id) {
Log::debug(sprintf('Accounts have different currencies. Source has %s, dest has %s', $sourceCurrency->code, $destCurrency->code));
$foreignAmount = '' === (string)$sourceTransaction->foreign_amount ? $sourceTransaction->amount : $sourceTransaction->foreign_amount;
$foreignAmount = '' === (string) $sourceTransaction->foreign_amount ? $sourceTransaction->amount : $sourceTransaction->foreign_amount;
Log::debug(sprintf('Foreign amount: %s', $foreignAmount));
// source transaction: set the foreign currency ID and leave as is.
$sourceTransaction->foreign_currency_id = $destCurrency->id;
$sourceTransaction->foreign_amount = Steam::negative($foreignAmount);
$sourceTransaction->foreign_currency_id = $destCurrency->id;
$sourceTransaction->foreign_amount = Steam::negative($foreignAmount);
$sourceTransaction->save();
Log::debug(sprintf(
'Set source transaction #%d foreign currency ID to #%d (amount: %s)',
$sourceTransaction->id,
$destCurrency->id,
$foreignAmount
));
'Set source transaction #%d foreign currency ID to #%d (amount: %s)',
$sourceTransaction->id,
$destCurrency->id,
$foreignAmount
));
// dest transaction: set reverse amounts and currency IDs from source transaction.
$destTransaction->foreign_currency_id = $sourceCurrency->transaction_currency_id;
@@ -264,19 +264,19 @@ class ConvertToTransfer implements ActionInterface
$destTransaction->foreign_amount = Steam::positive($sourceTransaction->amount);
$destTransaction->save();
Log::debug(sprintf(
'Set dest transaction #%d to #%d %s and foreign #%d %s',
$destTransaction->id,
$destTransaction->transaction_currency_id,
$destTransaction->amount,
$destTransaction->foreign_currency_id,
$destTransaction->foreign_amount
));
'Set dest transaction #%d to #%d %s and foreign #%d %s',
$destTransaction->id,
$destTransaction->transaction_currency_id,
$destTransaction->amount,
$destTransaction->foreign_currency_id,
$destTransaction->foreign_amount
));
}
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::TRANSFER->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::TRANSFER->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
Log::debug('Converted withdrawal to transfer.');
@@ -319,14 +319,15 @@ class ConvertToTransfer implements ActionInterface
// update source transaction:
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposing->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $opposing->id])
;
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::TRANSFER->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::TRANSFER->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id, 'bill_id' => null]);
Log::debug('Converted deposit to transfer.');

View File

@@ -28,7 +28,6 @@ use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\Account;
@@ -58,14 +57,14 @@ class ConvertToWithdrawal implements ActionInterface
// make object from array (so the data is fresh).
/** @var null|TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('Cannot find journal #%d, cannot convert to withdrawal.', $journal['transaction_journal_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_not_found')));
return false;
}
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
$groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
if ($groupCount > 1) {
Log::error(sprintf('Group #%d has more than one transaction in it, cannot convert to withdrawal.', $journal['transaction_group_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group')));
@@ -73,7 +72,7 @@ class ConvertToWithdrawal implements ActionInterface
return false;
}
$type = $object->transactionType->type;
$type = $object->transactionType->type;
if (TransactionTypeEnum::WITHDRAWAL->value === $type) {
Log::error(sprintf('Journal #%d is already a withdrawal (rule #%d).', $journal['transaction_journal_id'], $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.is_already_withdrawal')));
@@ -139,21 +138,21 @@ class ConvertToWithdrawal implements ActionInterface
*/
private function convertDepositArray(TransactionJournal $journal, string $actionValue = ''): bool
{
$user = $journal->user;
$user = $journal->user;
/** @var AccountFactory $factory */
$factory = app(AccountFactory::class);
$factory = app(AccountFactory::class);
$factory->setUser($user);
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$sourceAccount = $this->getSourceAccount($journal);
$destAccount = $this->getDestinationAccount($journal);
$sourceAccount = $this->getSourceAccount($journal);
$destAccount = $this->getDestinationAccount($journal);
// get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (expense) account, which is the destination of the new withdrawal.
$opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
$opposingName = '' === $actionValue ? $sourceAccount->name : $actionValue;
// we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.destination.Withdrawal');
$opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -165,18 +164,20 @@ class ConvertToWithdrawal implements ActionInterface
// update source transaction(s) to be the original destination account
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $destAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '<', 0)
->update(['account_id' => $destAccount->id])
;
// update destination transaction(s) to be new expense account.
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $opposingAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $opposingAccount->id])
;
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::WITHDRAWAL->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::WITHDRAWAL->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id]);
Log::debug('Converted deposit to withdrawal.');
@@ -221,20 +222,20 @@ class ConvertToWithdrawal implements ActionInterface
private function convertTransferArray(TransactionJournal $journal, string $actionValue = ''): bool
{
// find or create expense account.
$user = $journal->user;
$user = $journal->user;
/** @var AccountFactory $factory */
$factory = app(AccountFactory::class);
$factory = app(AccountFactory::class);
$factory->setUser($user);
$repository = app(AccountRepositoryInterface::class);
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($user);
$destAccount = $this->getDestinationAccount($journal);
$destAccount = $this->getDestinationAccount($journal);
// get the action value, or use the original source name in case the action value is empty:
// this becomes a new or existing (expense) account, which is the destination of the new withdrawal.
$opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
$opposingName = '' === $actionValue ? $destAccount->name : $actionValue;
// we check all possible source account types if one exists:
$validTypes = config('firefly.expected_source_types.destination.Withdrawal');
$opposingAccount = $repository->findByName($opposingName, $validTypes);
@@ -246,12 +247,13 @@ class ConvertToWithdrawal implements ActionInterface
// update destination transaction(s) to be new expense account.
DB::table('transactions')
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $opposingAccount->id]);
->where('transaction_journal_id', '=', $journal->id)
->where('amount', '>', 0)
->update(['account_id' => $opposingAccount->id])
;
// change transaction type of journal:
$newType = TransactionType::whereType(TransactionTypeEnum::WITHDRAWAL->value)->first();
$newType = TransactionType::whereType(TransactionTypeEnum::WITHDRAWAL->value)->first();
DB::table('transaction_journals')->where('id', '=', $journal->id)->update(['transaction_type_id' => $newType->id]);
Log::debug('Converted transfer to withdrawal.');

View File

@@ -45,15 +45,15 @@ class DeleteTransaction implements ActionInterface
public function actOnArray(array $journal): bool
{
$count = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
$count = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count();
// destroy entire group.
if (1 === $count) {
Log::debug(sprintf(
'RuleAction DeleteTransaction DELETED the entire transaction group of journal #%d ("%s").',
$journal['transaction_journal_id'],
$journal['description']
));
'RuleAction DeleteTransaction DELETED the entire transaction group of journal #%d ("%s").',
$journal['transaction_journal_id'],
$journal['description']
));
/** @var TransactionGroup $group */
$group = TransactionGroup::find($journal['transaction_group_id']);
@@ -65,10 +65,10 @@ class DeleteTransaction implements ActionInterface
return true;
}
Log::debug(sprintf(
'RuleAction DeleteTransaction DELETED transaction journal #%d ("%s").',
$journal['transaction_journal_id'],
$journal['description']
));
'RuleAction DeleteTransaction DELETED transaction journal #%d ("%s").',
$journal['transaction_journal_id'],
$journal['description']
));
// trigger delete factory:
/** @var null|TransactionJournal $object */

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;

View File

@@ -47,7 +47,7 @@ class MoveDescriptionToNotes implements ActionInterface
public function actOnArray(array $journal): bool
{
/** @var null|TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
@@ -56,9 +56,9 @@ class MoveDescriptionToNotes implements ActionInterface
}
/** @var null|Note $note */
$note = $object->notes()->first();
$note = $object->notes()->first();
if (null === $note) {
$note = new Note();
$note = new Note();
$note->noteable()->associate($object);
$note->text = '';
}
@@ -69,10 +69,10 @@ class MoveDescriptionToNotes implements ActionInterface
$object->description = '(no description)';
}
if ('' === $note->text) {
$note->text = (string)$object->description;
$note->text = (string) $object->description;
$object->description = '(no description)';
}
$after = $note->text;
$after = $note->text;
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'update_description', $beforeDescription, $object->description));
event(new TransactionGroupRequestsAuditLogEntry($this->action->rule, $object, 'update_notes', $before, $after));

View File

@@ -53,14 +53,14 @@ class MoveNotesToDescription implements ActionInterface
public function actOnArray(array $journal): bool
{
/** @var null|TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);
if (null === $object) {
Log::error(sprintf('No journal #%d belongs to user #%d.', $journal['transaction_journal_id'], $journal['user_id']));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_other_user')));
return false;
}
$note = $object->notes()->first();
$note = $object->notes()->first();
if (null === $note) {
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_notes_to_move')));
@@ -76,7 +76,7 @@ class MoveNotesToDescription implements ActionInterface
}
$before = $object->description;
$beforeNote = $note->text;
$object->description = (string)$this->clearString($note->text);
$object->description = (string) $this->clearString($note->text);
$object->save();
$note->delete();

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Facades\DB;

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Facades\DB;

View File

@@ -45,33 +45,34 @@ class RemoveTag implements ActionInterface
public function actOnArray(array $journal): bool
{
$name = $this->action->getValue($journal);
$name = $this->action->getValue($journal);
/** @var User $user */
$user = User::find($journal['user_id']);
$tag = $user->tags()->where('tag', $name)->first();
$user = User::find($journal['user_id']);
$tag = $user->tags()->where('tag', $name)->first();
// if tag does not exist, no need to continue:
if (null === $tag) {
Log::debug(sprintf(
'RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.',
$name,
$journal['transaction_journal_id']
));
'RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag exists.',
$name,
$journal['transaction_journal_id']
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_tag', ['tag' => $name])));
return false;
}
$count = DB::table('tag_transaction_journal')
->where('transaction_journal_id', $journal['transaction_journal_id'])
->where('tag_id', $tag->id)
->count();
$count = DB::table('tag_transaction_journal')
->where('transaction_journal_id', $journal['transaction_journal_id'])
->where('tag_id', $tag->id)
->count()
;
if (0 === $count) {
Log::debug(sprintf(
'RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.',
$name,
$journal['transaction_journal_id']
));
'RuleAction RemoveTag tried to remove tag "%s" from journal #%d but no such tag is linked.',
$name,
$journal['transaction_journal_id']
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_unlink_tag', ['tag' => $name])));
return false;
@@ -79,9 +80,10 @@ class RemoveTag implements ActionInterface
Log::debug(sprintf('RuleAction RemoveTag removed tag #%d ("%s") from journal #%d.', $tag->id, $tag->tag, $journal['transaction_journal_id']));
DB::table('tag_transaction_journal')
->where('transaction_journal_id', $journal['transaction_journal_id'])
->where('tag_id', $tag->id)
->delete();
->where('transaction_journal_id', $journal['transaction_journal_id'])
->where('tag_id', $tag->id)
->delete()
;
/** @var TransactionJournal $object */
$object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']);

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\User;

View File

@@ -25,7 +25,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\TransactionRules\Traits\RefreshNotesTrait;

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Note;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\Account;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -27,7 +27,6 @@ namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupRequestsAuditLogEntry;
use FireflyIII\Events\TriggeredAuditLog;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;

View File

@@ -54,12 +54,12 @@ class UpdatePiggyBank implements ActionInterface
// refresh the transaction type.
/** @var User $user */
$user = User::find($journal['user_id']);
$user = User::find($journal['user_id']);
/** @var TransactionJournal $journalObj */
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$journalObj = $user->transactionJournals()->find($journal['transaction_journal_id']);
$piggyBank = $this->findPiggyBank($user, $actionValue);
$piggyBank = $this->findPiggyBank($user, $actionValue);
if (!$piggyBank instanceof PiggyBank) {
Log::info(sprintf('No piggy bank named "%s", cant execute action #%d of rule #%d', $actionValue, $this->action->id, $this->action->rule_id));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue])));
@@ -72,11 +72,11 @@ class UpdatePiggyBank implements ActionInterface
// piggy bank already has an event for this transaction journal?
if ($this->alreadyEventPresent($piggyBank, $journal)) {
Log::info(sprintf(
'Piggy bank #%d ("%s") already has an event for transaction journal #%d, so no action will be taken.',
$piggyBank->id,
$piggyBank->name,
$journalObj->id
));
'Piggy bank #%d ("%s") already has an event for transaction journal #%d, so no action will be taken.',
$piggyBank->id,
$piggyBank->name,
$journalObj->id
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.cannot_find_piggy', ['name' => $actionValue])));
@@ -86,7 +86,7 @@ class UpdatePiggyBank implements ActionInterface
/** @var Transaction $destination */
$destination = $journalObj->transactions()->where('amount', '>', 0)->first();
$accounts = $this->getAccounts($journalObj);
$accounts = $this->getAccounts($journalObj);
Log::debug(sprintf('Source account is #%d: "%s"', $accounts['source']->id, $accounts['source']->name));
Log::debug(sprintf('Destination account is #%d: "%s"', $accounts['destination']->id, $accounts['source']->name));
@@ -121,19 +121,19 @@ class UpdatePiggyBank implements ActionInterface
}
if ($this->isConnected($piggyBank, $accounts['source']) && $this->isConnected($piggyBank, $accounts['destination'])) {
Log::info(sprintf(
'Piggy bank is linked to BOTH source ("#%d") and destination ("#%d"), so no action will be taken.',
$accounts['source']->id,
$accounts['destination']->id
));
'Piggy bank is linked to BOTH source ("#%d") and destination ("#%d"), so no action will be taken.',
$accounts['source']->id,
$accounts['destination']->id
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $actionValue])));
return false;
}
Log::info(sprintf(
'Piggy bank is not linked to source ("#%d") or destination ("#%d"), so no action will be taken.',
$accounts['source']->id,
$accounts['destination']->id
));
'Piggy bank is not linked to source ("#%d") or destination ("#%d"), so no action will be taken.',
$accounts['source']->id,
$accounts['destination']->id
));
event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.no_link_piggy', ['name' => $actionValue])));
return false;
@@ -179,11 +179,11 @@ class UpdatePiggyBank implements ActionInterface
$accountRepository->setUser($account->user);
// how much can we remove from this piggy bank?
$toRemove = $repository->getCurrentAmount($piggyBank, $account);
$toRemove = $repository->getCurrentAmount($piggyBank, $account);
Log::debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove));
// if $amount is bigger than $toRemove, shrink it.
$amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove;
$amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove;
Log::debug(sprintf('Amount is now %s', $amount));
// if amount is zero, stop.
@@ -218,7 +218,7 @@ class UpdatePiggyBank implements ActionInterface
// how much can we add to the piggy bank?
if (0 !== bccomp($piggyBank->target_amount, '0')) {
$toAdd = bcsub($piggyBank->target_amount, $repository->getCurrentAmount($piggyBank, $account));
$toAdd = bcsub($piggyBank->target_amount, $repository->getCurrentAmount($piggyBank, $account));
Log::debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount));
// update amount to fit:

32
composer.lock generated
View File

@@ -9840,16 +9840,16 @@
},
{
"name": "twig/twig",
"version": "v3.22.2",
"version": "v3.23.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2"
"reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/946ddeafa3c9f4ce279d1f34051af041db0e16f2",
"reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9",
"reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9",
"shasum": ""
},
"require": {
@@ -9903,7 +9903,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.22.2"
"source": "https://github.com/twigphp/Twig/tree/v3.23.0"
},
"funding": [
{
@@ -9915,7 +9915,7 @@
"type": "tidelift"
}
],
"time": "2025-12-14T11:28:47+00:00"
"time": "2026-01-23T21:00:41+00:00"
},
{
"name": "vlucas/phpdotenv",
@@ -11303,11 +11303,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.1.36",
"version": "2.1.37",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/2132e5e2361d11d40af4c17faa16f043269a4cf3",
"reference": "2132e5e2361d11d40af4c17faa16f043269a4cf3",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/28cd424c5ea984128c95cfa7ea658808e8954e49",
"reference": "28cd424c5ea984128c95cfa7ea658808e8954e49",
"shasum": ""
},
"require": {
@@ -11352,7 +11352,7 @@
"type": "github"
}
],
"time": "2026-01-21T13:58:26+00:00"
"time": "2026-01-24T08:21:55+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@@ -12019,16 +12019,16 @@
},
{
"name": "sebastian/comparator",
"version": "7.1.3",
"version": "7.1.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148"
"reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/dc904b4bb3ab070865fa4068cd84f3da8b945148",
"reference": "dc904b4bb3ab070865fa4068cd84f3da8b945148",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6",
"reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6",
"shasum": ""
},
"require": {
@@ -12087,7 +12087,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/7.1.3"
"source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4"
},
"funding": [
{
@@ -12107,7 +12107,7 @@
"type": "tidelift"
}
],
"time": "2025-08-20T11:27:00+00:00"
"time": "2026-01-24T09:28:48+00:00"
},
{
"name": "sebastian/complexity",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2026-01-23',
'build_time' => 1769198887,
'version' => 'develop/2026-01-24',
'build_time' => 1769259315,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.