mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-02-05 03:49:04 +00:00
Compare commits
83 Commits
develop-20
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35f611b3f2 | ||
|
|
e5d394533c | ||
|
|
831d39a41e | ||
|
|
2920a9b9e3 | ||
|
|
5c8204e963 | ||
|
|
d25283f193 | ||
|
|
20986e6426 | ||
|
|
9cd0ebe37e | ||
|
|
9c2b83a971 | ||
|
|
e1d32da409 | ||
|
|
c51df8cd83 | ||
|
|
9f016aed16 | ||
|
|
27df5ea800 | ||
|
|
2d7cdd36f0 | ||
|
|
7888023c1a | ||
|
|
3032118788 | ||
|
|
89d96ddc17 | ||
|
|
97c9937571 | ||
|
|
c46aca0594 | ||
|
|
0b33e1ff09 | ||
|
|
d267d2a0b0 | ||
|
|
f2996dcebe | ||
|
|
ebb6a186cc | ||
|
|
304fae439a | ||
|
|
9ca81cf305 | ||
|
|
cad5fb6d6b | ||
|
|
53efafdbb2 | ||
|
|
7922017288 | ||
|
|
4e910a33dd | ||
|
|
bb031cdeb6 | ||
|
|
60026bbcba | ||
|
|
e96a1850da | ||
|
|
bd2a746b8a | ||
|
|
e4a3cbc9da | ||
|
|
18734b0edd | ||
|
|
f52b3bf5f5 | ||
|
|
3abba71f8d | ||
|
|
95bdc87ed7 | ||
|
|
9a66b4017b | ||
|
|
610e3f3ae5 | ||
|
|
a58e70c08b | ||
|
|
24c96d40c9 | ||
|
|
74ae59910f | ||
|
|
6d49815be9 | ||
|
|
ae680cd41f | ||
|
|
5e9ea1ca10 | ||
|
|
6f558f424d | ||
|
|
4387876203 | ||
|
|
1b1ce3e04e | ||
|
|
ff64675122 | ||
|
|
1c545b7a74 | ||
|
|
a433ddcd7e | ||
|
|
b0e1b6fe51 | ||
|
|
f5523e60b6 | ||
|
|
e300efe640 | ||
|
|
a97f227ddb | ||
|
|
5f8d7049b5 | ||
|
|
7e80f78f2e | ||
|
|
ad922745c4 | ||
|
|
40abe74dc1 | ||
|
|
e5d2c4d163 | ||
|
|
2851053900 | ||
|
|
b2f6ce1277 | ||
|
|
340b0661ba | ||
|
|
be18f11f8c | ||
|
|
2f8ee67b31 | ||
|
|
1ecf55165e | ||
|
|
5aceccde4a | ||
|
|
abfaee5a55 | ||
|
|
fa65cc7ee2 | ||
|
|
d64f1d0c18 | ||
|
|
31206ce56c | ||
|
|
e4e9a09522 | ||
|
|
11303dc6e2 | ||
|
|
993f5cd292 | ||
|
|
cc0854c712 | ||
|
|
5c6aee0037 | ||
|
|
391f8c34cc | ||
|
|
db6ed26d5a | ||
|
|
eece951036 | ||
|
|
3d7a62293b | ||
|
|
2691dbe438 | ||
|
|
fe971ec611 |
@@ -26,6 +26,7 @@ $paths = [
|
||||
$current . '/../../config',
|
||||
$current . '/../../routes',
|
||||
$current . '/../../tests',
|
||||
$current . '/../../resources/lang/en_US',
|
||||
];
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
|
||||
36
.ci/php-cs-fixer/composer.lock
generated
36
.ci/php-cs-fixer/composer.lock
generated
@@ -402,16 +402,16 @@
|
||||
},
|
||||
{
|
||||
"name": "friendsofphp/php-cs-fixer",
|
||||
"version": "v3.93.0",
|
||||
"version": "v3.93.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||
"reference": "50895a07cface1385082e4caa6a6786c4e033468"
|
||||
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/50895a07cface1385082e4caa6a6786c4e033468",
|
||||
"reference": "50895a07cface1385082e4caa6a6786c4e033468",
|
||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
|
||||
"reference": "b3546ab487c0762c39f308dc1ec0ea2c461fc21a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -494,7 +494,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.0"
|
||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.93.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -502,7 +502,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-23T17:33:21+00:00"
|
||||
"time": "2026-01-28T23:50:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/container",
|
||||
@@ -1640,16 +1640,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v8.0.4",
|
||||
"version": "v8.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "42e48eb02e07d5f3771d194d67da117eb824c8c1"
|
||||
"reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/42e48eb02e07d5f3771d194d67da117eb824c8c1",
|
||||
"reference": "42e48eb02e07d5f3771d194d67da117eb824c8c1",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/8bd576e97c67d45941365bf824e18dc8538e6eb0",
|
||||
"reference": "8bd576e97c67d45941365bf824e18dc8538e6eb0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1684,7 +1684,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.4"
|
||||
"source": "https://github.com/symfony/finder/tree/v8.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1704,7 +1704,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-12T12:37:40+00:00"
|
||||
"time": "2026-01-26T15:08:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
@@ -2358,16 +2358,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v8.0.4",
|
||||
"version": "v8.0.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "10df72602d88c0a3fa685b822976a052611dd607"
|
||||
"reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/10df72602d88c0a3fa685b822976a052611dd607",
|
||||
"reference": "10df72602d88c0a3fa685b822976a052611dd607",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674",
|
||||
"reference": "b5f3aa6762e33fd95efbaa2ec4f4bc9fdd16d674",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2399,7 +2399,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.4"
|
||||
"source": "https://github.com/symfony/process/tree/v8.0.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -2419,7 +2419,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-23T11:07:10+00:00"
|
||||
"time": "2026-01-26T15:08:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/service-contracts",
|
||||
|
||||
@@ -4,6 +4,7 @@ Over time, many people have contributed to Firefly III. Their efforts are not al
|
||||
Please find below all the people who contributed to the Firefly III code. Their names are mentioned in the year of their first contribution.
|
||||
|
||||
## 2026
|
||||
- Nick Huang
|
||||
- mateuszkulapl
|
||||
- Gianluca Martino
|
||||
- embedded
|
||||
|
||||
@@ -63,30 +63,28 @@ class TriggerController extends Controller
|
||||
{
|
||||
// find recurrence occurrence for this date and trigger it.
|
||||
// grab the date from the last time the recurrence fired:
|
||||
$backupDate = $recurrence->latest_date;
|
||||
$date = $request->getDate();
|
||||
$backupDate = $recurrence->latest_date;
|
||||
$date = $request->getDate();
|
||||
|
||||
// fire the recurring cron job on the given date, then post-date the created transaction.
|
||||
Log::info(sprintf('Trigger: will now fire recurring cron job task for date "%s".', $date->format('Y-m-d H:i:s')));
|
||||
|
||||
/** @var CreateRecurringTransactions $job */
|
||||
$job = app(CreateRecurringTransactions::class);
|
||||
$job = app(CreateRecurringTransactions::class);
|
||||
$job->setRecurrences(new Collection()->push($recurrence));
|
||||
$job->setDate($date);
|
||||
$job->setForce(false);
|
||||
$job->handle();
|
||||
Log::debug('Done with recurrence.');
|
||||
|
||||
$groups = $job->getGroups();
|
||||
$groups = $job->getGroups();
|
||||
$this->repository->markGroupsAsNow($groups);
|
||||
$recurrence->latest_date = $backupDate;
|
||||
$recurrence->latest_date_tz = $backupDate?->format('e');
|
||||
$recurrence->save();
|
||||
$recurrence = $this->repository->setLatestDate($recurrence, $backupDate);
|
||||
Preferences::mark();
|
||||
|
||||
// enrich groups and return them:
|
||||
|
||||
$paginator = new LengthAwarePaginator(new Collection(), 0, 1);
|
||||
$paginator = new LengthAwarePaginator(new Collection(), 0, 1);
|
||||
if ($groups->count() > 0) {
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
@@ -98,20 +96,20 @@ class TriggerController extends Controller
|
||||
$paginator = $collector->getPaginatedGroups();
|
||||
}
|
||||
|
||||
$manager = $this->getManager();
|
||||
$manager = $this->getManager();
|
||||
$paginator->setPath(route('api.v1.recurrences.trigger', [$recurrence->id]).$this->buildParams());
|
||||
|
||||
// enrich
|
||||
$admin = auth()->user();
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$admin = auth()->user();
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$transactions = $enrichment->enrich($paginator->getCollection());
|
||||
$transactions = $enrichment->enrich($paginator->getCollection());
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
|
||||
@@ -27,14 +27,11 @@ namespace FireflyIII\Api\V1\Controllers\Models\Transaction;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Models\Transaction\StoreRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use FireflyIII\Rules\IsDuplicateTransaction;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
@@ -88,9 +85,9 @@ class StoreController extends Controller
|
||||
public function store(StoreRequest $request): JsonResponse
|
||||
{
|
||||
Log::debug('Now in API StoreController::store()');
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user();
|
||||
$data['user_group'] = $this->userGroup;
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user();
|
||||
$data['user_group'] = $this->userGroup;
|
||||
|
||||
Log::channel('audit')->info('Store new transaction over API.', $data);
|
||||
|
||||
@@ -109,22 +106,15 @@ class StoreController extends Controller
|
||||
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
Preferences::mark();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = $data['apply_rules'] ?? true;
|
||||
$flags->fireWebhooks = $data['fire_webhooks'] ?? true;
|
||||
$flags->batchSubmission = $data['batch_submission'] ?? false;
|
||||
Log::debug('CreatedSingleTransactionGroup');
|
||||
event(new CreatedSingleTransactionGroup($transactionGroup, $flags));
|
||||
|
||||
$manager = $this->getManager();
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$admin = auth()->user();
|
||||
|
||||
// use new group collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector
|
||||
->setUser($admin)
|
||||
->setUserGroup($this->userGroup)
|
||||
@@ -134,20 +124,20 @@ class StoreController extends Controller
|
||||
->withAPIInformation()
|
||||
;
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw HttpException::fromStatusCode(410, '200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
}
|
||||
|
||||
// enrich
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($selectedGroup, $transformer, 'transactions');
|
||||
$resource = new Item($selectedGroup, $transformer, 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ namespace FireflyIII\Api\V1\Controllers\Models\Transaction;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Models\Transaction\UpdateRequest;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
@@ -72,27 +74,30 @@ class UpdateController extends Controller
|
||||
public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse
|
||||
{
|
||||
Log::debug('Now in update routine for transaction group');
|
||||
$data = $request->getAll();
|
||||
$oldHash = $this->groupRepository->getCompareHash($transactionGroup);
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
$newHash = $this->groupRepository->getCompareHash($transactionGroup);
|
||||
$manager = $this->getManager();
|
||||
$data = $request->getAll();
|
||||
$oldHash = $this->groupRepository->getCompareHash($transactionGroup);
|
||||
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
|
||||
$newHash = $this->groupRepository->getCompareHash($transactionGroup);
|
||||
$manager = $this->getManager();
|
||||
|
||||
Preferences::mark();
|
||||
$applyRules = $data['apply_rules'] ?? true;
|
||||
$fireWebhooks = $data['fire_webhooks'] ?? true;
|
||||
$runRecalculations = $oldHash !== $newHash;
|
||||
$applyRules = $data['apply_rules'] ?? true;
|
||||
$fireWebhooks = $data['fire_webhooks'] ?? true;
|
||||
$runRecalculations = $oldHash !== $newHash;
|
||||
|
||||
// FIXME responds to a single event.
|
||||
// flags in array?
|
||||
event(new UpdatedTransactionGroup($transactionGroup, $applyRules, $fireWebhooks, $runRecalculations));
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = $applyRules;
|
||||
$flags->fireWebhooks = $fireWebhooks;
|
||||
$flags->recalculateCredit = $runRecalculations;
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$admin = auth()->user();
|
||||
|
||||
// use new group collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector
|
||||
->setUser($admin)
|
||||
// filter on transaction group.
|
||||
@@ -101,20 +106,20 @@ class UpdateController extends Controller
|
||||
->withAPIInformation()
|
||||
;
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
|
||||
// enrich
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment = new TransactionGroupEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
$resource = new Item($selectedGroup, $transformer, 'transactions');
|
||||
$resource = new Item($selectedGroup, $transformer, 'transactions');
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Api\V1\Controllers\System;
|
||||
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UserRequestedBatchProcessing;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -44,6 +44,7 @@ class BatchController extends Controller
|
||||
parent::__construct();
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->repository = app(JournalRepositoryInterface::class);
|
||||
$this->repository->setUser(auth()->user()); // should not have to do this.
|
||||
|
||||
return $next($request);
|
||||
});
|
||||
@@ -64,7 +65,8 @@ class BatchController extends Controller
|
||||
}
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = 'true' === $request->get('apply_rules');
|
||||
event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
event(new UserRequestedBatchProcessing($flags));
|
||||
// event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Factory\TransactionCurrencyFactory;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Rules\IsValidPositiveAmount;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
@@ -89,24 +90,24 @@ class StoreRequest extends FormRequest
|
||||
if (0 !== count($validator->failed())) {
|
||||
return;
|
||||
}
|
||||
$data = $validator->getData();
|
||||
$data = $validator->getData();
|
||||
|
||||
// if no currency has been provided, use the user's default currency:
|
||||
/** @var TransactionCurrencyFactory $factory */
|
||||
$factory = app(TransactionCurrencyFactory::class);
|
||||
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
|
||||
$factory = app(TransactionCurrencyFactory::class);
|
||||
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
|
||||
if (null === $currency) {
|
||||
$currency = Amount::getPrimaryCurrency();
|
||||
}
|
||||
$currency->enabled = true;
|
||||
$currency->save();
|
||||
$repository = app(CurrencyRepositoryInterface::class);
|
||||
$repository->enable($currency);
|
||||
|
||||
// validator already concluded start and end are valid dates:
|
||||
$start = Carbon::parse($data['start'], config('app.timezone'));
|
||||
$end = Carbon::parse($data['end'], config('app.timezone'));
|
||||
$start = Carbon::parse($data['start'], config('app.timezone'));
|
||||
$end = Carbon::parse($data['end'], config('app.timezone'));
|
||||
|
||||
// find limit with same date range and currency.
|
||||
$limit = $budget
|
||||
$limit = $budget
|
||||
->budgetlimits()
|
||||
->where('budget_limits.start_date', $start->format('Y-m-d'))
|
||||
->where('budget_limits.end_date', $end->format('Y-m-d'))
|
||||
|
||||
@@ -38,6 +38,8 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Destroy\GenericDestroyService;
|
||||
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -51,9 +53,13 @@ class CorrectsAmounts extends Command
|
||||
|
||||
protected $description = 'This command makes sure positive and negative amounts are recorded correctly.';
|
||||
protected $signature = 'correction:amounts';
|
||||
private JournalDestroyService $service;
|
||||
private GenericDestroyService $genericService;
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->service = new JournalDestroyService();
|
||||
$this->genericService = new GenericDestroyService();
|
||||
// transfers must not have foreign currency info if both accounts have the same currency.
|
||||
$this->correctTransfers();
|
||||
// auto budgets must be positive
|
||||
@@ -177,8 +183,7 @@ class CorrectsAmounts extends Command
|
||||
|
||||
private function deleteJournal(TransactionJournal $journal): void
|
||||
{
|
||||
$journal->transactionGroup?->delete();
|
||||
$journal->delete();
|
||||
$this->service->destroy($journal);
|
||||
}
|
||||
|
||||
private function fixAutoBudgets(): void
|
||||
@@ -282,7 +287,7 @@ class CorrectsAmounts extends Command
|
||||
));
|
||||
$item->rule->active = false;
|
||||
$item->rule->save();
|
||||
$item->forceDelete();
|
||||
$this->genericService->deleteRuleTrigger($item);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Handlers\Events\UpdatedGroupEventHandler;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -44,8 +45,8 @@ class CorrectsGroupAccounts extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$groups = [];
|
||||
$res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
|
||||
$groups = [];
|
||||
$res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($res as $journal) {
|
||||
@@ -53,14 +54,16 @@ class CorrectsGroupAccounts extends Command
|
||||
$groups[] = (int) $journal->transaction_group_id;
|
||||
}
|
||||
}
|
||||
$handler = new UpdatedGroupEventHandler();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = true;
|
||||
$flags->fireWebhooks = true;
|
||||
$flags->recalculateCredit = true;
|
||||
$objects = new TransactionGroupEventObjects();
|
||||
foreach ($groups as $groupId) {
|
||||
$group = TransactionGroup::find($groupId);
|
||||
// TODO in theory the "unifyAccounts" method could lead to the need for run recalculations.
|
||||
// FIXME needs to be a collection.
|
||||
$event = new UpdatedTransactionGroup($group, true, true, false);
|
||||
$handler->unifyAccounts($event);
|
||||
$objects->appendFromTransactionGroup($group);
|
||||
}
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -25,28 +25,9 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Handlers\Observer\TransactionObserver;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AutoBudget;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Services\Internal\Recalculate\PrimaryAmountRecalculationService;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Database\Query\Builder as DatabaseBuilder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
@@ -70,186 +51,11 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
||||
Log::debug('Will update all primary currency amounts. This may take some time.');
|
||||
$this->friendlyWarning('Recalculating primary currency amounts for all objects. This may take some time!');
|
||||
|
||||
/** @var UserGroupRepositoryInterface $repository */
|
||||
$repository = app(UserGroupRepositoryInterface::class);
|
||||
$calculator = new PrimaryAmountRecalculationService();
|
||||
$calculator->recalculate();
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
/** @var UserGroup $userGroup */
|
||||
foreach ($repository->getAll() as $userGroup) {
|
||||
$this->recalculateForGroup($userGroup);
|
||||
}
|
||||
$this->friendlyInfo('Recalculated all primary currency amounts.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function recalculateForGroup(UserGroup $userGroup): void
|
||||
{
|
||||
Log::debug(sprintf('Now recalculating for user group #%d', $userGroup->id));
|
||||
$this->recalculateAccounts($userGroup);
|
||||
|
||||
// do a check with the group's currency so we can skip some stuff.
|
||||
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
|
||||
|
||||
$this->recalculatePiggyBanks($userGroup, $currency);
|
||||
$this->recalculateBudgets($userGroup, $currency);
|
||||
$this->recalculateAvailableBudgets($userGroup, $currency);
|
||||
$this->recalculateBills($userGroup, $currency);
|
||||
$this->calculateTransactions($userGroup, $currency);
|
||||
}
|
||||
|
||||
private function recalculateAccounts(UserGroup $userGroup): void
|
||||
{
|
||||
$set = $userGroup
|
||||
->accounts()
|
||||
->where(static function (EloquentBuilder $q): void {
|
||||
$q->whereNotNull('virtual_balance');
|
||||
|
||||
// this needs a different piece of code for postgres.
|
||||
if ('pgsql' === config('database.default')) {
|
||||
$q->orWhere(DB::raw('CAST(virtual_balance AS TEXT)'), '!=', '');
|
||||
}
|
||||
if ('pgsql' !== config('database.default')) {
|
||||
$q->orWhere('virtual_balance', '!=', '');
|
||||
}
|
||||
})
|
||||
->get()
|
||||
;
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($set as $account) {
|
||||
$account->touch();
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d accounts for user group #%d.', $set->count(), $userGroup->id));
|
||||
}
|
||||
|
||||
private function recalculatePiggyBanks(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUserGroup($userGroup);
|
||||
$set = $repository->getPiggyBanks();
|
||||
$set = $set->filter(static fn (PiggyBank $piggyBank): bool => $currency->id !== $piggyBank->transaction_currency_id);
|
||||
foreach ($set as $piggyBank) {
|
||||
$piggyBank->encrypted = false;
|
||||
$piggyBank->save();
|
||||
|
||||
foreach ($piggyBank->accounts as $account) {
|
||||
$account->pivot->native_current_amount = null;
|
||||
if (0 !== bccomp((string) $account->pivot->current_amount, '0')) {
|
||||
$account->pivot->native_current_amount = $converter->convert(
|
||||
$piggyBank->transactionCurrency,
|
||||
$currency,
|
||||
today(),
|
||||
(string) $account->pivot->current_amount
|
||||
);
|
||||
}
|
||||
$account->pivot->save();
|
||||
}
|
||||
$this->recalculatePiggyBankEvents($piggyBank);
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d piggy banks for user group #%d.', $set->count(), $userGroup->id));
|
||||
}
|
||||
|
||||
private function recalculatePiggyBankEvents(PiggyBank $piggyBank): void
|
||||
{
|
||||
$set = $piggyBank->piggyBankEvents()->get();
|
||||
$set->each(static function (PiggyBankEvent $event): void { // @phpstan-ignore-line
|
||||
$event->touch();
|
||||
});
|
||||
Log::debug(sprintf('Recalculated %d piggy bank events.', $set->count()));
|
||||
}
|
||||
|
||||
private function recalculateBudgets(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
$set = $userGroup->budgets()->get();
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$this->recalculateBudgetLimits($budget, $currency);
|
||||
$this->recalculateAutoBudgets($budget, $currency);
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d budgets.', $set->count()));
|
||||
}
|
||||
|
||||
private function recalculateBudgetLimits(Budget $budget, TransactionCurrency $currency): void
|
||||
{
|
||||
$set = $budget->budgetlimits()->where('transaction_currency_id', '!=', $currency->id)->get();
|
||||
|
||||
/** @var BudgetLimit $limit */
|
||||
foreach ($set as $limit) {
|
||||
Log::debug(sprintf('Will now touch BL #%d', $limit->id));
|
||||
$limit->touch();
|
||||
Log::debug(sprintf('Done with touch BL #%d', $limit->id));
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d budget limits for budget #%d.', $set->count(), $budget->id));
|
||||
}
|
||||
|
||||
private function recalculateAutoBudgets(Budget $budget, TransactionCurrency $currency): void
|
||||
{
|
||||
$set = $budget->autoBudgets()->where('transaction_currency_id', '!=', $currency->id)->get();
|
||||
|
||||
/** @var AutoBudget $autoBudget */
|
||||
foreach ($set as $autoBudget) {
|
||||
$autoBudget->touch();
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d auto budgets for budget #%d.', $set->count(), $budget->id));
|
||||
}
|
||||
|
||||
private function recalculateAvailableBudgets(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
Log::debug('Start with available budgets.');
|
||||
$set = $userGroup->availableBudgets()->where('transaction_currency_id', '!=', $currency->id)->get();
|
||||
|
||||
/** @var AvailableBudget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$budget->touch();
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d available budgets.', $set->count()));
|
||||
}
|
||||
|
||||
private function recalculateBills(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
$set = $userGroup->bills()->where('transaction_currency_id', '!=', $currency->id)->get();
|
||||
|
||||
/** @var Bill $bill */
|
||||
foreach ($set as $bill) {
|
||||
$bill->touch();
|
||||
}
|
||||
Log::debug(sprintf('Recalculated %d bills.', $set->count()));
|
||||
}
|
||||
|
||||
private function calculateTransactions(UserGroup $userGroup, TransactionCurrency $currency): void
|
||||
{
|
||||
// custom query because of the potential size of this update.
|
||||
$set = DB::table('transactions')
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where(static function (DatabaseBuilder $q1) use ($currency): void {
|
||||
$q1->where(static function (DatabaseBuilder $q2) use ($currency): void {
|
||||
$q2->whereNot('transactions.transaction_currency_id', $currency->id)->whereNull('transactions.foreign_currency_id');
|
||||
})->orWhere(static function (DatabaseBuilder $q3) use ($currency): void {
|
||||
$q3->whereNot('transactions.transaction_currency_id', $currency->id)->whereNot('transactions.foreign_currency_id', $currency->id);
|
||||
});
|
||||
})
|
||||
// ->where(static function (DatabaseBuilder $q) use ($currency): void {
|
||||
// $q->whereNot('transactions.transaction_currency_id', $currency->id)
|
||||
// ->whereNot('transactions.foreign_currency_id', $currency->id)
|
||||
// ;
|
||||
// })
|
||||
->get(['transactions.id'])
|
||||
;
|
||||
TransactionObserver::$recalculate = false;
|
||||
foreach ($set as $item) {
|
||||
// here we are.
|
||||
/** @var null|Transaction $transaction */
|
||||
$transaction = Transaction::find($item->id);
|
||||
$transaction?->touch();
|
||||
}
|
||||
TransactionObserver::$recalculate = true;
|
||||
Log::debug(sprintf('Recalculated %d transactions.', $set->count()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Console\Commands\Correction;
|
||||
|
||||
use FireflyIII\Console\Commands\ShowsFriendlyMessages;
|
||||
use FireflyIII\Models\AccountBalance;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
@@ -47,7 +46,6 @@ class CorrectsTimezoneInformation extends Command
|
||||
use ShowsFriendlyMessages;
|
||||
|
||||
public static array $models = [
|
||||
AccountBalance::class => ['date'], // done
|
||||
AvailableBudget::class => ['start_date', 'end_date'], // done
|
||||
Bill::class => ['date', 'end_date', 'extension_date'], // done
|
||||
BudgetLimit::class => ['start_date', 'end_date'], // done
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* DestroyedTransactionGroup.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class DestroyedTransactionGroup.
|
||||
*/
|
||||
class DestroyedTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public TransactionGroup $transactionGroup
|
||||
) {
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* StoredAccount.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
* CreatedNewAccount.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,17 +22,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
namespace FireflyIII\Events\Model\Account;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class StoredAccount
|
||||
*/
|
||||
class StoredAccount extends Event
|
||||
class CreatedNewAccount extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* StoredTransactionGroup.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* UpdatedExistingAccount.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,17 +22,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\Account;
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\Account;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class StoredTransactionGroup.
|
||||
*/
|
||||
class StoredTransactionGroup extends Event
|
||||
class UpdatedExistingAccount extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -38,8 +36,6 @@ class StoredTransactionGroup extends Event
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public TransactionGroup $transactionGroup,
|
||||
public bool $applyRules,
|
||||
public bool $fireWebhooks
|
||||
public Account $account
|
||||
) {}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -37,8 +36,8 @@ class CreatedSingleTransactionGroup extends Event
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public TransactionGroup $transactionGroup,
|
||||
public TransactionGroupEventFlags $flags
|
||||
public TransactionGroupEventFlags $flags,
|
||||
public TransactionGroupEventObjects $objects
|
||||
) {
|
||||
Log::debug(__METHOD__);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* CreatedTransactionGroupBatch.php
|
||||
* DestroyedSingleTransactionGroup.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
@@ -25,10 +25,10 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CreatedTransactionGroupInBatch extends Event
|
||||
class DestroyedSingleTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -36,7 +36,6 @@ class CreatedTransactionGroupInBatch extends Event
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Collection $collection,
|
||||
public array $flags
|
||||
public TransactionGroup $transactionGroup
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* This class collects all objects before and after the creation, removal or updating
|
||||
* of a transaction group. The idea is that this class contains all relevant objects.
|
||||
* Right now, that means journals, tags, accounts, budgets and categories.
|
||||
*
|
||||
* By collecting these objects (in case of an update: before AND after update) there
|
||||
* is a unified set of objects to manage: update balances, recalculate credits, etc.
|
||||
*/
|
||||
class TransactionGroupEventObjects
|
||||
{
|
||||
public Collection $accounts;
|
||||
public Collection $budgets;
|
||||
public Collection $categories;
|
||||
public Collection $tags;
|
||||
public Collection $transactionGroups;
|
||||
public Collection $transactionJournals;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->accounts = new Collection();
|
||||
$this->budgets = new Collection();
|
||||
$this->categories = new Collection();
|
||||
$this->tags = new Collection();
|
||||
$this->transactionGroups = new Collection();
|
||||
$this->transactionJournals = new Collection();
|
||||
}
|
||||
|
||||
public static function collectFromTransactionGroup(TransactionGroup $transactionGroup): self
|
||||
{
|
||||
Log::debug(sprintf('collectFromTransactionGroup(#%d)', $transactionGroup->id));
|
||||
$object = new self();
|
||||
$object->appendFromTransactionGroup($transactionGroup);
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
public function appendFromTransactionGroup(TransactionGroup $transactionGroup): void
|
||||
{
|
||||
$this->transactionGroups->push($transactionGroup);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($transactionGroup->transactionJournals as $journal) {
|
||||
$this->transactionJournals->push($journal);
|
||||
$this->budgets = $this->budgets->merge($journal->budgets);
|
||||
$this->categories = $this->categories->merge($journal->categories);
|
||||
$this->tags = $this->tags->merge($journal->tags);
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
foreach ($journal->transactions as $transaction) {
|
||||
$this->accounts->push($transaction->account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,9 @@ use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class TriggeredStoredTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* UpdatedTransactionGroup.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* UpdatedSingleTransactionGroup.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,17 +22,12 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Events\Event;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class UpdatedTransactionGroup.
|
||||
*/
|
||||
class UpdatedTransactionGroup extends Event
|
||||
class UpdatedSingleTransactionGroup extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
@@ -38,9 +35,7 @@ class UpdatedTransactionGroup extends Event
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public TransactionGroup $transactionGroup,
|
||||
public bool $applyRules,
|
||||
public bool $fireWebhooks,
|
||||
public bool $runRecalculations
|
||||
public TransactionGroupEventFlags $flags,
|
||||
public TransactionGroupEventObjects $objects
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* UserRequestedBatchProcessing.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Events\Event;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UserRequestedBatchProcessing extends Event
|
||||
{
|
||||
public function __construct(
|
||||
public TransactionGroupEventFlags $flags
|
||||
) {
|
||||
Log::debug(__METHOD__);
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ use FireflyIII\Jobs\MailError;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -71,6 +72,7 @@ class Handler extends ExceptionHandler
|
||||
AuthenticationException::class,
|
||||
LaravelValidationException::class,
|
||||
NotFoundHttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
GoneHttpException::class,
|
||||
OAuthServerException::class,
|
||||
LaravelOAuthException::class,
|
||||
|
||||
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Factory;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Events\StoredAccount;
|
||||
use FireflyIII\Events\Model\Account\CreatedNewAccount;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
@@ -122,7 +122,7 @@ class AccountFactory
|
||||
|
||||
$return = $this->createAccount($type, $data);
|
||||
|
||||
event(new StoredAccount($return));
|
||||
event(new CreatedNewAccount($return));
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -109,10 +109,6 @@ class TransactionJournalFactory
|
||||
public function create(array $data): Collection
|
||||
{
|
||||
Log::debug('Now in TransactionJournalFactory::create()');
|
||||
// convert to special object.
|
||||
// $dataObject = new NullArrayObject($data);
|
||||
|
||||
Log::debug('Start of TransactionJournalFactory::create()');
|
||||
$collection = new Collection();
|
||||
$transactions = $data['transactions'] ?? [];
|
||||
if (0 === count($transactions)) {
|
||||
@@ -127,7 +123,7 @@ class TransactionJournalFactory
|
||||
foreach ($transactions as $index => $row) {
|
||||
$row['batch_submission'] = $batchSubmission;
|
||||
Log::debug(sprintf('Now creating journal %d/%d', $index + 1, count($transactions)));
|
||||
$journal = $this->createJournal($row);
|
||||
$journal = $this->createJournal(new NullArrayObject($row));
|
||||
if ($journal instanceof TransactionJournal) {
|
||||
$collection->push($journal);
|
||||
}
|
||||
@@ -188,7 +184,6 @@ class TransactionJournalFactory
|
||||
$carbon->setTimezone(config('app.timezone'));
|
||||
|
||||
// 2024-11-19, overrule timezone with UTC and store it as UTC.
|
||||
|
||||
if (true === FireflyConfig::get('utc', false)->data) {
|
||||
$carbon->setTimezone('UTC');
|
||||
}
|
||||
@@ -399,7 +394,7 @@ class TransactionJournalFactory
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateAccounts(array $data): void
|
||||
private function validateAccounts(NullArrayObject $data): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$transactionType = $data['type'] ?? 'invalid';
|
||||
|
||||
46
app/Factory/WebhookMessageFactory.php
Normal file
46
app/Factory/WebhookMessageFactory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* WebhookMessageFactory.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Factory;
|
||||
|
||||
use FireflyIII\Models\Webhook;
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class WebhookMessageFactory
|
||||
{
|
||||
public function create(Webhook $webhook, array $data): WebhookMessage
|
||||
{
|
||||
$webhookMessage = new WebhookMessage();
|
||||
$webhookMessage->webhook()->associate($webhook);
|
||||
$webhookMessage->sent = false;
|
||||
$webhookMessage->errored = false;
|
||||
$webhookMessage->uuid = $data['uuid'];
|
||||
$webhookMessage->message = $data;
|
||||
$webhookMessage->save();
|
||||
Log::debug(sprintf('Stored new webhook message #%d', $webhookMessage->id));
|
||||
|
||||
return $webhookMessage;
|
||||
}
|
||||
}
|
||||
@@ -27,13 +27,13 @@ namespace FireflyIII\Generator\Webhook;
|
||||
use FireflyIII\Enums\WebhookResponse;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\WebhookMessageFactory;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\Webhook;
|
||||
use FireflyIII\Models\WebhookMessage;
|
||||
use FireflyIII\Models\WebhookResponse as WebhookResponseModel;
|
||||
use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment;
|
||||
@@ -254,7 +254,8 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
||||
$basicMessage['content'][] = $transformer->transform($account);
|
||||
}
|
||||
}
|
||||
$this->storeMessage($webhook, $basicMessage);
|
||||
$factory = new WebhookMessageFactory();
|
||||
$factory->create($webhook, $basicMessage);
|
||||
}
|
||||
|
||||
public function getVersion(): int
|
||||
@@ -277,18 +278,6 @@ class StandardMessageGenerator implements MessageGeneratorInterface
|
||||
return $accounts->unique();
|
||||
}
|
||||
|
||||
private function storeMessage(Webhook $webhook, array $message): void
|
||||
{
|
||||
$webhookMessage = new WebhookMessage();
|
||||
$webhookMessage->webhook()->associate($webhook);
|
||||
$webhookMessage->sent = false;
|
||||
$webhookMessage->errored = false;
|
||||
$webhookMessage->uuid = $message['uuid'];
|
||||
$webhookMessage->message = $message;
|
||||
$webhookMessage->save();
|
||||
Log::debug(sprintf('Stored new webhook message #%d', $webhookMessage->id));
|
||||
}
|
||||
|
||||
public function setObjects(Collection $objects): void
|
||||
{
|
||||
$this->objects = $objects;
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* APIEventHandler.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Notifications\User\NewAccessToken;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Laravel\Passport\Events\AccessTokenCreated;
|
||||
|
||||
/**
|
||||
* Class APIEventHandler
|
||||
*/
|
||||
class APIEventHandler
|
||||
{
|
||||
/**
|
||||
* Respond to the creation of an access token.
|
||||
*/
|
||||
public function accessTokenCreated(AccessTokenCreated $event): void
|
||||
{
|
||||
Log::debug(__METHOD__);
|
||||
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
$user = $repository->find((int) $event->userId);
|
||||
|
||||
if (null !== $user) {
|
||||
try {
|
||||
Notification::send($user, new NewAccessToken());
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* StoredAccountEventHandler.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\StoredAccount;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
|
||||
/**
|
||||
* Class StoredAccountEventHandler
|
||||
*/
|
||||
class StoredAccountEventHandler
|
||||
{
|
||||
public function recalculateCredit(StoredAccount $event): void
|
||||
{
|
||||
$account = $event->account;
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setAccount($account);
|
||||
$object->recalculate();
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* StoredGroupEventHandler.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class StoredGroupEventHandler
|
||||
*
|
||||
* TODO migrate to observer?
|
||||
*/
|
||||
class StoredGroupEventHandler
|
||||
{
|
||||
public function runAllHandlers(StoredTransactionGroup $event): void
|
||||
{
|
||||
// $this->processRules($event, null);
|
||||
// $this->recalculateCredit($event);
|
||||
// $this->triggerWebhooks($event);
|
||||
$this->removePeriodStatistics($event);
|
||||
}
|
||||
|
||||
public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void
|
||||
{
|
||||
// $newEvent = new StoredTransactionGroup($event->transactionGroup, true, false);
|
||||
// $this->processRules($newEvent, $event->ruleGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method grabs all the users rules and processes them.
|
||||
*/
|
||||
private function processRules(StoredTransactionGroup $storedGroupEvent, ?RuleGroup $ruleGroup): void
|
||||
{
|
||||
if (false === $storedGroupEvent->applyRules) {
|
||||
Log::info(sprintf('Will not run rules on group #%d', $storedGroupEvent->transactionGroup->id));
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('Now in StoredGroupEventHandler::processRules()');
|
||||
|
||||
$journals = $storedGroupEvent->transactionGroup->transactionJournals;
|
||||
$array = [];
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$array[] = $journal->id;
|
||||
}
|
||||
$journalIds = implode(',', $array);
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$ruleGroupRepository->setUser($storedGroupEvent->transactionGroup->user);
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
if (null === $ruleGroup) {
|
||||
Log::debug('Fire processRules with ALL store-journal rule groups.');
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
}
|
||||
if (null !== $ruleGroup) {
|
||||
Log::debug(sprintf('Fire processRules with rule group #%d.', $ruleGroup->id));
|
||||
$groups = new Collection([$ruleGroup]);
|
||||
}
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($storedGroupEvent->transactionGroup->user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
private function recalculateCredit(StoredTransactionGroup $event): void
|
||||
{
|
||||
$group = $event->transactionGroup;
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setGroup($group);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function removePeriodStatistics(StoredTransactionGroup $event): void
|
||||
{
|
||||
/** @var PeriodStatisticRepositoryInterface $repository */
|
||||
$repository = app(PeriodStatisticRepositoryInterface::class);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($event->transactionGroup->transactionJournals as $journal) {
|
||||
/** @var null|Transaction $source */
|
||||
$source = $journal->transactions()->where('amount', '<', '0')->first();
|
||||
|
||||
/** @var null|Transaction $dest */
|
||||
$dest = $journal->transactions()->where('amount', '>', '0')->first();
|
||||
|
||||
if (null !== $source) {
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
}
|
||||
if (null !== $dest) {
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
}
|
||||
$categories = $journal->categories;
|
||||
$tags = $journal->tags;
|
||||
$budgets = $journal->budgets;
|
||||
foreach ($categories as $category) {
|
||||
$repository->deleteStatisticsForModel($category, $journal->date);
|
||||
}
|
||||
foreach ($tags as $tag) {
|
||||
$repository->deleteStatisticsForModel($tag, $journal->date);
|
||||
}
|
||||
foreach ($budgets as $budget) {
|
||||
$repository->deleteStatisticsForModel($budget, $journal->date);
|
||||
}
|
||||
if (0 === $categories->count()) {
|
||||
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_category', $journal->date);
|
||||
}
|
||||
if (0 === $budgets->count()) {
|
||||
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_budget', $journal->date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method processes all webhooks that respond to the "stored transaction group" trigger (100)
|
||||
*/
|
||||
private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void
|
||||
{
|
||||
Log::debug(__METHOD__);
|
||||
$group = $storedGroupEvent->transactionGroup;
|
||||
if (false === $storedGroupEvent->fireWebhooks) {
|
||||
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $group->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
|
||||
// tell the generator which trigger it should look for
|
||||
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
|
||||
// tell the generator which objects to process
|
||||
$engine->setObjects(new Collection()->push($group));
|
||||
// tell the generator to generate the messages
|
||||
$engine->generateMessages();
|
||||
|
||||
// trigger event to send them:
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* UpdatedAccountEventHandler.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\UpdatedAccount;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
|
||||
/**
|
||||
* Class UpdatedAccountEventHandler
|
||||
*/
|
||||
class UpdatedAccountEventHandler
|
||||
{
|
||||
public function recalculateCredit(UpdatedAccount $event): void
|
||||
{
|
||||
$account = $event->account;
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setAccount($account);
|
||||
$object->recalculate();
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* UpdatedGroupEventHandler.php
|
||||
* Copyright (c) 2019 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class UpdatedGroupEventHandler
|
||||
*/
|
||||
class UpdatedGroupEventHandler
|
||||
{
|
||||
public function runAllHandlers(UpdatedTransactionGroup $event): void
|
||||
{
|
||||
$this->unifyAccounts($event);
|
||||
$this->processRules($event);
|
||||
$this->recalculateCredit($event);
|
||||
$this->triggerWebhooks($event);
|
||||
$this->removePeriodStatistics($event);
|
||||
if ($event->runRecalculations) {
|
||||
$this->updateRunningBalance($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate
|
||||
*/
|
||||
private function removePeriodStatistics(UpdatedTransactionGroup $event): void
|
||||
{
|
||||
/** @var PeriodStatisticRepositoryInterface $repository */
|
||||
$repository = app(PeriodStatisticRepositoryInterface::class);
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($event->transactionGroup->transactionJournals as $journal) {
|
||||
$source = $journal->transactions()->where('amount', '<', '0')->first();
|
||||
$dest = $journal->transactions()->where('amount', '>', '0')->first();
|
||||
if (null !== $source) {
|
||||
$repository->deleteStatisticsForModel($source->account, $journal->date);
|
||||
}
|
||||
if (null !== $dest) {
|
||||
$repository->deleteStatisticsForModel($dest->account, $journal->date);
|
||||
}
|
||||
|
||||
$categories = $journal->categories;
|
||||
$tags = $journal->tags;
|
||||
$budgets = $journal->budgets;
|
||||
|
||||
foreach ($categories as $category) {
|
||||
$repository->deleteStatisticsForModel($category, $journal->date);
|
||||
}
|
||||
foreach ($tags as $tag) {
|
||||
$repository->deleteStatisticsForModel($tag, $journal->date);
|
||||
}
|
||||
foreach ($budgets as $budget) {
|
||||
$repository->deleteStatisticsForModel($budget, $journal->date);
|
||||
}
|
||||
if (0 === $categories->count()) {
|
||||
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_category', $journal->date);
|
||||
}
|
||||
if (0 === $budgets->count()) {
|
||||
$repository->deleteStatisticsForPrefix($journal->userGroup, 'no_budget', $journal->date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will make sure all source / destination accounts are the same.
|
||||
*/
|
||||
public function unifyAccounts(UpdatedTransactionGroup $updatedGroupEvent): void
|
||||
{
|
||||
$group = $updatedGroupEvent->transactionGroup;
|
||||
if (1 === $group->transactionJournals->count()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// first journal:
|
||||
/** @var null|TransactionJournal $first */
|
||||
$first = $group
|
||||
->transactionJournals()
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->first()
|
||||
;
|
||||
|
||||
if (null === $first) {
|
||||
Log::warning(sprintf('Group #%d has no transaction journals.', $group->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$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;
|
||||
|
||||
$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]);
|
||||
}
|
||||
if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::DEPOSIT->value === $type) {
|
||||
// set all destination transactions to destination account:
|
||||
Transaction::whereIn('transaction_journal_id', $all)->where('amount', '>', 0)->update(['account_id' => $destAccount->id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will check all the rules when a journal is updated.
|
||||
*/
|
||||
private function processRules(UpdatedTransactionGroup $updatedGroupEvent): void
|
||||
{
|
||||
if (false === $updatedGroupEvent->applyRules) {
|
||||
Log::info(sprintf('Will not run rules on group #%d', $updatedGroupEvent->transactionGroup->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$journals = $updatedGroupEvent->transactionGroup->transactionJournals;
|
||||
$array = [];
|
||||
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$array[] = $journal->id;
|
||||
}
|
||||
$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');
|
||||
|
||||
// file rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($updatedGroupEvent->transactionGroup->user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
private function recalculateCredit(UpdatedTransactionGroup $event): void
|
||||
{
|
||||
$group = $event->transactionGroup;
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setGroup($group);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function triggerWebhooks(UpdatedTransactionGroup $updatedGroupEvent): void
|
||||
{
|
||||
Log::debug(__METHOD__);
|
||||
$group = $updatedGroupEvent->transactionGroup;
|
||||
if (false === $updatedGroupEvent->fireWebhooks) {
|
||||
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
|
||||
|
||||
return;
|
||||
}
|
||||
$user = $group->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
$engine->setObjects(new Collection()->push($group));
|
||||
$engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION);
|
||||
$engine->generateMessages();
|
||||
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
|
||||
private function updateRunningBalance(UpdatedTransactionGroup $event): void
|
||||
{
|
||||
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
return;
|
||||
}
|
||||
Log::debug(__METHOD__);
|
||||
$group = $event->transactionGroup;
|
||||
foreach ($group->transactionJournals as $journal) {
|
||||
AccountBalanceCalculator::recalculateForJournal($journal);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
app/Handlers/ExchangeRate/ConversionParameters.php
Normal file
45
app/Handlers/ExchangeRate/ConversionParameters.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ConversionParameters.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\ExchangeRate;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ConversionParameters
|
||||
{
|
||||
public User $user;
|
||||
public Model $model;
|
||||
public ?TransactionCurrency $originalCurrency = null;
|
||||
public string $amountField;
|
||||
public string $primaryAmountField;
|
||||
public Carbon $date;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->date = now();
|
||||
}
|
||||
}
|
||||
104
app/Handlers/ExchangeRate/ConvertsAmountToPrimaryAmount.php
Normal file
104
app/Handlers/ExchangeRate/ConvertsAmountToPrimaryAmount.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ConvertsAmountToPrimaryAmount.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Handlers\ExchangeRate;
|
||||
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ConvertsAmountToPrimaryAmount
|
||||
{
|
||||
public static function convert(ConversionParameters $params): void
|
||||
{
|
||||
$amountField = $params->amountField;
|
||||
$primaryAmountField = $params->primaryAmountField;
|
||||
|
||||
if (!Amount::convertToPrimary($params->user)) {
|
||||
Log::debug(sprintf(
|
||||
'User does not want to do conversion, no need to convert %s and store it in field %s for %s #%d.',
|
||||
$params->amountField,
|
||||
$params->primaryAmountField,
|
||||
get_class($params->model),
|
||||
$params->model->id
|
||||
));
|
||||
$params->model->{$primaryAmountField} = null;
|
||||
$params->model->saveQuietly();
|
||||
|
||||
return;
|
||||
}
|
||||
if (null === $params->originalCurrency) {
|
||||
Log::debug(sprintf(
|
||||
'Original currency field is empty, no need to convert %s and store it in field %s for %s #%d.',
|
||||
$params->amountField,
|
||||
$params->primaryAmountField,
|
||||
get_class($params->model),
|
||||
$params->model->id
|
||||
));
|
||||
|
||||
return;
|
||||
}
|
||||
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($params->user->userGroup);
|
||||
Log::debug(sprintf(
|
||||
'Will convert amount in field %s from %s to %s and store it in %s',
|
||||
$params->originalCurrency->code,
|
||||
$primaryCurrency->code,
|
||||
$params->amountField,
|
||||
$params->primaryAmountField
|
||||
));
|
||||
if ($params->originalCurrency->id === $primaryCurrency->id) {
|
||||
Log::debug('Both currencies are the same, do nothing.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// field is empty or zero, do nothing.
|
||||
$amount = (string) $params->model->{$amountField};
|
||||
if ('' === $amount || 0 === bccomp($amount, '0')) {
|
||||
Log::debug(sprintf('Amount "%s" in field "%s" cannot be used, do nothing.', $amount, $amountField));
|
||||
$params->model->{$amountField} = null;
|
||||
$params->model->{$primaryAmountField} = null;
|
||||
$params->model->saveQuietly();
|
||||
|
||||
return;
|
||||
}
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($params->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$newAmount = $converter->convert($params->originalCurrency, $primaryCurrency, now(), $amount);
|
||||
$params->model->{$primaryAmountField} = $newAmount;
|
||||
$params->model->saveQuietly();
|
||||
Log::debug(sprintf(
|
||||
'Converted field "%s" of %s #%d from %s %s to %s %s (in field "%s")',
|
||||
$amountField,
|
||||
get_class($params->model),
|
||||
$params->model->id,
|
||||
$params->originalCurrency->code,
|
||||
$amount,
|
||||
$primaryCurrency->code,
|
||||
$newAmount,
|
||||
$primaryAmountField
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\AutoBudget;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AutoBudgetObserver
|
||||
@@ -37,26 +37,20 @@ class AutoBudgetObserver
|
||||
$this->updatePrimaryCurrencyAmount($autoBudget);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(AutoBudget $autoBudget): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($autoBudget->budget->user)) {
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($autoBudget->budget->user->userGroup);
|
||||
$autoBudget->native_amount = null;
|
||||
if ($autoBudget->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($autoBudget->budget->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$autoBudget->native_amount = $converter->convert($autoBudget->transactionCurrency, $userCurrency, today(), $autoBudget->amount);
|
||||
}
|
||||
$autoBudget->saveQuietly();
|
||||
Log::debug('Auto budget primary currency amount is updated.');
|
||||
}
|
||||
|
||||
public function updated(AutoBudget $autoBudget): void
|
||||
{
|
||||
Log::debug('Observe "updated" of an auto budget.');
|
||||
$this->updatePrimaryCurrencyAmount($autoBudget);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(AutoBudget $autoBudget): void
|
||||
{
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $autoBudget->budget->user;
|
||||
$params->model = $autoBudget;
|
||||
$params->originalCurrency = $autoBudget->transactionCurrency;
|
||||
$params->amountField = 'amount';
|
||||
$params->primaryAmountField = 'native_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,41 +24,30 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\AvailableBudget;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AvailableBudgetObserver
|
||||
{
|
||||
public function created(AvailableBudget $availableBudget): void
|
||||
{
|
||||
// Log::debug('Observe "created" of an available budget.');
|
||||
$this->updatePrimaryCurrencyAmount($availableBudget);
|
||||
}
|
||||
|
||||
public function updated(AvailableBudget $availableBudget): void
|
||||
{
|
||||
$this->updatePrimaryCurrencyAmount($availableBudget);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(AvailableBudget $availableBudget): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($availableBudget->user)) {
|
||||
// Log::debug('Do not update primary currency available amount of the available budget.');
|
||||
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($availableBudget->user->userGroup);
|
||||
$availableBudget->native_amount = null;
|
||||
if ($availableBudget->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($availableBudget->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$availableBudget->native_amount = $converter->convert($availableBudget->transactionCurrency, $userCurrency, today(), $availableBudget->amount);
|
||||
}
|
||||
$availableBudget->saveQuietly();
|
||||
Log::debug('Available budget primary currency amount is updated.');
|
||||
}
|
||||
|
||||
public function updated(AvailableBudget $availableBudget): void
|
||||
{
|
||||
// Log::debug('Observe "updated" of an available budget.');
|
||||
$this->updatePrimaryCurrencyAmount($availableBudget);
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $availableBudget->user;
|
||||
$params->model = $availableBudget;
|
||||
$params->originalCurrency = $availableBudget->transactionCurrency;
|
||||
$params->amountField = 'amount';
|
||||
$params->primaryAmountField = 'native_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class BillObserver
|
||||
@@ -37,35 +36,14 @@ class BillObserver
|
||||
{
|
||||
public function created(Bill $bill): void
|
||||
{
|
||||
// Log::debug('Observe "created" of a bill.');
|
||||
$this->updatePrimaryCurrencyAmount($bill);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(Bill $bill): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($bill->user)) {
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($bill->user->userGroup);
|
||||
$bill->native_amount_min = null;
|
||||
$bill->native_amount_max = null;
|
||||
if ($bill->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($bill->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$bill->native_amount_min = $converter->convert($bill->transactionCurrency, $userCurrency, today(), $bill->amount_min);
|
||||
$bill->native_amount_max = $converter->convert($bill->transactionCurrency, $userCurrency, today(), $bill->amount_max);
|
||||
}
|
||||
$bill->saveQuietly();
|
||||
Log::debug('Bill primary currency amounts are updated.');
|
||||
}
|
||||
|
||||
public function deleting(Bill $bill): void
|
||||
{
|
||||
$repository = app(AttachmentRepositoryInterface::class);
|
||||
$repository->setUser($bill->user);
|
||||
|
||||
// Log::debug('Observe "deleting" of a bill.');
|
||||
/** @var Attachment $attachment */
|
||||
foreach ($bill->attachments()->get() as $attachment) {
|
||||
$repository->destroy($attachment);
|
||||
@@ -78,4 +56,20 @@ class BillObserver
|
||||
// Log::debug('Observe "updated" of a bill.');
|
||||
$this->updatePrimaryCurrencyAmount($bill);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(Bill $bill): void
|
||||
{
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $bill->user;
|
||||
$params->model = $bill;
|
||||
$params->originalCurrency = $bill->transactionCurrency;
|
||||
$params->amountField = 'amount_min';
|
||||
$params->primaryAmountField = 'native_amount_min';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
|
||||
// and again!
|
||||
$params->amountField = 'amount_max';
|
||||
$params->primaryAmountField = 'native_amount_max';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace FireflyIII\Handlers\Observer;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Observers\RecalculatesAvailableBudgetsTrait;
|
||||
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -44,18 +44,30 @@ class BudgetLimitObserver
|
||||
Log::debug('Observe "created" of a budget limit.');
|
||||
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
||||
$this->updateAvailableBudget($budgetLimit);
|
||||
$this->sendWebhookMessages('fire_webhooks_bl_store', WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT, $budgetLimit);
|
||||
}
|
||||
|
||||
public function updated(BudgetLimit $budgetLimit): void
|
||||
{
|
||||
Log::debug('Observe "updated" of a budget limit.');
|
||||
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
||||
$this->updateAvailableBudget($budgetLimit);
|
||||
$this->sendWebhookMessages('fire_webhooks_bl_update', WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT, $budgetLimit);
|
||||
}
|
||||
|
||||
private function sendWebhookMessages(string $key, WebhookTrigger $trigger, BudgetLimit $budgetLimit): void
|
||||
{
|
||||
// this is a lame trick to communicate with the observer.
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
|
||||
if (true === $singleton->getPreference('fire_webhooks_bl_store')) {
|
||||
if (true === $singleton->getPreference($key)) {
|
||||
$user = $budgetLimit->budget->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
$engine->setObjects(new Collection()->push($budgetLimit));
|
||||
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
|
||||
$engine->setTrigger($trigger);
|
||||
$engine->generateMessages();
|
||||
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
@@ -65,44 +77,12 @@ class BudgetLimitObserver
|
||||
|
||||
private function updatePrimaryCurrencyAmount(BudgetLimit $budgetLimit): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($budgetLimit->budget->user)) {
|
||||
// Log::debug('Do not update primary currency amount of the budget limit.');
|
||||
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($budgetLimit->budget->user->userGroup);
|
||||
$budgetLimit->native_amount = null;
|
||||
if ($budgetLimit->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($budgetLimit->budget->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$budgetLimit->native_amount = $converter->convert($budgetLimit->transactionCurrency, $userCurrency, today(), $budgetLimit->amount);
|
||||
}
|
||||
$budgetLimit->saveQuietly();
|
||||
Log::debug('Budget limit primary currency amounts are updated.');
|
||||
}
|
||||
|
||||
public function updated(BudgetLimit $budgetLimit): void
|
||||
{
|
||||
Log::debug('Observe "updated" of a budget limit.');
|
||||
$this->updatePrimaryCurrencyAmount($budgetLimit);
|
||||
$this->updateAvailableBudget($budgetLimit);
|
||||
|
||||
// this is a lame trick to communicate with the observer.
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
|
||||
if (true === $singleton->getPreference('fire_webhooks_bl_update')) {
|
||||
$user = $budgetLimit->budget->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
$engine->setObjects(new Collection()->push($budgetLimit));
|
||||
$engine->setTrigger(WebhookTrigger::STORE_UPDATE_BUDGET_LIMIT);
|
||||
$engine->generateMessages();
|
||||
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $budgetLimit->budget->user;
|
||||
$params->model = $budgetLimit;
|
||||
$params->originalCurrency = $budgetLimit->transactionCurrency;
|
||||
$params->amountField = 'amount';
|
||||
$params->primaryAmountField = 'native_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,27 +64,6 @@ class BudgetObserver
|
||||
}
|
||||
}
|
||||
|
||||
public function updated(Budget $budget): void
|
||||
{
|
||||
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
|
||||
|
||||
// this is a lame trick to communicate with the observer.
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
|
||||
if (true === $singleton->getPreference('fire_webhooks_budget_update')) {
|
||||
$user = $budget->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
$engine->setObjects(new Collection()->push($budget));
|
||||
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
|
||||
$engine->generateMessages();
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
}
|
||||
|
||||
public function deleting(Budget $budget): void
|
||||
{
|
||||
Log::debug('Observe "deleting" of a budget.');
|
||||
@@ -123,4 +102,25 @@ class BudgetObserver
|
||||
|
||||
// recalculate available budgets.
|
||||
}
|
||||
|
||||
public function updated(Budget $budget): void
|
||||
{
|
||||
Log::debug(sprintf('Observe "updated" of budget #%d ("%s").', $budget->id, $budget->name));
|
||||
|
||||
// this is a lame trick to communicate with the observer.
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
|
||||
if (true === $singleton->getPreference('fire_webhooks_budget_update')) {
|
||||
$user = $budget->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
$engine->setObjects(new Collection()->push($budget));
|
||||
$engine->setTrigger(WebhookTrigger::UPDATE_BUDGET);
|
||||
$engine->generateMessages();
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,55 +29,12 @@ use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class AccountObserver
|
||||
*/
|
||||
class AccountObserver
|
||||
class DeletedAccountObserver
|
||||
{
|
||||
public function created(Account $account): void
|
||||
{
|
||||
// Log::debug('Observe "created" of an account.');
|
||||
$this->updatePrimaryCurrencyAmount($account);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(Account $account): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($account->user)) {
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$currency = $repository->getAccountCurrency($account);
|
||||
if (
|
||||
null !== $currency
|
||||
&& $currency->id !== $userCurrency->id
|
||||
&& '' !== (string) $account->virtual_balance
|
||||
&& 0 !== bccomp($account->virtual_balance, '0')
|
||||
) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($account->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$account->native_virtual_balance = $converter->convert($currency, $userCurrency, today(), $account->virtual_balance);
|
||||
}
|
||||
if ('' === (string) $account->virtual_balance || 0 === bccomp($account->virtual_balance, '0')) {
|
||||
$account->virtual_balance = null;
|
||||
$account->native_virtual_balance = null;
|
||||
}
|
||||
$account->saveQuietly();
|
||||
|
||||
// Log::debug('Account primary currency virtual balance is updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Also delete related objects.
|
||||
*/
|
||||
public function deleting(Account $account): void
|
||||
{
|
||||
Log::debug('Observe "deleting" of an account.');
|
||||
@@ -117,10 +74,4 @@ class AccountObserver
|
||||
$account->notes()->delete();
|
||||
$account->locations()->delete();
|
||||
}
|
||||
|
||||
public function updated(Account $account): void
|
||||
{
|
||||
// Log::debug('Observe "updated" of an account.');
|
||||
$this->updatePrimaryCurrencyAmount($account);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class AttachmentObserver
|
||||
*/
|
||||
class AttachmentObserver
|
||||
class DeletedAttachmentObserver
|
||||
{
|
||||
public function deleting(Attachment $attachment): void
|
||||
{
|
||||
@@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class CategoryObserver
|
||||
*/
|
||||
class CategoryObserver
|
||||
class DeletedCategoryObserver
|
||||
{
|
||||
public function deleting(Category $category): void
|
||||
{
|
||||
@@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class RecurrenceObserver
|
||||
*/
|
||||
class RecurrenceObserver
|
||||
class DeletedRecurrenceObserver
|
||||
{
|
||||
public function deleting(Recurrence $recurrence): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class RecurrenceTransactionObserver
|
||||
*/
|
||||
class RecurrenceTransactionObserver
|
||||
class DeletedRecurrenceTransactionObserver
|
||||
{
|
||||
public function deleting(RecurrenceTransaction $transaction): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class RuleGroupObserver
|
||||
*/
|
||||
class RuleGroupObserver
|
||||
class DeletedRuleGroupObserver
|
||||
{
|
||||
public function deleting(RuleGroup $ruleGroup): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class RuleObserver
|
||||
*/
|
||||
class RuleObserver
|
||||
class DeletedRuleObserver
|
||||
{
|
||||
public function deleting(Rule $rule): void
|
||||
{
|
||||
@@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class TagObserver
|
||||
*/
|
||||
class TagObserver
|
||||
class DeletedTagObserver
|
||||
{
|
||||
public function deleting(Tag $tag): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class TransactionGroup
|
||||
*/
|
||||
class TransactionGroupObserver
|
||||
class DeletedTransactionGroupObserver
|
||||
{
|
||||
public function deleting(TransactionGroup $transactionGroup): void
|
||||
{
|
||||
@@ -33,7 +33,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class TransactionJournalObserver
|
||||
*/
|
||||
class TransactionJournalObserver
|
||||
class DeletedTransactionJournalObserver
|
||||
{
|
||||
public function deleting(TransactionJournal $transactionJournal): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class WebhookMessageObserver
|
||||
*/
|
||||
class WebhookMessageObserver
|
||||
class DeletedWebhookMessageObserver
|
||||
{
|
||||
public function deleting(WebhookMessage $webhookMessage): void
|
||||
{
|
||||
@@ -29,7 +29,7 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* Class WebhookObserver
|
||||
*/
|
||||
class WebhookObserver
|
||||
class DeletedWebhookObserver
|
||||
{
|
||||
public function deleting(Webhook $webhook): void
|
||||
{
|
||||
@@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\PiggyBankEvent;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PiggyBankEventObserver
|
||||
@@ -37,32 +37,27 @@ class PiggyBankEventObserver
|
||||
$this->updatePrimaryCurrencyAmount($event);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(PiggyBankEvent $event): void
|
||||
{
|
||||
$user = $event->piggyBank->accounts()->first()?->user;
|
||||
if (null === $user) {
|
||||
Log::warning('Piggy bank seems to have no accounts. Break.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (!Amount::convertToPrimary($user)) {
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($event->piggyBank->accounts()->first()->user->userGroup);
|
||||
$event->native_amount = null;
|
||||
if ($event->piggyBank->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($event->piggyBank->accounts()->first()->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$event->native_amount = $converter->convert($event->piggyBank->transactionCurrency, $userCurrency, today(), $event->amount);
|
||||
}
|
||||
$event->saveQuietly();
|
||||
Log::debug('Piggy bank event primary currency amount is updated.');
|
||||
}
|
||||
|
||||
public function updated(PiggyBankEvent $event): void
|
||||
{
|
||||
Log::debug('Observe "updated" of a piggy bank event.');
|
||||
$this->updatePrimaryCurrencyAmount($event);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(PiggyBankEvent $event): void
|
||||
{
|
||||
$user = $event->piggyBank->accounts()->first()?->user;
|
||||
if (null === $user) {
|
||||
Log::warning('Piggy bank seems to have no accounts. Break.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $user;
|
||||
$params->model = $event;
|
||||
$params->originalCurrency = $event->piggyBank->transactionCurrency;
|
||||
$params->amountField = 'amount';
|
||||
$params->primaryAmountField = 'native_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -42,26 +42,6 @@ class PiggyBankObserver
|
||||
$this->updatePrimaryCurrencyAmount($piggyBank);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(PiggyBank $piggyBank): void
|
||||
{
|
||||
$group = $piggyBank->accounts()->first()?->user->userGroup;
|
||||
if (null === $group) {
|
||||
Log::debug(sprintf('No account(s) yet for piggy bank #%d.', $piggyBank->id));
|
||||
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($group);
|
||||
$piggyBank->native_target_amount = null;
|
||||
if ($piggyBank->transactionCurrency->id !== $userCurrency->id) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setIgnoreSettings(true);
|
||||
$converter->setUserGroup($group);
|
||||
$piggyBank->native_target_amount = $converter->convert($piggyBank->transactionCurrency, $userCurrency, today(), $piggyBank->target_amount);
|
||||
}
|
||||
$piggyBank->saveQuietly();
|
||||
Log::debug('Piggy bank primary currency target amount is updated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Also delete related objects.
|
||||
*/
|
||||
@@ -88,4 +68,22 @@ class PiggyBankObserver
|
||||
Log::debug('Observe "updated" of a piggy bank.');
|
||||
$this->updatePrimaryCurrencyAmount($piggyBank);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(PiggyBank $piggyBank): void
|
||||
{
|
||||
$group = $piggyBank->accounts()->first()?->user->userGroup;
|
||||
if (null === $group) {
|
||||
Log::debug(sprintf('No account(s) yet for piggy bank #%d.', $piggyBank->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $piggyBank->accounts()->first()?->user;
|
||||
$params->model = $piggyBank;
|
||||
$params->originalCurrency = $piggyBank->transactionCurrency;
|
||||
$params->amountField = 'target_amount';
|
||||
$params->primaryAmountField = 'native_target_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Observer;
|
||||
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
@@ -39,55 +37,10 @@ class TransactionObserver
|
||||
|
||||
public function created(Transaction $transaction): void
|
||||
{
|
||||
return;
|
||||
|
||||
Log::debug(sprintf('Observed creation of Transaction #%d.', $transaction->id));
|
||||
$this->updatePrimaryCurrencyAmount($transaction);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(Transaction $transaction): void
|
||||
{
|
||||
if (!Amount::convertToPrimary($transaction->transactionJournal->user)) {
|
||||
return;
|
||||
}
|
||||
$userCurrency = Amount::getPrimaryCurrencyByUserGroup($transaction->transactionJournal->user->userGroup);
|
||||
$transaction->native_amount = null;
|
||||
$transaction->native_foreign_amount = null;
|
||||
// first normal amount
|
||||
if (
|
||||
$transaction->transactionCurrency->id !== $userCurrency->id
|
||||
&& (
|
||||
null === $transaction->foreign_currency_id
|
||||
|| null !== $transaction->foreign_currency_id
|
||||
&& $transaction->foreign_currency_id !== $userCurrency->id
|
||||
)
|
||||
) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($transaction->transactionJournal->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$transaction->native_amount = $converter->convert(
|
||||
$transaction->transactionCurrency,
|
||||
$userCurrency,
|
||||
$transaction->transactionJournal->date,
|
||||
$transaction->amount
|
||||
);
|
||||
}
|
||||
// then foreign amount
|
||||
if ($transaction->foreignCurrency?->id !== $userCurrency->id && null !== $transaction->foreign_amount && null !== $transaction->foreignCurrency) {
|
||||
$converter = new ExchangeRateConverter();
|
||||
$converter->setUserGroup($transaction->transactionJournal->user->userGroup);
|
||||
$converter->setIgnoreSettings(true);
|
||||
$transaction->native_foreign_amount = $converter->convert(
|
||||
$transaction->foreignCurrency,
|
||||
$userCurrency,
|
||||
$transaction->transactionJournal->date,
|
||||
$transaction->foreign_amount
|
||||
);
|
||||
}
|
||||
|
||||
$transaction->saveQuietly();
|
||||
Log::debug(sprintf('Transaction #%d primary currency amounts are updated.', $transaction->id));
|
||||
}
|
||||
|
||||
public function deleting(?Transaction $transaction): void
|
||||
{
|
||||
Log::debug('Observe "deleting" of a transaction.');
|
||||
@@ -96,15 +49,29 @@ class TransactionObserver
|
||||
|
||||
public function updated(Transaction $transaction): void
|
||||
{
|
||||
// Log::debug('Observe "updated" of a transaction.');
|
||||
if (
|
||||
true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data
|
||||
&& self::$recalculate
|
||||
&& 1 === bccomp($transaction->amount, '0')
|
||||
) {
|
||||
Log::debug('Trigger recalculateForJournal');
|
||||
AccountBalanceCalculator::recalculateForJournal($transaction->transactionJournal);
|
||||
}
|
||||
$this->updatePrimaryCurrencyAmount($transaction);
|
||||
}
|
||||
|
||||
private function updatePrimaryCurrencyAmount(Transaction $transaction): void
|
||||
{
|
||||
// convert "amount" to "native_amount"
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $transaction->transactionJournal->user;
|
||||
$params->model = $transaction;
|
||||
$params->originalCurrency = $transaction->transactionCurrency;
|
||||
$params->amountField = 'amount';
|
||||
$params->date = $transaction->transactionJournal->date;
|
||||
$params->primaryAmountField = 'native_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
|
||||
// convert "foreign_amount" to "native_foreign_amount"
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $transaction->transactionJournal->user;
|
||||
$params->model = $transaction;
|
||||
$params->originalCurrency = $transaction->foreignCurrency;
|
||||
$params->date = $transaction->transactionJournal->date;
|
||||
$params->amountField = 'foreign_amount';
|
||||
$params->primaryAmountField = 'native_foreign_amount';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,36 +61,38 @@ class ConfigurationController extends Controller
|
||||
*/
|
||||
public function index(): Factory|\Illuminate\Contracts\View\View
|
||||
{
|
||||
$subTitle = (string) trans('firefly.instance_configuration');
|
||||
$subTitleIcon = 'fa-wrench';
|
||||
$subTitle = (string) trans('firefly.instance_configuration');
|
||||
$subTitleIcon = 'fa-wrench';
|
||||
|
||||
Log::channel('audit')->info('User visits admin config index.');
|
||||
|
||||
// all available configuration and their default value in case
|
||||
// they don't exist yet.
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
$siteOwner = config('firefly.site_owner');
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
$isDemoSite = FireflyConfig::get('is_demo_site', config('firefly.configuration.is_demo_site'))->data;
|
||||
$siteOwner = config('firefly.site_owner');
|
||||
|
||||
$enableExchangeRates = FireflyConfig::get('enable_exchange_rates', config('cer.enabled'))->data;
|
||||
$useRunningBalance = FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data;
|
||||
$enableExternalMap = FireflyConfig::get('enable_external_map', config('firefly.enable_external_map'))->data;
|
||||
$enableExternalRates = FireflyConfig::get('enable_external_rates', config('cer.download_enabled'))->data;
|
||||
$allowWebhooks = FireflyConfig::get('allow_webhooks', config('firefly.allow_webhooks'))->data;
|
||||
$validUrlProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
$enableExchangeRates = FireflyConfig::get('enable_exchange_rates', config('cer.enabled'))->data;
|
||||
$useRunningBalance = FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data;
|
||||
$enableExternalMap = FireflyConfig::get('enable_external_map', config('firefly.enable_external_map'))->data;
|
||||
$enableExternalRates = FireflyConfig::get('enable_external_rates', config('cer.download_enabled'))->data;
|
||||
$allowWebhooks = FireflyConfig::get('allow_webhooks', config('firefly.allow_webhooks'))->data;
|
||||
$enableBatchProcessing = FireflyConfig::get('enable_batch_processing', false)->data;
|
||||
$validUrlProtocols = FireflyConfig::get('valid_url_protocols', config('firefly.valid_url_protocols'))->data;
|
||||
|
||||
return view('settings.configuration.index', [
|
||||
'subTitle' => $subTitle,
|
||||
'subTitleIcon' => $subTitleIcon,
|
||||
'singleUserMode' => $singleUserMode,
|
||||
'isDemoSite' => $isDemoSite,
|
||||
'siteOwner' => $siteOwner,
|
||||
'enableExchangeRates' => $enableExchangeRates,
|
||||
'useRunningBalance' => $useRunningBalance,
|
||||
'enableExternalMap' => $enableExternalMap,
|
||||
'enableExternalRates' => $enableExternalRates,
|
||||
'allowWebhooks' => $allowWebhooks,
|
||||
'validUrlProtocols' => $validUrlProtocols,
|
||||
'subTitle' => $subTitle,
|
||||
'subTitleIcon' => $subTitleIcon,
|
||||
'singleUserMode' => $singleUserMode,
|
||||
'isDemoSite' => $isDemoSite,
|
||||
'siteOwner' => $siteOwner,
|
||||
'enableExchangeRates' => $enableExchangeRates,
|
||||
'useRunningBalance' => $useRunningBalance,
|
||||
'enableExternalMap' => $enableExternalMap,
|
||||
'enableExternalRates' => $enableExternalRates,
|
||||
'allowWebhooks' => $allowWebhooks,
|
||||
'enableBatchProcessing' => $enableBatchProcessing,
|
||||
'validUrlProtocols' => $validUrlProtocols,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Transaction;
|
||||
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\BulkEditJournalRequest;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
@@ -115,11 +117,15 @@ class BulkController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = new TransactionGroupEventObjects();
|
||||
|
||||
// run rules on changed journals:
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($collection as $journal) { // @phpstan-ignore-line
|
||||
event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, false));
|
||||
foreach ($collection as $journal) {
|
||||
$objects->appendFromTransactionGroup($journal->transactionGroup);
|
||||
}
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
|
||||
Preferences::mark();
|
||||
$request->session()->flash('success', trans_choice('firefly.mass_edited_transactions_success', $count));
|
||||
|
||||
@@ -26,7 +26,9 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
||||
use Exception;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
@@ -305,7 +307,9 @@ class ConvertController extends Controller
|
||||
$group->refresh();
|
||||
|
||||
session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type));
|
||||
event(new UpdatedTransactionGroup($group, true, true, true));
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($group);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Transaction;
|
||||
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
@@ -76,11 +74,6 @@ class CreateController extends Controller
|
||||
$service = app(GroupCloneService::class);
|
||||
$newGroup = $service->cloneGroup($group);
|
||||
|
||||
// event!
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
// event(new StoredTransactionGroup($newGroup, true, true));
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
$title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
|
||||
|
||||
@@ -26,7 +26,9 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\MassDeleteJournalRequest;
|
||||
@@ -194,15 +196,15 @@ class MassController extends Controller
|
||||
*/
|
||||
private function updateJournal(int $journalId, MassEditJournalRequest $request): void
|
||||
{
|
||||
$journal = $this->repository->find($journalId);
|
||||
$journal = $this->repository->find($journalId);
|
||||
if (!$journal instanceof TransactionJournal) {
|
||||
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId));
|
||||
}
|
||||
$service = app(JournalUpdateService::class);
|
||||
$service = app(JournalUpdateService::class);
|
||||
// for each field, call the update service.
|
||||
$service->setTransactionJournal($journal);
|
||||
|
||||
$data = [
|
||||
$data = [
|
||||
'date' => $this->getDateFromRequest($request, $journal->id, 'date'),
|
||||
'description' => $this->getStringFromRequest($request, $journal->id, 'description'),
|
||||
'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'),
|
||||
@@ -220,8 +222,11 @@ class MassController extends Controller
|
||||
$service->setData($data);
|
||||
$service->update();
|
||||
// trigger rules
|
||||
$runRecalculations = $service->isCompareHashChanged();
|
||||
event(new UpdatedTransactionGroup($journal->transactionGroup, true, true, $runRecalculations));
|
||||
$runRecalculations = $service->isCompareHashChanged();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->recalculateCredit = $runRecalculations;
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
}
|
||||
|
||||
private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon
|
||||
|
||||
@@ -41,14 +41,15 @@ class ConfigurationRequest extends FormRequest
|
||||
public function getConfigurationData(): array
|
||||
{
|
||||
return [
|
||||
'single_user_mode' => $this->boolean('single_user_mode'),
|
||||
'enable_exchange_rates' => $this->boolean('enable_exchange_rates'),
|
||||
'use_running_balance' => $this->boolean('use_running_balance'),
|
||||
'enable_external_map' => $this->boolean('enable_external_map'),
|
||||
'enable_external_rates' => $this->boolean('enable_external_rates'),
|
||||
'allow_webhooks' => $this->boolean('allow_webhooks'),
|
||||
'valid_url_protocols' => $this->string('valid_url_protocols'),
|
||||
'is_demo_site' => $this->boolean('is_demo_site'),
|
||||
'single_user_mode' => $this->boolean('single_user_mode'),
|
||||
'enable_exchange_rates' => $this->boolean('enable_exchange_rates'),
|
||||
'use_running_balance' => $this->boolean('use_running_balance'),
|
||||
'enable_external_map' => $this->boolean('enable_external_map'),
|
||||
'enable_external_rates' => $this->boolean('enable_external_rates'),
|
||||
'allow_webhooks' => $this->boolean('allow_webhooks'),
|
||||
'valid_url_protocols' => $this->string('valid_url_protocols'),
|
||||
'is_demo_site' => $this->boolean('is_demo_site'),
|
||||
'enable_batch_processing' => $this->boolean('enable_batch_processing'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -59,14 +60,15 @@ class ConfigurationRequest extends FormRequest
|
||||
{
|
||||
// fixed
|
||||
return [
|
||||
'single_user_mode' => 'min:0|max:1|numeric',
|
||||
'enable_exchange_rates' => 'min:0|max:1|numeric',
|
||||
'use_running_balance' => 'min:0|max:1|numeric',
|
||||
'enable_external_map' => 'min:0|max:1|numeric',
|
||||
'enable_external_rates' => 'min:0|max:1|numeric',
|
||||
'allow_webhooks' => 'min:0|max:1|numeric',
|
||||
'valid_url_protocols' => 'min:0|max:255',
|
||||
'is_demo_site' => 'min:0|max:1|numeric',
|
||||
'single_user_mode' => 'min:0|max:1|numeric',
|
||||
'enable_exchange_rates' => 'min:0|max:1|numeric',
|
||||
'use_running_balance' => 'min:0|max:1|numeric',
|
||||
'enable_external_map' => 'min:0|max:1|numeric',
|
||||
'enable_external_rates' => 'min:0|max:1|numeric',
|
||||
'allow_webhooks' => 'min:0|max:1|numeric',
|
||||
'enable_batch_processing' => 'min:0|max:1|numeric',
|
||||
'valid_url_protocols' => 'min:0|max:255',
|
||||
'is_demo_site' => 'min:0|max:1|numeric',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupsRequestedReporting;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -336,7 +334,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d')));
|
||||
|
||||
// count created journals on THIS day.
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
if ($journalCount > 0 && false === $this->force) {
|
||||
Log::info(sprintf('Already created %d journal(s) for date %s', $journalCount, $date->format('Y-m-d')));
|
||||
|
||||
@@ -354,11 +352,11 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
}
|
||||
|
||||
// create transaction array and send to factory.
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
// #8844, if there is one recurrence transaction, use the first title as the title.
|
||||
// #9305, if there is one recurrence transaction, group title must be NULL.
|
||||
$groupTitle = null;
|
||||
$groupTitle = null;
|
||||
|
||||
// #8844, if there are more, use the recurrence transaction itself.
|
||||
if ($count > 1) {
|
||||
@@ -371,7 +369,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
return null;
|
||||
}
|
||||
|
||||
$array = [
|
||||
$array = [
|
||||
'user' => $recurrence->user,
|
||||
'user_group' => $recurrence->user->userGroup,
|
||||
'group_title' => $groupTitle,
|
||||
@@ -379,21 +377,13 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
];
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
$group = $this->groupRepository->store($array);
|
||||
$group = $this->groupRepository->store($array);
|
||||
++$this->created;
|
||||
Log::info(sprintf('Created new transaction group #%d', $group->id));
|
||||
|
||||
// trigger event:
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = $recurrence->apply_rules;
|
||||
event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
// event(new StoredTransactionGroup($group, $recurrence->apply_rules, true));
|
||||
$this->groups->push($group);
|
||||
|
||||
// update recurring thing:
|
||||
$recurrence->latest_date = $date;
|
||||
$recurrence->latest_date_tz = $date->format('e');
|
||||
$recurrence->save();
|
||||
$this->repository->setLatestDate($recurrence, $date);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
74
app/Listeners/Model/Account/UpdatesAccountInformation.php
Normal file
74
app/Listeners/Model/Account/UpdatesAccountInformation.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* TriggersCreditRecalculation.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Listeners\Model\Account;
|
||||
|
||||
use FireflyIII\Events\Model\Account\CreatedNewAccount;
|
||||
use FireflyIII\Events\Model\Account\UpdatedExistingAccount;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConversionParameters;
|
||||
use FireflyIII\Handlers\ExchangeRate\ConvertsAmountToPrimaryAmount;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UpdatesAccountInformation implements ShouldQueue
|
||||
{
|
||||
public function handle(CreatedNewAccount|UpdatedExistingAccount $event): void
|
||||
{
|
||||
$this->recalculateCredit($event->account);
|
||||
$this->updateVirtualBalance($event->account);
|
||||
}
|
||||
|
||||
private function recalculateCredit(Account $account): void
|
||||
{
|
||||
Log::debug('Will call CreditRecalculateService because a new account was created or updated.');
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setAccount($account);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function updateVirtualBalance(Account $account): void
|
||||
{
|
||||
Log::debug('Will updateVirtualBalance');
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$currency = $repository->getAccountCurrency($account);
|
||||
|
||||
if (null !== $currency) {
|
||||
// only when the account has a currency, because that is the only way for the
|
||||
// account to have a virtual balance.
|
||||
$params = new ConversionParameters();
|
||||
$params->user = $account->user;
|
||||
$params->model = $account;
|
||||
$params->originalCurrency = $currency;
|
||||
$params->amountField = 'virtual_balance';
|
||||
$params->primaryAmountField = 'native_virtual_balance';
|
||||
ConvertsAmountToPrimaryAmount::convert($params);
|
||||
Log::debug('Account primary currency virtual balance is updated.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,11 @@ namespace FireflyIII\Listeners\Model\Rule;
|
||||
|
||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray;
|
||||
use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\RuleActionFailed;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutFailedRuleAction implements ShouldQueue
|
||||
{
|
||||
@@ -58,11 +57,6 @@ class NotifiesUserAboutFailedRuleAction implements ShouldQueue
|
||||
$ruleTitle = $rule->title;
|
||||
$ruleLink = route('rules.edit', [$rule->id]);
|
||||
$params = [$mainMessage, $groupTitle, $groupLink, $ruleTitle, $ruleLink];
|
||||
|
||||
try {
|
||||
Notification::send($user, new RuleActionFailed($params));
|
||||
} catch (ClientException $e) {
|
||||
Log::error(sprintf('[a] Error sending notification that the rule action failed: %s', $e->getMessage()));
|
||||
}
|
||||
NotificationSender::send($user, new RuleActionFailed($params));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\Subscription;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionNeedsExtensionOrRenewal;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\BillReminder;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesAboutExtensionOrRenewal implements ShouldQueue
|
||||
{
|
||||
@@ -44,24 +43,7 @@ class NotifiesAboutExtensionOrRenewal implements ShouldQueue
|
||||
|
||||
if (true === $preference) {
|
||||
Log::debug('Subscription reminder is true!');
|
||||
|
||||
try {
|
||||
Notification::send($subscription->user, new BillReminder($subscription, $event->field, $event->diff));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($subscription->user, new BillReminder($subscription, $event->field, $event->diff));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,14 +24,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\Subscription;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Model\Subscription\SubscriptionsAreOverdueForPayment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\SubscriptionsOverdueReminder;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesAboutOverdueSubscriptions implements ShouldQueue
|
||||
{
|
||||
@@ -78,23 +77,6 @@ class NotifiesAboutOverdueSubscriptions implements ShouldQueue
|
||||
Preferences::setForUser($bill->user, $key, true);
|
||||
}
|
||||
Log::warning('should hit this ONCE');
|
||||
|
||||
try {
|
||||
Notification::send($user, new SubscriptionsOverdueReminder($toBeWarned));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new SubscriptionsOverdueReminder($toBeWarned));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupsRequestedReporting;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\TransactionCreation;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class MailsNewTransactionsReport implements ShouldQueue
|
||||
{
|
||||
@@ -71,23 +70,7 @@ class MailsNewTransactionsReport implements ShouldQueue
|
||||
$groups[] = $transformer->transformObject($group);
|
||||
}
|
||||
|
||||
try {
|
||||
Notification::send($user, new TransactionCreation($groups));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new TransactionCreation($groups));
|
||||
Log::debug('If there is no error above this line, message was sent.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* DestroyedGroupEventHandler.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
* ProcessesDestroyedTransactionGroup.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -20,31 +22,27 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\DestroyedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class DestroyedGroupEventHandler
|
||||
*/
|
||||
class DestroyedGroupEventHandler
|
||||
class ProcessesDestroyedTransactionGroup implements ShouldQueue
|
||||
{
|
||||
public function runAllHandlers(DestroyedTransactionGroup $event): void
|
||||
public function handle(DestroyedSingleTransactionGroup $event): void
|
||||
{
|
||||
$this->triggerWebhooks($event);
|
||||
$this->updateRunningBalance($event);
|
||||
}
|
||||
|
||||
private function triggerWebhooks(DestroyedTransactionGroup $destroyedGroupEvent): void
|
||||
private function triggerWebhooks(DestroyedSingleTransactionGroup $destroyedGroupEvent): void
|
||||
{
|
||||
Log::debug('DestroyedTransactionGroup:triggerWebhooks');
|
||||
$group = $destroyedGroupEvent->transactionGroup;
|
||||
@@ -60,7 +58,7 @@ class DestroyedGroupEventHandler
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
|
||||
private function updateRunningBalance(DestroyedTransactionGroup $event): void
|
||||
private function updateRunningBalance(DestroyedSingleTransactionGroup $event): void
|
||||
{
|
||||
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
return;
|
||||
@@ -24,170 +24,54 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UserRequestedBatchProcessing;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProcessesNewTransactionGroup implements ShouldQueue
|
||||
{
|
||||
public function handle(CreatedSingleTransactionGroup $event): void
|
||||
use SupportsGroupProcessingTrait;
|
||||
|
||||
public function handle(CreatedSingleTransactionGroup|UserRequestedBatchProcessing $event): void
|
||||
{
|
||||
Log::debug(sprintf('In ProcessesNewTransactionGroup::handle(#%d)', $event->transactionGroup->id));
|
||||
if (true === $event->flags->batchSubmission) {
|
||||
Log::debug(sprintf('Will do nothing for group #%d because it is part of a batch.', $event->transactionGroup->id));
|
||||
Log::debug(sprintf('User called %s', get_class($event)));
|
||||
|
||||
$setting = FireflyConfig::get('enable_batch_processing', false)->data;
|
||||
if (true === $event->flags->batchSubmission && true === $setting) {
|
||||
Log::debug('Will do nothing for event because it is part of a batch.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Will join group #%d with all other open transaction groups and process them.', $event->transactionGroup->id));
|
||||
$collection = $event->transactionGroup->transactionJournals;
|
||||
Log::debug('Will also collect all open transaction groups and process them as well.');
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$set = $collection->merge($repository->getUncompletedJournals());
|
||||
if (0 === $set->count()) {
|
||||
Log::debug('Set is empty, never mind.');
|
||||
$journals = $event->objects->transactionJournals->merge($repository->getAllUncompletedJournals());
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Transaction journal count is %d', $journals->count()));
|
||||
if (!$event->flags->applyRules) {
|
||||
Log::debug(sprintf('Will NOT process rules for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT process rules for %d journal(s)', $journals->count()));
|
||||
}
|
||||
if (!$event->flags->recalculateCredit) {
|
||||
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $journals->count()));
|
||||
}
|
||||
if (!$event->flags->fireWebhooks) {
|
||||
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $journals->count()));
|
||||
}
|
||||
|
||||
if ($event->flags->applyRules) {
|
||||
$this->processRules($set);
|
||||
$this->processRules($journals, 'store-journal');
|
||||
}
|
||||
if ($event->flags->recalculateCredit) {
|
||||
$this->recalculateCredit($set);
|
||||
$this->recalculateCredit($event->objects->accounts);
|
||||
}
|
||||
if ($event->flags->fireWebhooks) {
|
||||
$this->fireWebhooks($set);
|
||||
$this->fireWebhooks($journals, WebhookTrigger::STORE_TRANSACTION);
|
||||
}
|
||||
// always remove old statistics.
|
||||
$this->removePeriodStatistics($set);
|
||||
|
||||
// recalculate running balance if necessary.
|
||||
|
||||
Log::debug('Observe "created" of a transaction.');
|
||||
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
$this->recalculateRunningBalance($set);
|
||||
}
|
||||
|
||||
$repository->markAsCompleted($set);
|
||||
}
|
||||
|
||||
private function recalculateRunningBalance(Collection $set): void
|
||||
{
|
||||
Log::debug('Now in recalculateRunningBalance');
|
||||
// find the earliest date in the set, based on date and _internal_previous_date
|
||||
$earliest = $set->pluck('date')->sort()->first();
|
||||
$entries = TransactionJournalMeta::whereIn('transaction_journal_id', $set->pluck('id')->toArray())->where('name', '_internal_previous_date')->get([
|
||||
'journal_meta.*',
|
||||
]);
|
||||
$array = $entries->toArray();
|
||||
if (count($array) > 0) {
|
||||
usort($array, function (array $a, array $b) {
|
||||
return Carbon::parse($a['data'])->gt(Carbon::parse($b['data']));
|
||||
});
|
||||
|
||||
/** @var Carbon $date */
|
||||
$date = Carbon::parse($array[0]['data']);
|
||||
$earliest = $date->lt($earliest) ? $date : $earliest;
|
||||
}
|
||||
|
||||
// get accounts
|
||||
$accounts = Account::leftJoin('transactions', 'transactions.account_id', 'accounts.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
|
||||
->whereIn('transaction_journals.id', $set->pluck('id')->toArray())
|
||||
->get(['accounts.*'])
|
||||
;
|
||||
|
||||
AccountBalanceCalculator::optimizedCalculation($accounts, $earliest);
|
||||
}
|
||||
|
||||
private function removePeriodStatistics(Collection $set): void
|
||||
{
|
||||
Log::debug('Always remove period statistics');
|
||||
|
||||
/** @var PeriodStatisticRepositoryInterface $repository */
|
||||
$repository = app(PeriodStatisticRepositoryInterface::class);
|
||||
$repository->deleteStatisticsForCollection($set);
|
||||
}
|
||||
|
||||
private function fireWebhooks(Collection $set): void
|
||||
{
|
||||
// collect transaction groups by set ids.
|
||||
$groups = TransactionGroup::whereIn('id', array_unique($set->pluck('transaction_group_id')->toArray()))->get();
|
||||
|
||||
Log::debug(__METHOD__);
|
||||
$user = $set->first()->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
|
||||
// tell the generator which trigger it should look for
|
||||
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
|
||||
// tell the generator which objects to process
|
||||
$engine->setObjects($groups);
|
||||
// tell the generator to generate the messages
|
||||
$engine->generateMessages();
|
||||
|
||||
// trigger event to send them:
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
|
||||
private function recalculateCredit(Collection $set): void
|
||||
{
|
||||
Log::debug(sprintf('Will now recalculateCredit for %d journal(s)', $set->count()));
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setJournals($set);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function processRules(Collection $set): void
|
||||
{
|
||||
Log::debug(sprintf('Will now processRules for %d journal(s)', $set->count()));
|
||||
$array = $set->pluck('id')->toArray();
|
||||
$journalIds = implode(',', $array);
|
||||
$user = $set->first()->user;
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$ruleGroupRepository->setUser($user);
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
Log::debug('Fire processRules with ALL store-journal rule groups.');
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
$this->removePeriodStatistics($event->objects);
|
||||
$this->recalculateRunningBalance($event->objects);
|
||||
$repository->markAsCompleted($journals);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* ProcessesUpdatedTransactionGroup.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProcessesUpdatedTransactionGroup
|
||||
{
|
||||
use SupportsGroupProcessingTrait;
|
||||
|
||||
public function handle(UpdatedSingleTransactionGroup $event): void
|
||||
{
|
||||
Log::debug(sprintf('User called %s', get_class($event)));
|
||||
$this->unifyAccounts($event);
|
||||
|
||||
Log::debug(sprintf('Transaction journal count is %d', $event->objects->transactionJournals->count()));
|
||||
if (!$event->flags->applyRules) {
|
||||
Log::debug(sprintf('Will NOT process rules for %d journal(s)', $event->objects->transactionJournals->count()));
|
||||
}
|
||||
if (!$event->flags->recalculateCredit) {
|
||||
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $event->objects->transactionJournals->count()));
|
||||
}
|
||||
if (!$event->flags->fireWebhooks) {
|
||||
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $event->objects->transactionJournals->count()));
|
||||
}
|
||||
|
||||
if ($event->flags->applyRules) {
|
||||
$this->processRules($event->objects->transactionJournals, 'update-journal');
|
||||
}
|
||||
if ($event->flags->recalculateCredit) {
|
||||
$this->recalculateCredit($event->objects->accounts);
|
||||
}
|
||||
if ($event->flags->fireWebhooks) {
|
||||
$this->fireWebhooks($event->objects->transactionJournals, WebhookTrigger::UPDATE_TRANSACTION);
|
||||
}
|
||||
$this->removePeriodStatistics($event->objects);
|
||||
$this->recalculateRunningBalance($event->objects);
|
||||
|
||||
Log::debug('Done with handle() for UpdatedSingleTransactionGroup');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will make sure all source / destination accounts are the same.
|
||||
*/
|
||||
protected function unifyAccounts(UpdatedSingleTransactionGroup $updatedGroupEvent): void
|
||||
{
|
||||
Log::debug('Now in unifyAccounts()');
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
foreach ($updatedGroupEvent->objects->transactionGroups as $group) {
|
||||
$this->unifyAccountsForGroup($group);
|
||||
}
|
||||
Log::debug('Done with unifyAccounts()');
|
||||
}
|
||||
|
||||
private function unifyAccountsForGroup(TransactionGroup $group): void
|
||||
{
|
||||
if (1 === $group->transactionJournals->count()) {
|
||||
Log::debug('Nothing to do in unifyAccounts()');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// first journal:
|
||||
/** @var null|TransactionJournal $first */
|
||||
$first = $group
|
||||
->transactionJournals()
|
||||
->orderBy('transaction_journals.date', 'DESC')
|
||||
->orderBy('transaction_journals.order', 'ASC')
|
||||
->orderBy('transaction_journals.id', 'DESC')
|
||||
->orderBy('transaction_journals.description', 'DESC')
|
||||
->first()
|
||||
;
|
||||
|
||||
if (null === $first) {
|
||||
Log::warning(sprintf('Group #%d has no transaction journals.', $group->id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$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;
|
||||
|
||||
$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]);
|
||||
}
|
||||
if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::DEPOSIT->value === $type) {
|
||||
// set all destination transactions to destination account:
|
||||
Transaction::whereIn('transaction_journal_id', $all)->where('amount', '>', 0)->update(['account_id' => $destAccount->id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Support\CreditRecalculateService;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\Support\Models\AccountBalanceCalculator;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
trait SupportsGroupProcessingTrait
|
||||
{
|
||||
private function recalculateCredit(Collection $accounts): void
|
||||
{
|
||||
Log::debug(sprintf('Will now recalculateCredit for %d account(s)', $accounts->count()));
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setAccounts($accounts);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function fireWebhooks(Collection $journals, WebhookTrigger $trigger): void
|
||||
{
|
||||
// collect transaction groups by set ids.
|
||||
$groups = TransactionGroup::whereIn('id', array_unique($journals->pluck('transaction_group_id')->toArray()))->get();
|
||||
|
||||
Log::debug(__METHOD__);
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $journals->first();
|
||||
$user = $first->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
|
||||
// tell the generator which trigger it should look for
|
||||
$engine->setTrigger($trigger);
|
||||
// tell the generator which objects to process
|
||||
$engine->setObjects($groups);
|
||||
// tell the generator to generate the messages
|
||||
$engine->generateMessages();
|
||||
}
|
||||
|
||||
protected function removePeriodStatistics(TransactionGroupEventObjects $objects): void
|
||||
{
|
||||
if (auth()->check()) {
|
||||
// since you get a bunch of journals AND a bunch of
|
||||
// objects, this needs to be a collection
|
||||
/** @var PeriodStatisticRepositoryInterface $repository */
|
||||
$repository = app(PeriodStatisticRepositoryInterface::class);
|
||||
$dates = $this->collectDatesFromJournals($objects->transactionJournals);
|
||||
$repository->deleteStatisticsForType(Account::class, $objects->accounts, $dates);
|
||||
$repository->deleteStatisticsForType(Budget::class, $objects->budgets, $dates);
|
||||
$repository->deleteStatisticsForType(Category::class, $objects->categories, $dates);
|
||||
$repository->deleteStatisticsForType(Tag::class, $objects->tags, $dates);
|
||||
|
||||
// remove if no stuff present:
|
||||
// remove for no tag, no cat, etc.
|
||||
if (0 === $objects->budgets->count()) {
|
||||
Log::debug('No budgets, delete "no_category" stats.');
|
||||
$repository->deleteStatisticsForPrefix('no_budget', $dates);
|
||||
}
|
||||
if (0 === $objects->categories->count()) {
|
||||
Log::debug('No categories, delete "no_category" stats.');
|
||||
$repository->deleteStatisticsForPrefix('no_category', $dates);
|
||||
}
|
||||
if (0 === $objects->tags->count()) {
|
||||
Log::debug('No tags, delete "no_category" stats.');
|
||||
$repository->deleteStatisticsForPrefix('no_tag', $dates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function collectDatesFromJournals(Collection $journals): Collection
|
||||
{
|
||||
$collection = $journals->pluck('date');
|
||||
if (0 === count($collection)) {
|
||||
$collection->push(now(config('app.timezone')));
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
protected function processRules(Collection $set, string $type): void
|
||||
{
|
||||
Log::debug(sprintf('Will now processRules("%s") for %d journal(s)', $type, $set->count()));
|
||||
$array = $set->pluck('id')->toArray();
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $set->first();
|
||||
$journalIds = implode(',', $array);
|
||||
$user = $first->user;
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$ruleGroupRepository->setUser($user);
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
Log::debug(sprintf('Fire processRules with ALL %s rule groups.', $type));
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules($type);
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
protected function recalculateRunningBalance(TransactionGroupEventObjects $objects): void
|
||||
{
|
||||
Log::debug('Now in recalculateRunningBalance');
|
||||
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
Log::debug('Running balance is disabled.');
|
||||
|
||||
return;
|
||||
}
|
||||
// find the earliest date in the set, based on date and _internal_previous_date
|
||||
$earliest = $objects->transactionJournals->pluck('date')->sort()->first();
|
||||
$fromInternalDate = $this->getFromInternalDate($objects->transactionJournals->pluck('id')->toArray());
|
||||
$earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest;
|
||||
Log::debug(sprintf('Found earliest date: %s', $earliest->toW3cString()));
|
||||
|
||||
Log::debug('Found accounts to process', $objects->accounts->pluck('id')->toArray());
|
||||
|
||||
AccountBalanceCalculator::optimizedCalculation($objects->accounts, $earliest);
|
||||
}
|
||||
|
||||
private function getFromInternalDate(array $ids): Carbon
|
||||
{
|
||||
$entries = TransactionJournalMeta::whereIn('transaction_journal_id', $ids)->where('name', '_internal_previous_date')->get(['journal_meta.*']);
|
||||
$array = $entries->toArray();
|
||||
$return = today()->subDay();
|
||||
if (count($array) > 0) {
|
||||
usort($array, function (array $a, array $b) {
|
||||
return Carbon::parse($a['data'])->gt(Carbon::parse($b['data']));
|
||||
});
|
||||
|
||||
$date = Carbon::parse($array[0]['data']);
|
||||
$return = $date->lt($return) ? $date : $return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Listeners\Security\System;
|
||||
|
||||
use Database\Seeders\ExchangeRateSeeder;
|
||||
use Exception;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Events\Security\System\NewUserRegistered;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -34,13 +33,13 @@ use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Models\UserRole;
|
||||
use FireflyIII\Notifications\Admin\UserRegistration as AdminRegistrationNotification;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\UserRegistration as UserRegistrationNotification;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class HandlesNewUserRegistration implements ShouldQueue
|
||||
{
|
||||
@@ -117,24 +116,7 @@ class HandlesNewUserRegistration implements ShouldQueue
|
||||
if (!$sendMail) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Notification::send($owner, new AdminRegistrationNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($owner, new AdminRegistrationNotification($user));
|
||||
}
|
||||
|
||||
private function sendRegistrationMail(User $user): void
|
||||
@@ -144,22 +126,6 @@ class HandlesNewUserRegistration implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Notification::send($user, new UserRegistrationNotification());
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new UserRegistrationNotification());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,11 +31,11 @@ use FireflyIII\Mail\InvitationMail;
|
||||
use FireflyIII\Models\InvitedUser;
|
||||
use FireflyIII\Notifications\Admin\UserInvitation;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesAboutNewInvitation implements ShouldQueue
|
||||
{
|
||||
@@ -68,22 +68,6 @@ class NotifiesAboutNewInvitation implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Notification::send(new OwnerNotifiable(), new UserInvitation($invitee));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send(new OwnerNotifiable(), new UserInvitation($invitee));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\System;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\System\SystemFoundNewVersionOnline;
|
||||
use FireflyIII\Notifications\Admin\VersionCheckResult;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Support\Facades\FireflyConfig;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesOwnerAboutNewVersion implements ShouldQueue
|
||||
{
|
||||
@@ -42,23 +40,7 @@ class NotifiesOwnerAboutNewVersion implements ShouldQueue
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$owner = new OwnerNotifiable();
|
||||
Notification::send($owner, new VersionCheckResult($event->message));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
$owner = new OwnerNotifiable();
|
||||
NotificationSender::send($owner, new VersionCheckResult($event->message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,35 +24,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\System;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\System\UnknownUserTriedLogin;
|
||||
use FireflyIII\Notifications\Admin\UnknownUserLoginAttempt;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesOwnerAboutUnknownUser implements ShouldQueue
|
||||
{
|
||||
public function handle(UnknownUserTriedLogin $event): void
|
||||
{
|
||||
try {
|
||||
$owner = new OwnerNotifiable();
|
||||
Notification::send($owner, new UnknownUserLoginAttempt($event->address));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
$owner = new OwnerNotifiable();
|
||||
NotificationSender::send($owner, new UnknownUserLoginAttempt($event->address));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,37 +24,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasDisabledMFA;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\DisabledMFANotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutDisabledMFA implements ShouldQueue
|
||||
{
|
||||
public function handle(UserHasDisabledMFA $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new DisabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($event->user, new DisabledMFANotification($event->user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasEnabledMFA;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\EnabledMFANotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutEnabledMFA implements ShouldQueue
|
||||
{
|
||||
@@ -38,23 +37,6 @@ class NotifiesUserAboutEnabledMFA implements ShouldQueue
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new EnabledMFANotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new EnabledMFANotification($user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,32 +24,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserFailedLoginAttempt;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\UserFailedLoginAttempt as NotificationFailedLoginAttempt;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutFailedLogin implements ShouldQueue
|
||||
{
|
||||
public function handle(UserFailedLoginAttempt $event): void
|
||||
{
|
||||
try {
|
||||
Notification::send($event->user, new \FireflyIII\Notifications\Security\UserFailedLoginAttempt($event->user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($event->user, new NotificationFailedLoginAttempt($event->user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasFewMFABackupCodesLeft;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\MFABackupFewLeftNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutFewCodesLeft implements ShouldQueue
|
||||
{
|
||||
@@ -39,23 +38,6 @@ class NotifiesUserAboutFewCodesLeft implements ShouldQueue
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupFewLeftNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new MFABackupFewLeftNotification($user, $count));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* NotifiesUserAboutNewAccessToken.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\NewAccessToken;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Laravel\Passport\Events\AccessTokenCreated;
|
||||
|
||||
class NotifiesUserAboutNewAccessToken
|
||||
{
|
||||
public function handle(AccessTokenCreated $event): void
|
||||
{
|
||||
/** @var UserRepositoryInterface $repository */
|
||||
$repository = app(UserRepositoryInterface::class);
|
||||
$user = $repository->find((int) $event->userId);
|
||||
|
||||
if (null !== $user) {
|
||||
NotificationSender::send($user, new NewAccessToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,37 +24,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasGeneratedNewBackupCodes;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\NewBackupCodesNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutNewBackupCodes implements ShouldQueue
|
||||
{
|
||||
public function handle(UserHasGeneratedNewBackupCodes $event): void
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new NewBackupCodesNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new NewBackupCodesNotification($user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,13 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserLoggedInFromNewIpAddress;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\UserLogin;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutNewIpAddress implements ShouldQueue
|
||||
{
|
||||
@@ -50,23 +48,7 @@ class NotifiesUserAboutNewIpAddress implements ShouldQueue
|
||||
/** @var array $entry */
|
||||
foreach ($list as $index => $entry) {
|
||||
if (false === $entry['notified']) {
|
||||
try {
|
||||
Notification::send($user, new UserLogin());
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new UserLogin());
|
||||
}
|
||||
$list[$index]['notified'] = true;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasNoMFABackupCodesLeft;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\MFABackupNoLeftNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutNoCodesLeft implements ShouldQueue
|
||||
{
|
||||
@@ -39,22 +38,6 @@ class NotifiesUserAboutNoCodesLeft implements ShouldQueue
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFABackupNoLeftNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new MFABackupNoLeftNotification($user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserKeepsFailingMFA;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\MFAManyFailedAttemptsNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutRepeatedMFAFailures implements ShouldQueue
|
||||
{
|
||||
@@ -39,23 +38,6 @@ class NotifiesUserAboutRepeatedMFAFailures implements ShouldQueue
|
||||
|
||||
$user = $event->user;
|
||||
$count = $event->count;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAManyFailedAttemptsNotification($user, $count));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new MFAManyFailedAttemptsNotification($user, $count));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserHasUsedBackupCode;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Security\MFAUsedBackupCodeNotification;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class NotifiesUserAboutUsedBackupCode implements ShouldQueue
|
||||
{
|
||||
@@ -38,23 +37,6 @@ class NotifiesUserAboutUsedBackupCode implements ShouldQueue
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
|
||||
$user = $event->user;
|
||||
|
||||
try {
|
||||
Notification::send($user, new MFAUsedBackupCodeNotification($user));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($user, new MFAUsedBackupCodeNotification($user));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,33 +24,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Security\User;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Security\User\UserRequestedNewPassword;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\User\UserNewPassword;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class SendsUserNewPassword implements ShouldQueue
|
||||
{
|
||||
public function handle(UserRequestedNewPassword $event): void
|
||||
{
|
||||
try {
|
||||
Notification::send($event->user, new UserNewPassword(route('password.reset', [$event->token])));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($event->user, new UserNewPassword(route('password.reset', [$event->token])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* PreferencesEventHandler.php
|
||||
* Copyright (c) 2024 james@firefly-iii.org.
|
||||
* RecalculatesPrimaryCurrencyAmounts.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
@@ -17,12 +19,10 @@
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
namespace FireflyIII\Listeners\System;
|
||||
|
||||
use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency;
|
||||
use FireflyIII\Models\Budget;
|
||||
@@ -30,15 +30,15 @@ use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Recalculate\PrimaryAmountRecalculationService;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PreferencesEventHandler
|
||||
class RecalculatesPrimaryCurrencyAmounts
|
||||
{
|
||||
public function resetPrimaryCurrencyAmounts(UserGroupChangedPrimaryCurrency $event): void
|
||||
public function handle(UserGroupChangedPrimaryCurrency $event): void
|
||||
{
|
||||
// Reset the primary currency amounts for all objects that have it.
|
||||
Log::debug('Resetting primary currency amounts for all objects.');
|
||||
@@ -50,79 +50,67 @@ class PreferencesEventHandler
|
||||
'bills' => ['native_amount_min', 'native_amount_max'],
|
||||
];
|
||||
foreach ($tables as $table => $columns) {
|
||||
Log::debug(sprintf('Now processing table "%s"', $table));
|
||||
foreach ($columns as $column) {
|
||||
Log::debug(sprintf('Resetting column %s in table %s.', $column, $table));
|
||||
Log::debug(sprintf('Resetting column "%s" in table "%s".', $column, $table));
|
||||
DB::table($table)->where('user_group_id', $event->userGroup->id)->update([$column => null]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->resetPiggyBanks($event->userGroup);
|
||||
$this->resetBudgets($event->userGroup);
|
||||
$this->resetTransactions($event->userGroup);
|
||||
Log::debug('Have now reset all primary amounts to NULL.');
|
||||
// fire laravel command to recalculate them all.
|
||||
if (Amount::convertToPrimary()) {
|
||||
Log::debug('Will now convert to primary currency.');
|
||||
Artisan::call('correction:recalculate-pc-amounts');
|
||||
Log::debug('Will now convert amounts to primary currency.');
|
||||
|
||||
$calculator = new PrimaryAmountRecalculationService();
|
||||
$calculator->recalculate();
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug('Will NOT convert to primary currency.');
|
||||
}
|
||||
|
||||
private function resetPiggyBanks(UserGroup $userGroup): void
|
||||
private function resetBudget(Budget $budget): void
|
||||
{
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUserGroup($userGroup);
|
||||
$piggyBanks = $repository->getPiggyBanks();
|
||||
Log::debug(sprintf('Resetting %d piggy bank(s).', $piggyBanks->count()));
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
foreach ($piggyBanks as $piggyBank) {
|
||||
if (null !== $piggyBank->native_target_amount) {
|
||||
Log::debug(sprintf('Resetting native_target_amount for piggy bank #%d.', $piggyBank->id));
|
||||
$piggyBank->native_target_amount = null;
|
||||
$piggyBank->saveQuietly();
|
||||
foreach ($budget->autoBudgets as $autoBudget) {
|
||||
if ('' === (string) $autoBudget->native_amount) {
|
||||
continue;
|
||||
}
|
||||
foreach ($piggyBank->accounts as $account) {
|
||||
if (null !== $account->pivot->native_current_amount) {
|
||||
Log::debug(sprintf('Resetting native_current_amount for piggy bank #%d and account #%d.', $piggyBank->id, $account->id));
|
||||
$account->pivot->native_current_amount = null;
|
||||
$account->pivot->save();
|
||||
}
|
||||
}
|
||||
foreach ($piggyBank->piggyBankEvents as $event) {
|
||||
if (null !== $event->native_amount) {
|
||||
Log::debug(sprintf('Resetting native_amount for piggy bank #%d and event #%d.', $piggyBank->id, $event->id));
|
||||
$event->native_amount = null;
|
||||
$event->saveQuietly();
|
||||
}
|
||||
Log::debug(sprintf('Resetting native_amount for budget #%d and auto budget #%d.', $budget->id, $autoBudget->id));
|
||||
$autoBudget->native_amount = null;
|
||||
$autoBudget->saveQuietly();
|
||||
}
|
||||
foreach ($budget->budgetlimits as $limit) {
|
||||
if ('' !== (string) $limit->native_amount) {
|
||||
Log::debug(sprintf('Resetting native_amount for budget #%d and budget limit #%d.', $budget->id, $limit->id));
|
||||
$limit->native_amount = null;
|
||||
$limit->saveQuietly();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function resetBudgets(UserGroup $userGroup): void
|
||||
private function resetPiggyBank(PiggyBank $piggyBank): void
|
||||
{
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUserGroup($userGroup);
|
||||
$set = $repository->getBudgets();
|
||||
|
||||
Log::debug(sprintf('Resetting %d budget(s).', $set->count()));
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
foreach ($budget->autoBudgets as $autoBudget) {
|
||||
if (null === $autoBudget->native_amount) {
|
||||
continue;
|
||||
}
|
||||
Log::debug(sprintf('Resetting native_amount for budget #%d and auto budget #%d.', $budget->id, $autoBudget->id));
|
||||
$autoBudget->native_amount = null;
|
||||
$autoBudget->saveQuietly();
|
||||
if ('' !== (string) $piggyBank->native_target_amount) {
|
||||
Log::debug(sprintf('Resetting native_target_amount for piggy bank #%d.', $piggyBank->id));
|
||||
$piggyBank->native_target_amount = null;
|
||||
$piggyBank->saveQuietly();
|
||||
}
|
||||
foreach ($piggyBank->accounts as $account) {
|
||||
if ('' !== (string) $account->pivot->native_current_amount) {
|
||||
Log::debug(sprintf('Resetting native_current_amount for piggy bank #%d and account #%d.', $piggyBank->id, $account->id));
|
||||
$account->pivot->native_current_amount = null;
|
||||
$account->pivot->save();
|
||||
}
|
||||
foreach ($budget->budgetlimits as $limit) {
|
||||
if (null !== $limit->native_amount) {
|
||||
Log::debug(sprintf('Resetting native_amount for budget #%d and budget limit #%d.', $budget->id, $limit->id));
|
||||
$limit->native_amount = null;
|
||||
$limit->saveQuietly();
|
||||
}
|
||||
}
|
||||
foreach ($piggyBank->piggyBankEvents as $event) {
|
||||
if ('' !== (string) $event->native_amount) {
|
||||
Log::debug(sprintf('Resetting native_amount for piggy bank #%d and event #%d.', $piggyBank->id, $event->id));
|
||||
$event->native_amount = null;
|
||||
$event->saveQuietly();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,10 +122,42 @@ class PreferencesEventHandler
|
||||
->join('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.user_group_id', $userGroup->id)
|
||||
->where(static function (Builder $q): void {
|
||||
$q->whereNotNull('native_amount')->orWhereNotNull('native_foreign_amount');
|
||||
$q
|
||||
->whereNotNull('native_amount')
|
||||
->orWhereNotNull('native_foreign_amount')
|
||||
->orWhere('native_amount', '!=', '')
|
||||
->orWhere('native_foreign_amount', '!=', '')
|
||||
;
|
||||
})
|
||||
->update(['native_amount' => null, 'native_foreign_amount' => null])
|
||||
;
|
||||
Log::debug(sprintf('Reset %d transactions.', $success));
|
||||
}
|
||||
|
||||
private function resetPiggyBanks(UserGroup $userGroup): void
|
||||
{
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUserGroup($userGroup);
|
||||
$piggyBanks = $repository->getPiggyBanks();
|
||||
Log::debug(sprintf('Reset primary currency of %d piggy bank(s).', $piggyBanks->count()));
|
||||
|
||||
/** @var PiggyBank $piggyBank */
|
||||
foreach ($piggyBanks as $piggyBank) {
|
||||
$this->resetPiggyBank($piggyBank);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetBudgets(UserGroup $userGroup): void
|
||||
{
|
||||
$repository = app(BudgetRepositoryInterface::class);
|
||||
$repository->setUserGroup($userGroup);
|
||||
$set = $repository->getBudgets();
|
||||
|
||||
Log::debug(sprintf('Reset primary currency of %d budget(s).', $set->count()));
|
||||
|
||||
/** @var Budget $budget */
|
||||
foreach ($set as $budget) {
|
||||
$this->resetBudget($budget);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Test;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\Test\OwnerTestsNotificationChannel;
|
||||
use FireflyIII\Events\Test\UserTestsNotificationChannel;
|
||||
use FireflyIII\Notifications\NotificationSender;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationEmail;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationPushover;
|
||||
use FireflyIII\Notifications\Test\OwnerTestNotificationSlack;
|
||||
@@ -34,7 +34,6 @@ use FireflyIII\Notifications\Test\UserTestNotificationEmail;
|
||||
use FireflyIII\Notifications\Test\UserTestNotificationPushover;
|
||||
use FireflyIII\Notifications\Test\UserTestNotificationSlack;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class SendsTestNotification
|
||||
{
|
||||
@@ -82,24 +81,7 @@ class SendsTestNotification
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Will send %s as a notification.', $class));
|
||||
|
||||
try {
|
||||
Notification::send($event->user, new $class());
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
NotificationSender::send($event->user, new $class());
|
||||
Log::debug(sprintf('If you see no errors above this line, test notification was sent over channel "%s"', $event->channel));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Enums\AccountTypeEnum;
|
||||
use FireflyIII\Handlers\Observer\AccountObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedAccountObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -42,7 +42,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([AccountObserver::class])]
|
||||
#[ObservedBy([DeletedAccountObserver::class])]
|
||||
class Account extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* AccountBalance.php
|
||||
* Copyright (c) 2025 james@firefly-iii.org.
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AccountBalance extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['account_id', 'title', 'transaction_currency_id', 'balance', 'date', 'date_tz'];
|
||||
|
||||
public function account(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
public function transactionCurrency(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TransactionCurrency::class);
|
||||
}
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return ['date' => SeparateTimezoneCaster::class, 'balance' => 'string'];
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\AttachmentObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedAttachmentObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -36,7 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([AttachmentObserver::class])]
|
||||
#[ObservedBy([DeletedAttachmentObserver::class])]
|
||||
class Attachment extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\CategoryObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedCategoryObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -36,7 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([CategoryObserver::class])]
|
||||
#[ObservedBy([DeletedCategoryObserver::class])]
|
||||
class Category extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace FireflyIII\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Handlers\Observer\RecurrenceObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedRecurrenceObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -43,7 +43,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @property Carbon $first_date
|
||||
* @property null|Carbon $latest_date
|
||||
*/
|
||||
#[ObservedBy([RecurrenceObserver::class])]
|
||||
#[ObservedBy([DeletedRecurrenceObserver::class])]
|
||||
class Recurrence extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\RecurrenceTransactionObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedRecurrenceTransactionObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
@@ -33,7 +33,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
#[ObservedBy([RecurrenceTransactionObserver::class])]
|
||||
#[ObservedBy([DeletedRecurrenceTransactionObserver::class])]
|
||||
class RecurrenceTransaction extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\RuleObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedRuleObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -38,7 +38,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
/**
|
||||
* @property User $user
|
||||
*/
|
||||
#[ObservedBy([RuleObserver::class])]
|
||||
#[ObservedBy([DeletedRuleObserver::class])]
|
||||
class Rule extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\RuleGroupObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedRuleGroupObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -40,7 +40,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @property User $user
|
||||
* @property Collection $rules
|
||||
*/
|
||||
#[ObservedBy([RuleGroupObserver::class])]
|
||||
#[ObservedBy([DeletedRuleGroupObserver::class])]
|
||||
class RuleGroup extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Handlers\Observer\TagObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedTagObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -36,7 +36,7 @@ use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([TagObserver::class])]
|
||||
#[ObservedBy([DeletedTagObserver::class])]
|
||||
class Tag extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -23,7 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\TransactionGroupObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedTransactionGroupObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -41,7 +41,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
* @property UserGroup $userGroup
|
||||
* @property Collection<TransactionJournal> $transactionJournals
|
||||
*/
|
||||
#[ObservedBy([TransactionGroupObserver::class])]
|
||||
#[ObservedBy([DeletedTransactionGroupObserver::class])]
|
||||
class TransactionGroup extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace FireflyIII\Models;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Casts\SeparateTimezoneCaster;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Handlers\Observer\TransactionJournalObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedTransactionJournalObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -50,7 +50,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
*
|
||||
* @property TransactionGroup $transactionGroup
|
||||
*/
|
||||
#[ObservedBy([TransactionJournalObserver::class])]
|
||||
#[ObservedBy([DeletedTransactionJournalObserver::class])]
|
||||
class TransactionJournal extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FireflyIII\Models;
|
||||
use FireflyIII\Enums\WebhookDelivery as WebhookDeliveryEnum;
|
||||
use FireflyIII\Enums\WebhookResponse as WebhookResponseEnum;
|
||||
use FireflyIII\Enums\WebhookTrigger as WebhookTriggerEnum;
|
||||
use FireflyIII\Handlers\Observer\WebhookObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedWebhookObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerUserIdTrait;
|
||||
use FireflyIII\User;
|
||||
@@ -39,7 +39,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([WebhookObserver::class])]
|
||||
#[ObservedBy([DeletedWebhookObserver::class])]
|
||||
class Webhook extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
@@ -24,7 +24,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Models;
|
||||
|
||||
use FireflyIII\Handlers\Observer\WebhookMessageObserver;
|
||||
use FireflyIII\Handlers\Observer\DeletedWebhookMessageObserver;
|
||||
use FireflyIII\Support\Models\ReturnsIntegerIdTrait;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
|
||||
@@ -34,7 +34,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
#[ObservedBy([WebhookMessageObserver::class])]
|
||||
#[ObservedBy([DeletedWebhookMessageObserver::class])]
|
||||
class WebhookMessage extends Model
|
||||
{
|
||||
use ReturnsIntegerIdTrait;
|
||||
|
||||
59
app/Notifications/NotificationSender.php
Normal file
59
app/Notifications/NotificationSender.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* NotificationSender.php
|
||||
* Copyright (c) 2026 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Notifications;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
|
||||
use FireflyIII\User;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Notification as NotificationFacade;
|
||||
|
||||
class NotificationSender
|
||||
{
|
||||
public static function send(OwnerNotifiable|User $user, Notification $notification): void
|
||||
{
|
||||
try {
|
||||
NotificationFacade::send($user, $notification);
|
||||
} catch (ClientException $e) {
|
||||
Log::error(sprintf('[a] Error sending notification: %s', $e->getMessage()));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
if (str_contains($message, 'RFC 2822')) {
|
||||
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::error($e->getMessage());
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user