From 9f94ec067a68fffc30eaee9b2e7fa2dd5085afd4 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 15 Feb 2025 14:10:54 +0100 Subject: [PATCH] Greatly reduce number of queries in transaction transformer. --- .../Models/Account/ListController.php | 9 +- .../Models/Bill/ListController.php | 7 +- .../Models/Budget/ListController.php | 13 +- .../Models/BudgetLimit/ListController.php | 7 +- .../Models/Category/ListController.php | 7 +- .../Models/Recurrence/ListController.php | 7 +- .../Models/Rule/TriggerController.php | 6 + .../Models/RuleGroup/TriggerController.php | 6 + .../Controllers/Models/Tag/ListController.php | 8 +- .../Models/Transaction/ShowController.php | 13 +- .../Models/Transaction/StoreController.php | 6 + .../Models/Transaction/UpdateController.php | 6 + .../TransactionCurrency/ListController.php | 7 +- .../TransactionLinkType/ListController.php | 7 +- .../Search/TransactionController.php | 7 +- .../V1/Requests/Models/Rule/TestRequest.php | 2 +- .../Requests/Models/RuleGroup/TestRequest.php | 2 +- app/Factory/TagFactory.php | 2 +- app/Factory/TransactionGroupFactory.php | 2 +- .../Currency/CurrencyRepositoryInterface.php | 2 + .../Enrichments/EnrichmentInterface.php | 2 +- .../TransactionGroupEnrichment.php | 238 ++++++++++++++++++ .../Summarizer/TransactionSummarizer.php | 4 + .../TransactionGroupTransformer.php | 139 +++++----- 24 files changed, 418 insertions(+), 91 deletions(-) create mode 100644 app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php diff --git a/app/Api/V1/Controllers/Models/Account/ListController.php b/app/Api/V1/Controllers/Models/Account/ListController.php index b2d4e39d90..a450435f08 100644 --- a/app/Api/V1/Controllers/Models/Account/ListController.php +++ b/app/Api/V1/Controllers/Models/Account/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Account; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\PiggyBankTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -165,13 +166,17 @@ class ListController extends Controller $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.accounts.transactions', [$account->id]).$this->buildParams()); - $groups = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); - $resource = new FractalCollection($groups, $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); diff --git a/app/Api/V1/Controllers/Models/Bill/ListController.php b/app/Api/V1/Controllers/Models/Bill/ListController.php index 005c121d00..088633618f 100644 --- a/app/Api/V1/Controllers/Models/Bill/ListController.php +++ b/app/Api/V1/Controllers/Models/Bill/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Bill; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\RuleTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -176,7 +177,11 @@ class ListController extends Controller // get paginator. $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.bills.transactions', [$bill->id]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 5e5bb38ac6..3f6bc40df2 100644 --- a/app/Api/V1/Controllers/Models/Budget/ListController.php +++ b/app/Api/V1/Controllers/Models/Budget/ListController.php @@ -32,6 +32,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; @@ -172,7 +173,11 @@ class ListController extends Controller $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.budgets.transactions', [$budget->id]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); @@ -232,7 +237,11 @@ class ListController extends Controller $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.budgets.without-budget').$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php index d7530eb5f0..1b79478858 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -84,7 +85,11 @@ class ListController extends Controller $collector->setTypes($types); $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.budgets.limits.transactions', [$budget->id, $budgetLimit->id]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Category/ListController.php b/app/Api/V1/Controllers/Models/Category/ListController.php index c53749c1a0..0a743b8e42 100644 --- a/app/Api/V1/Controllers/Models/Category/ListController.php +++ b/app/Api/V1/Controllers/Models/Category/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -139,7 +140,11 @@ class ListController extends Controller $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.categories.transactions', [$category->id]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Recurrence/ListController.php b/app/Api/V1/Controllers/Models/Recurrence/ListController.php index 05fc30100d..b78c2defd9 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/ListController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Recurrence; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -110,7 +111,11 @@ class ListController extends Controller $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Rule/TriggerController.php b/app/Api/V1/Controllers/Models/Rule/TriggerController.php index e9cd6890cf..1da162c513 100644 --- a/app/Api/V1/Controllers/Models/Rule/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Rule/TriggerController.php @@ -29,6 +29,7 @@ use FireflyIII\Api\V1\Requests\Models\Rule\TestRequest; use FireflyIII\Api\V1\Requests\Models\Rule\TriggerRequest; use FireflyIII\Models\Rule; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -94,6 +95,11 @@ class TriggerController extends Controller $transactions = $ruleEngine->find(); $count = $transactions->count(); + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($rule->user); + $transactions = $enrichment->enrich($transactions); + $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); $paginator->setPath(route('api.v1.rules.test', [$rule->id]).$this->buildParams()); diff --git a/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php index 881fcd6b29..699f98fa74 100644 --- a/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php +++ b/app/Api/V1/Controllers/Models/RuleGroup/TriggerController.php @@ -30,6 +30,7 @@ use FireflyIII\Api\V1\Requests\Models\RuleGroup\TriggerRequest; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\RuleGroup; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\TransactionRules\Engine\RuleEngineInterface; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -100,6 +101,11 @@ class TriggerController extends Controller $transactions = $ruleEngine->find(); $count = $transactions->count(); + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($group->user); + $transactions = $enrichment->enrich($transactions); + $paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page')); $paginator->setPath(route('api.v1.rule-groups.test', [$group->id]).$this->buildParams()); diff --git a/app/Api/V1/Controllers/Models/Tag/ListController.php b/app/Api/V1/Controllers/Models/Tag/ListController.php index 707a65da9f..f887fedc68 100644 --- a/app/Api/V1/Controllers/Models/Tag/ListController.php +++ b/app/Api/V1/Controllers/Models/Tag/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\Tag; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; @@ -141,7 +142,12 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.tags.transactions', [$tag->id]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/Transaction/ShowController.php b/app/Api/V1/Controllers/Models/Transaction/ShowController.php index f95a8b00ff..78d435f061 100644 --- a/app/Api/V1/Controllers/Models/Transaction/ShowController.php +++ b/app/Api/V1/Controllers/Models/Transaction/ShowController.php @@ -30,10 +30,12 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Collection; use League\Fractal\Pagination\IlluminatePaginatorAdapter; use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; @@ -85,7 +87,11 @@ class ShowController extends Controller } $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); @@ -137,6 +143,11 @@ class ShowController extends Controller throw new NotFoundHttpException(); } + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Transaction/StoreController.php b/app/Api/V1/Controllers/Models/Transaction/StoreController.php index 933154037b..4704be91ed 100644 --- a/app/Api/V1/Controllers/Models/Transaction/StoreController.php +++ b/app/Api/V1/Controllers/Models/Transaction/StoreController.php @@ -34,6 +34,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Rules\IsDuplicateTransaction; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -133,6 +134,11 @@ class StoreController extends Controller throw new FireflyException('200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.'); } + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php index 1615f9002d..760d387f08 100644 --- a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php @@ -30,6 +30,7 @@ use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionGroup; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -99,6 +100,11 @@ class UpdateController extends Controller throw new NotFoundHttpException(); } + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); + /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php index 04b44e4a38..18e3a3126c 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -42,6 +42,7 @@ use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AccountTransformer; use FireflyIII\Transformers\AvailableBudgetTransformer; use FireflyIII\Transformers\BillTransformer; @@ -360,7 +361,11 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.currencies.transactions', [$currency->code]).$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php index a135a815bf..d777fb8824 100644 --- a/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionLinkType/ListController.php @@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\LinkType; use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface; use FireflyIII\Support\Http\Api\TransactionFilter; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -109,7 +110,11 @@ class ListController extends Controller } $paginator = $collector->getPaginatedGroups(); $paginator->setPath(route('api.v1.transactions.index').$this->buildParams()); - $transactions = $paginator->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser($admin); + $transactions = $enrichment->enrich($paginator->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Controllers/Search/TransactionController.php b/app/Api/V1/Controllers/Search/TransactionController.php index b8a5d450d3..9ab21f116b 100644 --- a/app/Api/V1/Controllers/Search/TransactionController.php +++ b/app/Api/V1/Controllers/Search/TransactionController.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Controllers\Search; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Support\Search\SearchInterface; use FireflyIII\Transformers\TransactionGroupTransformer; use Illuminate\Http\JsonResponse; @@ -57,7 +58,11 @@ class TransactionController extends Controller $parameters = ['search' => $fullQuery]; $url = route('api.v1.search.transactions').'?'.http_build_query($parameters); $groups->setPath($url); - $transactions = $groups->getCollection(); + + // enrich + $enrichment = new TransactionGroupEnrichment(); + $enrichment->setUser(auth()->user()); + $transactions = $enrichment->enrich($groups->getCollection()); /** @var TransactionGroupTransformer $transformer */ $transformer = app(TransactionGroupTransformer::class); diff --git a/app/Api/V1/Requests/Models/Rule/TestRequest.php b/app/Api/V1/Requests/Models/Rule/TestRequest.php index a22b170df3..27490d14f4 100644 --- a/app/Api/V1/Requests/Models/Rule/TestRequest.php +++ b/app/Api/V1/Requests/Models/Rule/TestRequest.php @@ -65,7 +65,7 @@ class TestRequest extends FormRequest private function getAccounts(): array { - return $this->get('accounts'); + return $this->get('accounts') ?? []; } public function rules(): array diff --git a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php index 79230edebf..34fa7231e5 100644 --- a/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php +++ b/app/Api/V1/Requests/Models/RuleGroup/TestRequest.php @@ -59,7 +59,7 @@ class TestRequest extends FormRequest private function getAccounts(): array { - return $this->get('accounts'); + return $this->get('accounts') ?? []; } public function rules(): array diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index 3cdb46d2c3..302624004c 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -76,7 +76,7 @@ class TagFactory $longitude = 0.0 === (float) $data['longitude'] ? null : (float) $data['longitude']; // intentional float $array = [ 'user_id' => $this->user->id, - 'user_group_id' => $this->user->user_group_id, + 'user_group_id' => $this->userGroup->id, 'tag' => trim($data['tag']), 'tagMode' => 'nothing', 'date' => $data['date'], diff --git a/app/Factory/TransactionGroupFactory.php b/app/Factory/TransactionGroupFactory.php index 3ddf9d37f6..110f6fa01f 100644 --- a/app/Factory/TransactionGroupFactory.php +++ b/app/Factory/TransactionGroupFactory.php @@ -79,7 +79,7 @@ class TransactionGroupFactory $group = new TransactionGroup(); $group->user()->associate($this->user); - $group->userGroup()->associate($data['user_group']); + $group->userGroup()->associate($this->userGroup); $group->title = $title; $group->save(); diff --git a/app/Repositories/Currency/CurrencyRepositoryInterface.php b/app/Repositories/Currency/CurrencyRepositoryInterface.php index 09258197fd..4b689542ac 100644 --- a/app/Repositories/Currency/CurrencyRepositoryInterface.php +++ b/app/Repositories/Currency/CurrencyRepositoryInterface.php @@ -26,6 +26,7 @@ namespace FireflyIII\Repositories\Currency; use Carbon\Carbon; use FireflyIII\Models\CurrencyExchangeRate; use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Models\UserGroup; use FireflyIII\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Support\Collection; @@ -67,4 +68,5 @@ interface CurrencyRepositoryInterface public function setExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date, float $rate): CurrencyExchangeRate; public function setUser(null|Authenticatable|User $user): void; + public function setUserGroup(UserGroup $userGroup): void; } diff --git a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php index 83ac519962..90d921f9e9 100644 --- a/app/Support/JsonApi/Enrichments/EnrichmentInterface.php +++ b/app/Support/JsonApi/Enrichments/EnrichmentInterface.php @@ -31,5 +31,5 @@ interface EnrichmentInterface { public function enrich(Collection $collection): Collection; - public function enrichSingle(Model $model): Model; + public function enrichSingle(Model|array $model): Model|array; } diff --git a/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php new file mode 100644 index 0000000000..e165f8b6d6 --- /dev/null +++ b/app/Support/JsonApi/Enrichments/TransactionGroupEnrichment.php @@ -0,0 +1,238 @@ +notes = []; + $this->journalIds = []; + $this->tags = []; + $this->metaData = []; + $this->locations = []; + $this->attachmentCount = []; + $this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date',]; + } + + #[\Override] public function enrich(Collection $collection): Collection + { + Log::debug(sprintf('Now doing account enrichment for %d transaction group(s)', $collection->count())); + // prep local fields + $this->collection = $collection; + $this->collectJournalIds(); + + // collect first, then enrich. + $this->collectNotes(); + $this->collectTags(); + $this->collectMetaData(); + $this->collectLocations(); + $this->collectAttachmentCount(); + $this->appendCollectedData(); + + return $this->collection; + } + + #[\Override] public function enrichSingle(Model|array $model): Model|array + { + Log::debug(__METHOD__); + if(is_array($model)) { + $collection = new Collection([$model]); + $collection = $this->enrich($collection); + return $collection->first(); + } + throw new FireflyException('Cannot enrich single model.'); + } + + private function collectJournalIds(): void + { + /** @var array $group */ + foreach ($this->collection as $group) { + foreach ($group['transactions'] as $journal) { + $this->journalIds[] = $journal['transaction_journal_id']; + } + } + $this->journalIds = array_unique($this->journalIds); + } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + public function setUser(User $user): void + { + $this->user = $user; + $this->userGroup = $user->userGroup; + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->journalIds) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); + foreach ($notes as $note) { + $this->notes[(int) $note['noteable_id']] = (string) $note['text']; + } + Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); + } + + private function collectTags(): void + { + $set = Tag:: + leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') + ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) + ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray(); + foreach ($set as $item) { + $journalId = $item['transaction_journal_id']; + $this->tags[$journalId] ??= []; + $this->tags[$journalId][] = $item['tag']; + } + } + + private function collectMetaData(): void + { + $set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray(); + foreach ($set as $entry) { + $name = $entry['name']; + $data = (string) $entry['data']; + if ('' === $data) { + continue; + } + if (in_array($name, $this->dateFields, true)) { + $this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data); + continue; + } + $this->metaData[(int) $entry['transaction_journal_id']][$name] = $data; + } + } + + private function collectLocations(): void + { + $locations = Location::query()->whereIn('locatable_id', $this->journalIds) + ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); + foreach ($locations as $location) { + $this->locations[(int) $location['locatable_id']] + = [ + 'latitude' => (float) $location['latitude'], + 'longitude' => (float) $location['longitude'], + 'zoom_level' => (int) $location['zoom_level'], + ]; + } + Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); + } + + private function collectAttachmentCount(): void + { + // select count(id) as nr_of_attachments, attachable_id from attachments + //group by attachable_id + $attachments = Attachment::query() + ->whereIn('attachable_id', $this->journalIds) + ->where('attachable_type', TransactionJournal::class) + ->groupBy('attachable_id') + ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')])->toArray(); + foreach ($attachments as $row) { + $this->attachmentCount[(int) $row['attachable_id']] = (int) $row['nr_of_attachments']; + } + } + + private function appendCollectedData(): void { + $notes = $this->notes; + $tags = $this->tags; + $metaData = $this->metaData; + $locations = $this->locations; + $attachmentCount = $this->attachmentCount; + + $this->collection = $this->collection->map(function (array $item) use ($notes, $tags, $metaData, $locations, $attachmentCount) { + foreach ($item['transactions'] as $index => $transaction) { + $journalId = (int) $transaction['transaction_journal_id']; + + // attach notes if they exist: + $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; + + // attach tags if they exist: + $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; + + // attachment count + $item['transactions'][$index]['attachment_count'] = array_key_exists($journalId, $attachmentCount) ? $attachmentCount[$journalId] : 0; + + // default location data + $item['transactions'][$index]['location'] = [ + 'latitude' => null, + 'longitude' => null, + 'zoom_level' => null, + ]; + + // append meta data + $item['transactions'][$index]['meta'] = []; + $item['transactions'][$index]['meta_date'] = []; + if (array_key_exists($journalId, $metaData)) { + // loop al meta data: + foreach ($metaData[$journalId] as $name => $value) { + if (in_array($name, $this->dateFields, true)) { + $item['transactions'][$index]['meta_date'][$name] = Carbon::parse($value); + continue; + } + $item['transactions'][$index]['meta'][$name] = $value; + } + } + + // append location data + if (array_key_exists($journalId, $locations)) { + $item['transactions'][$index]['location'] = $locations[$journalId]; + } + } + return $item; + }); + } + + +} diff --git a/app/Support/Report/Summarizer/TransactionSummarizer.php b/app/Support/Report/Summarizer/TransactionSummarizer.php index 941b83121e..435ab4cd91 100644 --- a/app/Support/Report/Summarizer/TransactionSummarizer.php +++ b/app/Support/Report/Summarizer/TransactionSummarizer.php @@ -65,6 +65,10 @@ class TransactionSummarizer // prepare foreign currency info: $foreignCurrencyId = 0; + $foreignCurrencyName = null; + $foreignCurrencySymbol = null; + $foreignCurrencyCode = null; + $foreignCurrencyDecimalPlaces = null; if ($this->convertToNative) { // if convert to native, use the native amount yes or no? diff --git a/app/Transformers/TransactionGroupTransformer.php b/app/Transformers/TransactionGroupTransformer.php index 1d063bb852..c23bccb421 100644 --- a/app/Transformers/TransactionGroupTransformer.php +++ b/app/Transformers/TransactionGroupTransformer.php @@ -37,6 +37,7 @@ use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface; use FireflyIII\Support\NullArrayObject; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; /** * Class TransactionGroupTransformer @@ -46,12 +47,14 @@ class TransactionGroupTransformer extends AbstractTransformer private TransactionGroupRepositoryInterface $groupRepos; private array $metaDateFields; private array $metaFields; + private Collection $collection; /** * Constructor. */ public function __construct() { + Log::debug('TransactionGroupTransformer constructor.'); $this->groupRepos = app(TransactionGroupRepositoryInterface::class); $this->metaFields = [ 'sepa_cc', @@ -85,6 +88,7 @@ class TransactionGroupTransformer extends AbstractTransformer 'created_at' => $first['created_at']->toAtomString(), 'updated_at' => $first['updated_at']->toAtomString(), 'user' => (string) $data['user_id'], + 'user_group' => (string) $data['user_group_id'], 'group_title' => $data['title'], 'transactions' => $this->transformTransactions($data), 'links' => [ @@ -112,107 +116,96 @@ class TransactionGroupTransformer extends AbstractTransformer */ private function transformTransaction(array $transaction): array { - $row = new NullArrayObject($transaction); - // amount: - $amount = app('steam')->positive((string) ($row['amount'] ?? '0')); + $amount = app('steam')->positive((string) ($transaction['amount'] ?? '0')); $foreignAmount = null; - if (null !== $row['foreign_amount'] && '' !== $row['foreign_amount'] && 0 !== bccomp('0', $row['foreign_amount'])) { - $foreignAmount = app('steam')->positive($row['foreign_amount']); + if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', $transaction['foreign_amount'])) { + $foreignAmount = app('steam')->positive($transaction['foreign_amount']); } - - $metaFieldData = $this->groupRepos->getMetaFields((int) $row['transaction_journal_id'], $this->metaFields); - $metaDateData = $this->groupRepos->getMetaDateFields((int) $row['transaction_journal_id'], $this->metaDateFields); $type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionTypeEnum::WITHDRAWAL->value); - $longitude = null; - $latitude = null; - $zoomLevel = null; - $location = $this->getLocationById((int) $row['transaction_journal_id']); - if (null !== $location) { - $longitude = $location->longitude; - $latitude = $location->latitude; - $zoomLevel = $location->zoom_level; - } + // must be 0 (int) or NULL + $recurrenceTotal = $transaction['meta']['recurrence_total'] ?? null; + $recurrenceTotal = null !== $recurrenceTotal ? (int) $recurrenceTotal : null; + $recurrenceCount = $transaction['meta']['recurrence_count'] ?? null; + $recurrenceCount = null !== $recurrenceCount ? (int) $recurrenceCount : null; return [ - 'user' => (string) $row['user_id'], - 'transaction_journal_id' => (string) $row['transaction_journal_id'], + 'user' => (string) $transaction['user_id'], + 'transaction_journal_id' => $transaction['transaction_journal_id'], 'type' => strtolower($type), - 'date' => $row['date']->toAtomString(), - 'order' => $row['order'], + 'date' => $transaction['date']->toAtomString(), + 'order' => $transaction['order'], - 'currency_id' => (string) $row['currency_id'], - 'currency_code' => $row['currency_code'], - 'currency_name' => $row['currency_name'], - 'currency_symbol' => $row['currency_symbol'], - 'currency_decimal_places' => (int) $row['currency_decimal_places'], + 'currency_id' => (string) $transaction['currency_id'], + 'currency_code' => $transaction['currency_code'], + 'currency_name' => $transaction['currency_name'], + 'currency_symbol' => $transaction['currency_symbol'], + 'currency_decimal_places' => (int) $transaction['currency_decimal_places'], 'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null), - 'foreign_currency_code' => $row['foreign_currency_code'], - 'foreign_currency_symbol' => $row['foreign_currency_symbol'], - 'foreign_currency_decimal_places' => $row['foreign_currency_decimal_places'], + 'foreign_currency_code' => $transaction['foreign_currency_code'], + 'foreign_currency_symbol' => $transaction['foreign_currency_symbol'], + 'foreign_currency_decimal_places' => $transaction['foreign_currency_decimal_places'], 'amount' => $amount, 'foreign_amount' => $foreignAmount, - 'description' => $row['description'], + 'description' => $transaction['description'], - 'source_id' => (string) $row['source_account_id'], - 'source_name' => $row['source_account_name'], - 'source_iban' => $row['source_account_iban'], - 'source_type' => $row['source_account_type'], + 'source_id' => (string) $transaction['source_account_id'], + 'source_name' => $transaction['source_account_name'], + 'source_iban' => $transaction['source_account_iban'], + 'source_type' => $transaction['source_account_type'], - 'destination_id' => (string) $row['destination_account_id'], - 'destination_name' => $row['destination_account_name'], - 'destination_iban' => $row['destination_account_iban'], - 'destination_type' => $row['destination_account_type'], + 'destination_id' => (string) $transaction['destination_account_id'], + 'destination_name' => $transaction['destination_account_name'], + 'destination_iban' => $transaction['destination_account_iban'], + 'destination_type' => $transaction['destination_account_type'], 'budget_id' => $this->stringFromArray($transaction, 'budget_id', null), - 'budget_name' => $row['budget_name'], + 'budget_name' => $transaction['budget_name'], 'category_id' => $this->stringFromArray($transaction, 'category_id', null), - 'category_name' => $row['category_name'], + 'category_name' => $transaction['category_name'], 'bill_id' => $this->stringFromArray($transaction, 'bill_id', null), - 'bill_name' => $row['bill_name'], + 'bill_name' => $transaction['bill_name'], - 'reconciled' => $row['reconciled'], - 'notes' => $this->groupRepos->getNoteText((int) $row['transaction_journal_id']), - 'tags' => $this->groupRepos->getTags((int) $row['transaction_journal_id']), + 'reconciled' => $transaction['reconciled'], + 'notes' => $transaction['notes'], + 'tags' => $transaction['tags'], - 'internal_reference' => $metaFieldData['internal_reference'], - 'external_id' => $metaFieldData['external_id'], - 'original_source' => $metaFieldData['original_source'], - 'recurrence_id' => $this->stringFromArray($metaFieldData->getArrayCopy(), 'recurrence_id', null), - 'recurrence_total' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_total'), - 'recurrence_count' => $this->integerFromArray($metaFieldData->getArrayCopy(), 'recurrence_count'), - 'bunq_payment_id' => $metaFieldData['bunq_payment_id'], - 'external_url' => $metaFieldData['external_url'], - 'import_hash_v2' => $metaFieldData['import_hash_v2'], + 'internal_reference' => $transaction['meta']['internal_reference'] ?? null, + 'external_id' => $transaction['meta']['external_id'] ?? null, + 'original_source' => $transaction['meta']['original_source'] ?? null, + 'recurrence_id' => $transaction['meta']['recurrence_id'] ?? null, + 'recurrence_total' => $recurrenceTotal, + 'recurrence_count' => $recurrenceCount, + 'bunq_payment_id' => $transaction['meta']['bunq_payment_id'] ?? null, + 'external_url' => $transaction['meta']['external_url'] ?? null, + 'import_hash_v2' => $transaction['meta']['import_hash_v2'] ?? null, - 'sepa_cc' => $metaFieldData['sepa_cc'], - 'sepa_ct_op' => $metaFieldData['sepa_ct_op'], - 'sepa_ct_id' => $metaFieldData['sepa_ct_id'], - 'sepa_db' => $metaFieldData['sepa_db'], - 'sepa_country' => $metaFieldData['sepa_country'], - 'sepa_ep' => $metaFieldData['sepa_ep'], - 'sepa_ci' => $metaFieldData['sepa_ci'], - 'sepa_batch_id' => $metaFieldData['sepa_batch_id'], - - 'interest_date' => $this->dateFromArray($metaDateData, 'interest_date'), - 'book_date' => $this->dateFromArray($metaDateData, 'book_date'), - 'process_date' => $this->dateFromArray($metaDateData, 'process_date'), - 'due_date' => $this->dateFromArray($metaDateData, 'due_date'), - 'payment_date' => $this->dateFromArray($metaDateData, 'payment_date'), - 'invoice_date' => $this->dateFromArray($metaDateData, 'invoice_date'), + 'sepa_cc' => $transaction['meta']['sepa_cc'] ?? null, + 'sepa_ct_op' => $transaction['meta']['sepa_ct_op'] ?? null, + 'sepa_ct_id' => $transaction['meta']['sepa_ct_id'] ?? null, + 'sepa_db' => $transaction['meta']['sepa_db'] ?? null, + 'sepa_country' => $transaction['meta']['sepa_country'] ?? null, + 'sepa_ep' => $transaction['meta']['sepa_ep'] ?? null, + 'sepa_ci' => $transaction['meta']['sepa_ci'] ?? null, + 'sepa_batch_id' => $transaction['meta']['sepa_batch_id'] ?? null, + 'interest_date' => $transaction['meta_date']['interest_date'] ?? null, + 'book_date' => $transaction['meta_date']['book_date'] ?? null, + 'process_date' => $transaction['meta_date']['process_date'] ?? null, + 'due_date' => $transaction['meta_date']['due_date'] ?? null, + 'payment_date' => $transaction['meta_date']['payment_date'] ?? null, + 'invoice_date' => $transaction['meta_date']['invoice_date'] ?? null, // location data - 'longitude' => $longitude, - 'latitude' => $latitude, - 'zoom_level' => $zoomLevel, - - 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']), + 'longitude' => $transaction['location']['longitude'], + 'latitude' => $transaction['location']['latitude'], + 'zoom_level' => $transaction['location']['zoom_level'], + 'has_attachments' => $transaction['attachment_count'] > 0, ]; }