diff --git a/app/Api/V1/Controllers/Models/Budget/ListController.php b/app/Api/V1/Controllers/Models/Budget/ListController.php index 7c8eaad8b7..e06dc7d0d5 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\BudgetLimitEnrichment; use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AttachmentTransformer; use FireflyIII\Transformers\BudgetLimitTransformer; @@ -117,6 +118,14 @@ class ListController extends Controller $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator->setPath(route('api.v1.budgets.budget-limits', [$budget->id]).$this->buildParams()); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimits = $enrichment->enrich($budgetLimits); + + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php index 9be210853c..984d5a405e 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/ShowController.php @@ -31,6 +31,7 @@ use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -84,6 +85,14 @@ class ShowController extends Controller $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator->setPath(route('api.v1.budgets.limits.index', [$budget->id]).$this->buildParams()); + + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimits = $enrichment->enrich($budgetLimits); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); @@ -113,6 +122,13 @@ class ShowController extends Controller $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator->setPath(route('api.v1.budget-limits.index').$this->buildParams()); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimits = $enrichment->enrich($budgetLimits); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); @@ -137,6 +153,13 @@ class ShowController extends Controller // continue! $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimit = $enrichment->enrichSingle($budgetLimit); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php index 6f899b19ce..12f0b239b2 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/StoreController.php @@ -28,6 +28,7 @@ use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\BudgetLimit\StoreRequest; use FireflyIII\Models\Budget; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -74,6 +75,13 @@ class StoreController extends Controller $budgetLimit = $this->blRepository->store($data); $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimit = $enrichment->enrichSingle($budgetLimit); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php index 977339538c..5f0dab58d2 100644 --- a/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php +++ b/app/Api/V1/Controllers/Models/BudgetLimit/UpdateController.php @@ -30,6 +30,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; +use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; use FireflyIII\Transformers\BudgetLimitTransformer; use FireflyIII\User; use Illuminate\Http\JsonResponse; @@ -80,6 +81,13 @@ class UpdateController extends Controller $budgetLimit = $this->blRepository->update($budgetLimit, $data); $manager = $this->getManager(); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimit = $enrichment->enrich($budgetLimit); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::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 ed52de7657..fcd9a8dcc4 100644 --- a/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php +++ b/app/Api/V1/Controllers/Models/TransactionCurrency/ListController.php @@ -43,6 +43,7 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\TransactionFilter; use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; +use FireflyIII\Support\JsonApi\Enrichments\BudgetLimitEnrichment; use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment; use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment; use FireflyIII\Transformers\AccountTransformer; @@ -227,6 +228,13 @@ class ListController extends Controller $paginator = new LengthAwarePaginator($budgetLimits, $count, $pageSize, $this->parameters->get('page')); $paginator->setPath(route('api.v1.currencies.budget-limits', [$currency->code]).$this->buildParams()); + // enrich + /** @var User $admin */ + $admin = auth()->user(); + $enrichment = new BudgetLimitEnrichment(); + $enrichment->setUser($admin); + $budgetLimits = $enrichment->enrich($budgetLimits); + /** @var BudgetLimitTransformer $transformer */ $transformer = app(BudgetLimitTransformer::class); $transformer->setParameters($this->parameters); diff --git a/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php new file mode 100644 index 0000000000..22fa9426d9 --- /dev/null +++ b/app/Support/JsonApi/Enrichments/BudgetLimitEnrichment.php @@ -0,0 +1,130 @@ +convertToPrimary = Amount::convertToPrimary(); + $this->primaryCurrency = Amount::getPrimaryCurrency(); + } + + public function enrich(Collection $collection): Collection + { + $this->collection = $collection; + $this->collectIds(); + $this->collectNotes(); + $this->collectBudgets(); + $this->appendCollectedData(); + return $this->collection; + } + + public function enrichSingle(Model|array $model): array|Model + { + Log::debug(__METHOD__); + $collection = new Collection()->push($model); + $collection = $this->enrich($collection); + + return $collection->first(); + } + + public function setUser(User $user): void + { + $this->user = $user; + $this->userGroup = $user->userGroup; + } + + public function setUserGroup(UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + private function collectIds(): void + { + $this->start = $this->collection->min('start_date'); + $this->end = $this->collection->max('end_date'); + /** @var BudgetLimit $limit */ + foreach ($this->collection as $limit) { + $this->ids[] = (int)$limit->id; + } + $this->ids = array_unique($this->ids); + } + + private function collectNotes(): void + { + $notes = Note::query()->whereIn('noteable_id', $this->ids) + ->whereNotNull('notes.text') + ->where('notes.text', '!=', '') + ->where('noteable_type', BudgetLimit::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 appendCollectedData(): void + { + $this->collection = $this->collection->map(function (BudgetLimit $item) { + $id = (int)$item->id; + $meta = [ + 'notes' => $this->notes[$id] ?? null, + 'spent' => $this->expenses[$id] ?? [], + 'pc_spent' => $this->pcExpenses[$id] ?? [], + ]; + $item->meta = $meta; + return $item; + }); + } + + private function collectBudgets(): void + { + $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); + $this->budgets = Budget::whereIn('id', $budgetIds)->get(); + + $repository = app(OperationsRepository::class); + $repository->setUser($this->user); + $expenses = $repository->collectExpenses($this->start, $this->end, null, $this->budgets, null); + + /** @var BudgetLimit $budgetLimit */ + foreach ($this->collection as $budgetLimit) { + $id = (int)$budgetLimit->id; + $filteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, false); + $this->expenses[$id] = array_values($filteredExpenses); + + if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id !== $this->primaryCurrency->id) { + $pcFilteredExpenses = $repository->sumCollectedExpenses($expenses, $budgetLimit->start_date, $budgetLimit->end_date, $budgetLimit->transactionCurrency, true); + $this->pcExpenses[$id] = array_values($pcFilteredExpenses); + } + if (true === $this->convertToPrimary && $budgetLimit->transactionCurrency->id === $this->primaryCurrency->id) { + $this->pcExpenses[$id] = $this->expenses[$id]; + } + } + } +} diff --git a/app/Transformers/AvailableBudgetTransformer.php b/app/Transformers/AvailableBudgetTransformer.php index fe7d9c96f0..3759735ea6 100644 --- a/app/Transformers/AvailableBudgetTransformer.php +++ b/app/Transformers/AvailableBudgetTransformer.php @@ -51,11 +51,11 @@ class AvailableBudgetTransformer extends AbstractTransformer public function transform(AvailableBudget $availableBudget): array { $currency = $availableBudget->transactionCurrency; - $amount = app('steam')->bcround($availableBudget->amount, $currency->decimal_places); + $amount = Steam::bcround($availableBudget->amount, $currency->decimal_places); $pcAmount = null; if ($this->convertToPrimary) { - $pcAmount = app('steam')->bcround($availableBudget->native_amount, $this->primary->decimal_places); + $pcAmount = Steam::bcround($availableBudget->native_amount, $this->primary->decimal_places); } $data = [ diff --git a/app/Transformers/BudgetLimitTransformer.php b/app/Transformers/BudgetLimitTransformer.php index dca28f6ab2..e0cfe3c4a7 100644 --- a/app/Transformers/BudgetLimitTransformer.php +++ b/app/Transformers/BudgetLimitTransformer.php @@ -26,10 +26,8 @@ namespace FireflyIII\Transformers; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Budget\BudgetLimitRepositoryInterface; -use FireflyIII\Repositories\Budget\OperationsRepository; use FireflyIII\Support\Facades\Amount; -use Illuminate\Support\Collection; +use FireflyIII\Support\Facades\Steam; use League\Fractal\Resource\Item; /** @@ -42,11 +40,11 @@ class BudgetLimitTransformer extends AbstractTransformer 'budget', ]; protected bool $convertToPrimary; - protected TransactionCurrency $primary; + protected TransactionCurrency $primaryCurrency; public function __construct() { - $this->primary = Amount::getPrimaryCurrency(); + $this->primaryCurrency = Amount::getPrimaryCurrency(); $this->convertToPrimary = Amount::convertToPrimary(); } @@ -65,65 +63,53 @@ class BudgetLimitTransformer extends AbstractTransformer */ public function transform(BudgetLimit $budgetLimit): array { - $repository = app(OperationsRepository::class); - $limitRepos = app(BudgetLimitRepositoryInterface::class); - $repository->setUser($budgetLimit->budget->user); - $limitRepos->setUser($budgetLimit->budget->user); - $expenses = $repository->sumExpenses( - $budgetLimit->start_date, - $budgetLimit->end_date, - null, - new Collection([$budgetLimit->budget]), - $budgetLimit->transactionCurrency - ); - $currency = $budgetLimit->transactionCurrency; - $amount = $budgetLimit->amount; - $notes = $limitRepos->getNoteText($budgetLimit); - $currencyDecimalPlaces = 2; - $currencyId = null; - $currencyName = null; - $currencyCode = null; - $currencySymbol = null; - if (null !== $currency) { - $amount = $budgetLimit->amount; - $currencyId = $currency->id; - $currencyName = $currency->name; - $currencyCode = $currency->code; - $currencySymbol = $currency->symbol; - $currencyDecimalPlaces = $currency->decimal_places; - } - $amount = app('steam')->bcround($amount, $currencyDecimalPlaces); - $primary = $this->primary; - if (!$this->convertToPrimary) { - $primary = null; - } + $currency = $budgetLimit->transactionCurrency; + if (null === $currency) { + $currency = $this->primaryCurrency; + } + $amount = Steam::bcround($budgetLimit->amount, $currency->decimal_places); + $pcAmount = null; + if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) { + $pcAmount = $amount; + } + if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { + $pcAmount = Steam::bcround($budgetLimit->native_amount, $this->primaryCurrency->decimal_places); + } return [ - 'id' => (string)$budgetLimit->id, - 'created_at' => $budgetLimit->created_at->toAtomString(), - 'updated_at' => $budgetLimit->updated_at->toAtomString(), - 'start' => $budgetLimit->start_date->toAtomString(), - 'end' => $budgetLimit->end_date->endOfDay()->toAtomString(), - 'budget_id' => (string)$budgetLimit->budget_id, - 'currency_id' => (string)$currencyId, - 'currency_code' => $currencyCode, - 'currency_name' => $currencyName, - 'currency_decimal_places' => $currencyDecimalPlaces, - 'currency_symbol' => $currencySymbol, - 'primary_currency_id' => $primary instanceof TransactionCurrency ? (string)$primary->id : null, - 'primary_currency_code' => $primary?->code, - 'primary_currency_symbol' => $primary?->symbol, - 'primary_currency_decimal_places' => $primary?->decimal_places, - 'amount' => $amount, - 'pc_amount' => $this->convertToPrimary ? app('steam')->bcround($budgetLimit->native_amount, $primary->decimal_places) : null, - 'period' => $budgetLimit->period, - 'spent' => $expenses[$currencyId]['sum'] ?? '0', // will be in primary currency if convertToPrimary. - 'notes' => '' === $notes ? null : $notes, - 'links' => [ + 'id' => (string)$budgetLimit->id, + 'created_at' => $budgetLimit->created_at->toAtomString(), + 'updated_at' => $budgetLimit->updated_at->toAtomString(), + 'start' => $budgetLimit->start_date->toAtomString(), + 'end' => $budgetLimit->end_date->endOfDay()->toAtomString(), + 'budget_id' => (string)$budgetLimit->budget_id, + + // currency settings according to 6.3.0 + 'object_has_currency_setting' => true, + + 'currency_id' => (string)$currency->id, + 'currency_name' => $currency->name, + 'currency_code' => $currency->code, + 'currency_symbol' => $currency->symbol, + 'currency_decimal_places' => $currency->decimal_places, + + 'primary_currency_id' => (int)$this->primaryCurrency->id, + 'primary_currency_name' => $this->primaryCurrency->name, + 'primary_currency_code' => $this->primaryCurrency->code, + 'primary_currency_symbol' => $this->primaryCurrency->symbol, + 'primary_currency_decimal_places' => $this->primaryCurrency->decimal_places, + + 'amount' => $amount, + 'pc_amount' => $pcAmount, + 'period' => $budgetLimit->period, + 'spent' => $budgetLimit->meta['spent'], + 'pc_spent' => $budgetLimit->meta['pc_spent'], + 'notes' => $budgetLimit->meta['notes'], + 'links' => [ [ 'rel' => 'self', - 'uri' => '/budgets/limits/'.$budgetLimit->id, + 'uri' => '/budgets/limits/' . $budgetLimit->id, ], ], ]; diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index f65fa5d59c..6c7d8e2718 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -30,6 +30,7 @@ use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Budget\OperationsRepositoryInterface; use FireflyIII\Support\Facades\Amount; +use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; use Symfony\Component\HttpFoundation\ParameterBag; @@ -91,8 +92,8 @@ class BudgetTransformer extends AbstractTransformer } if (null !== $autoBudget) { $abType = $types[$autoBudget->auto_budget_type]; - $abAmount = app('steam')->bcround($autoBudget->amount, $currency->decimal_places); - $abPrimary = $this->convertToPrimary ? app('steam')->bcround($autoBudget->native_amount, $primary->decimal_places) : null; + $abAmount = Steam::bcround($autoBudget->amount, $currency->decimal_places); + $abPrimary = $this->convertToPrimary ? Steam::bcround($autoBudget->native_amount, $primary->decimal_places) : null; $abPeriod = $autoBudget->period; } @@ -136,7 +137,7 @@ class BudgetTransformer extends AbstractTransformer { $return = []; foreach ($array as $data) { - $data['sum'] = app('steam')->bcround($data['sum'], (int) $data['currency_decimal_places']); + $data['sum'] = Steam::bcround($data['sum'], (int) $data['currency_decimal_places']); $return[] = $data; } diff --git a/app/Transformers/CategoryTransformer.php b/app/Transformers/CategoryTransformer.php index ffc3618fee..19b26b2d11 100644 --- a/app/Transformers/CategoryTransformer.php +++ b/app/Transformers/CategoryTransformer.php @@ -99,7 +99,7 @@ class CategoryTransformer extends AbstractTransformer { $return = []; foreach ($array as $data) { - $data['sum'] = app('steam')->bcround($data['sum'], (int)$data['currency_decimal_places']); + $data['sum'] = Steam::bcround($data['sum'], (int)$data['currency_decimal_places']); $return[] = $data; } diff --git a/app/Transformers/PiggyBankEventTransformer.php b/app/Transformers/PiggyBankEventTransformer.php index 70f8122189..b99c90ee81 100644 --- a/app/Transformers/PiggyBankEventTransformer.php +++ b/app/Transformers/PiggyBankEventTransformer.php @@ -75,7 +75,7 @@ class PiggyBankEventTransformer extends AbstractTransformer 'id' => (string) $event->id, 'created_at' => $event->created_at?->toAtomString(), 'updated_at' => $event->updated_at?->toAtomString(), - 'amount' => app('steam')->bcround($event->amount, $currency->decimal_places), + 'amount' => Steam::bcround($event->amount, $currency->decimal_places), 'currency_id' => (string) $currency->id, 'currency_code' => $currency->code, 'currency_symbol' => $currency->symbol, diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php index 674ba62faa..367dedf31a 100644 --- a/app/Transformers/PiggyBankTransformer.php +++ b/app/Transformers/PiggyBankTransformer.php @@ -88,9 +88,9 @@ class PiggyBankTransformer extends AbstractTransformer if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00 $leftToSave = bcsub($piggyBank->target_amount, $currentAmount); $percentage = (int) bcmul(bcdiv($currentAmount, $targetAmount), '100'); - $targetAmount = app('steam')->bcround($targetAmount, $currency->decimal_places); - $leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places); - $savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places); + $targetAmount = Steam::bcround($targetAmount, $currency->decimal_places); + $leftToSave = Steam::bcround($leftToSave, $currency->decimal_places); + $savePerMonth = Steam::bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places); } $startDate = $piggyBank->start_date?->format('Y-m-d'); $targetDate = $piggyBank->target_date?->format('Y-m-d'); diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index dc07e04224..dc6fb9785d 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -193,10 +193,10 @@ class RecurrenceTransformer extends AbstractTransformer $destinationType = $destinationAccount->accountType->type; $destinationIban = $destinationAccount->iban; } - $amount = app('steam')->bcround($transaction->amount, $transaction->transactionCurrency->decimal_places); + $amount = Steam::bcround($transaction->amount, $transaction->transactionCurrency->decimal_places); $foreignAmount = null; if (null !== $transaction->foreign_currency_id && null !== $transaction->foreign_amount) { - $foreignAmount = app('steam')->bcround($transaction->foreign_amount, $foreignCurrencyDp); + $foreignAmount = Steam::bcround($transaction->foreign_amount, $foreignCurrencyDp); } $transactionArray = [ 'id' => (string) $transaction->id,