mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-05-07 14:43:38 +00:00
Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop
# Conflicts: # app/Providers/EventServiceProvider.php
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* WebhookMessagesRequestSending.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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.');
|
||||
|
||||
|
||||
@@ -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.');
|
||||
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
32
composer.lock
generated
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user