Optimize queries for statistics.

This commit is contained in:
James Cole
2025-09-26 06:05:37 +02:00
parent 08879d31ba
commit 4ec2fcdb8a
92 changed files with 6499 additions and 6514 deletions

View File

@@ -53,29 +53,29 @@ use Override;
*/
class AccountEnrichment implements EnrichmentInterface
{
private array $ids = [];
private array $accountTypeIds = [];
private array $accountTypes = [];
private Collection $collection;
private array $currencies = [];
private array $locations = [];
private array $meta = [];
private readonly TransactionCurrency $primaryCurrency;
private array $notes = [];
private array $openingBalances = [];
private User $user;
private UserGroup $userGroup;
private array $lastActivities = [];
private ?Carbon $date = null;
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $accountTypeIds = [];
private array $accountTypes = [];
private array $balances = [];
private Collection $collection;
private readonly bool $convertToPrimary;
private array $balances = [];
private array $startBalances = [];
private array $endBalances = [];
private array $objectGroups = [];
private array $mappedObjects = [];
private array $sort = [];
private array $currencies = [];
private ?Carbon $date = null;
private ?Carbon $end = null;
private array $endBalances = [];
private array $ids = [];
private array $lastActivities = [];
private array $locations = [];
private array $mappedObjects = [];
private array $meta = [];
private array $notes = [];
private array $objectGroups = [];
private array $openingBalances = [];
private readonly TransactionCurrency $primaryCurrency;
private array $sort = [];
private ?Carbon $start = null;
private array $startBalances = [];
private User $user;
private UserGroup $userGroup;
/**
* TODO The account enricher must do conversion from and to the primary currency.
@@ -86,16 +86,6 @@ class AccountEnrichment implements EnrichmentInterface
$this->convertToPrimary = Amount::convertToPrimary();
}
#[Override]
public function enrichSingle(array|Model $model): Account|array
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
}
#[Override]
/**
* Do the actual enrichment.
@@ -121,114 +111,47 @@ class AccountEnrichment implements EnrichmentInterface
return $this->collection;
}
private function collectIds(): void
#[Override]
public function enrichSingle(array | Model $model): Account | array
{
/** @var Account $account */
foreach ($this->collection as $account) {
$this->ids[] = (int)$account->id;
$this->accountTypeIds[] = (int)$account->account_type_id;
}
$this->ids = array_unique($this->ids);
$this->accountTypeIds = array_unique($this->accountTypeIds);
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
}
private function getAccountTypes(): void
public function getDate(): Carbon
{
$types = AccountType::whereIn('id', $this->accountTypeIds)->get();
/** @var AccountType $type */
foreach ($types as $type) {
$this->accountTypes[(int)$type->id] = $type->type;
if (!$this->date instanceof Carbon) {
return now();
}
return $this->date;
}
private function collectMetaData(): void
public function setDate(?Carbon $date): void
{
$set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt'])
->whereIn('account_id', $this->ids)
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray()
;
/** @var array $entry */
foreach ($set as $entry) {
$this->meta[(int)$entry['account_id']][$entry['name']] = (string)$entry['data'];
if ('currency_id' === $entry['name']) {
$this->currencies[(int)$entry['data']] = true;
}
}
if (count($this->currencies) > 0) {
$currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get();
foreach ($currencies as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
}
$this->currencies[0] = $this->primaryCurrency;
foreach ($this->currencies as $id => $currency) {
if (true === $currency) {
throw new FireflyException(sprintf('Currency #%d not found.', $id));
}
if ($date instanceof Carbon) {
$date->endOfDay();
Log::debug(sprintf('Date is now %s', $date->toW3cString()));
}
$this->date = $date;
}
private function collectNotes(): void
public function setEnd(?Carbon $end): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Account::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)));
$this->end = $end;
}
private function collectLocations(): void
public function setSort(array $sort): void
{
$locations = Location::query()->whereIn('locatable_id', $this->ids)
->where('locatable_type', Account::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)));
$this->sort = $sort;
}
private function collectOpeningBalances(): void
public function setStart(?Carbon $start): void
{
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($this->user)
->setUserGroup($this->userGroup)
->setAccounts($this->collection)
->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value])
;
$journals = $collector->getExtractedJournals();
foreach ($journals as $journal) {
$this->openingBalances[(int)$journal['source_account_id']]
= [
'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'],
];
$this->openingBalances[(int)$journal['destination_account_id']]
= [
'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'],
];
}
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
$this->start = $start;
}
public function setUser(User $user): void
@@ -237,12 +160,17 @@ class AccountEnrichment implements EnrichmentInterface
$this->userGroup = $user->userGroup;
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (Account $item) {
$id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [
$id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [
'currency' => null,
'location' => [
'latitude' => null,
@@ -289,30 +217,30 @@ class AccountEnrichment implements EnrichmentInterface
// add balances
// get currencies:
$currency = $this->primaryCurrency; // assume primary currency
$currency = $this->primaryCurrency; // assume primary currency
if (null !== $meta['currency']) {
$currency = $meta['currency'];
}
// get the current balance:
$date = $this->getDate();
$date = $this->getDate();
// $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
$finalBalance = $this->balances[$id];
$balanceDifference = $this->getBalanceDifference($id, $currency);
$finalBalance = $this->balances[$id];
$balanceDifference = $this->getBalanceDifference($id, $currency);
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
// collect current balances:
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $meta['current_debt'] ?? null;
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $meta['current_debt'] ?? null;
// set some pc_ default values to NULL:
$pcCurrentBalance = null;
$pcOpeningBalance = null;
$pcVirtualBalance = null;
$pcDebtAmount = null;
$pcBalanceDifference = null;
$pcCurrentBalance = null;
$pcOpeningBalance = null;
$pcVirtualBalance = null;
$pcDebtAmount = null;
$pcBalanceDifference = null;
// convert to primary currency if needed:
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
@@ -351,17 +279,12 @@ class AccountEnrichment implements EnrichmentInterface
'pc_balance_difference' => $pcBalanceDifference,
];
// end add balances
$item->meta = $meta;
$item->meta = $meta;
return $item;
});
}
private function collectLastActivities(): void
{
$this->lastActivities = Steam::getLastActivities($this->ids);
}
private function collectBalances(): void
{
$this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
@@ -371,15 +294,84 @@ class AccountEnrichment implements EnrichmentInterface
}
}
private function collectIds(): void
{
/** @var Account $account */
foreach ($this->collection as $account) {
$this->ids[] = (int)$account->id;
$this->accountTypeIds[] = (int)$account->account_type_id;
}
$this->ids = array_unique($this->ids);
$this->accountTypeIds = array_unique($this->accountTypeIds);
}
private function collectLastActivities(): void
{
$this->lastActivities = Steam::getLastActivities($this->ids);
}
private function collectLocations(): void
{
$locations = Location::query()->whereIn('locatable_id', $this->ids)
->where('locatable_type', Account::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 collectMetaData(): void
{
$set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt'])
->whereIn('account_id', $this->ids)
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray();
/** @var array $entry */
foreach ($set as $entry) {
$this->meta[(int)$entry['account_id']][$entry['name']] = (string)$entry['data'];
if ('currency_id' === $entry['name']) {
$this->currencies[(int)$entry['data']] = true;
}
}
if (count($this->currencies) > 0) {
$currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get();
foreach ($currencies as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
}
$this->currencies[0] = $this->primaryCurrency;
foreach ($this->currencies as $id => $currency) {
if (true === $currency) {
throw new FireflyException(sprintf('Currency #%d not found.', $id));
}
}
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Account::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 collectObjectGroups(): void
{
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Account::class)
->get(['object_groupable_id', 'object_group_id'])
;
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Account::class)
->get(['object_groupable_id', 'object_group_id']);
$ids = array_unique($set->pluck('object_group_id')->toArray());
$ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
@@ -393,32 +385,40 @@ class AccountEnrichment implements EnrichmentInterface
}
}
public function setDate(?Carbon $date): void
private function collectOpeningBalances(): void
{
if ($date instanceof Carbon) {
$date->endOfDay();
Log::debug(sprintf('Date is now %s', $date->toW3cString()));
// use new group collector:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector
->setUser($this->user)
->setUserGroup($this->userGroup)
->setAccounts($this->collection)
->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]);
$journals = $collector->getExtractedJournals();
foreach ($journals as $journal) {
$this->openingBalances[(int)$journal['source_account_id']]
= [
'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'],
];
$this->openingBalances[(int)$journal['destination_account_id']]
= [
'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'],
];
}
$this->date = $date;
}
public function getDate(): Carbon
private function getAccountTypes(): void
{
if (!$this->date instanceof Carbon) {
return now();
$types = AccountType::whereIn('id', $this->accountTypeIds)->get();
/** @var AccountType $type */
foreach ($types as $type) {
$this->accountTypes[(int)$type->id] = $type->type;
}
return $this->date;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
}
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
private function getBalanceDifference(int $id, TransactionCurrency $currency): ?string
@@ -431,17 +431,12 @@ class AccountEnrichment implements EnrichmentInterface
if (0 === count($startBalance) || 0 === count($endBalance)) {
return null;
}
$start = $startBalance[$currency->code] ?? '0';
$end = $endBalance[$currency->code] ?? '0';
$start = $startBalance[$currency->code] ?? '0';
$end = $endBalance[$currency->code] ?? '0';
return bcsub($end, $start);
}
public function setSort(array $sort): void
{
$this->sort = $sort;
}
private function sortData(): void
{
$dbParams = config('firefly.allowed_db_sort_parameters.Account', []);
@@ -458,7 +453,7 @@ class AccountEnrichment implements EnrichmentInterface
case 'current_balance':
case 'pc_current_balance':
$this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]);
$this->collection = $this->collection->sortBy(static fn(Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]);
break;
}

View File

@@ -40,20 +40,20 @@ use Override;
class AvailableBudgetEnrichment implements EnrichmentInterface
{
private User $user; // @phpstan-ignore-line
private UserGroup $userGroup; // @phpstan-ignore-line
private readonly bool $convertToPrimary;
private array $ids = [];
private array $currencyIds = [];
private Collection $collection; // @phpstan-ignore-line
private readonly bool $convertToPrimary; // @phpstan-ignore-line
private array $currencies = [];
private Collection $collection;
private array $spentInBudgets = [];
private array $spentOutsideBudgets = [];
private array $pcSpentInBudgets = [];
private array $pcSpentOutsideBudgets = [];
private array $currencyIds = [];
private array $ids = [];
private readonly NoBudgetRepositoryInterface $noBudgetRepository;
private readonly OperationsRepositoryInterface $opsRepository;
private array $pcSpentInBudgets = [];
private array $pcSpentOutsideBudgets = [];
private readonly BudgetRepositoryInterface $repository;
private array $spentInBudgets = [];
private array $spentOutsideBudgets = [];
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -79,7 +79,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
}
#[Override]
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -104,6 +104,34 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$this->repository->setUserGroup($userGroup);
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (AvailableBudget $item) {
$id = (int)$item->id;
$currencyId = $this->currencyIds[$id];
$currency = $this->currencies[$currencyId];
$meta = [
'currency' => $currency,
'spent_in_budgets' => $this->spentInBudgets[$id] ?? [],
'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [],
'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
private function collectCurrencies(): void
{
$ids = array_unique(array_values($this->currencyIds));
$set = TransactionCurrency::whereIn('id', $ids)->get();
foreach ($set as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
}
private function collectIds(): void
{
/** @var AvailableBudget $availableBudget */
@@ -138,32 +166,4 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
}
}
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (AvailableBudget $item) {
$id = (int)$item->id;
$currencyId = $this->currencyIds[$id];
$currency = $this->currencies[$currencyId];
$meta = [
'currency' => $currency,
'spent_in_budgets' => $this->spentInBudgets[$id] ?? [],
'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [],
'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
private function collectCurrencies(): void
{
$ids = array_unique(array_values($this->currencyIds));
$set = TransactionCurrency::whereIn('id', $ids)->get();
foreach ($set as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
}
}

View File

@@ -40,19 +40,19 @@ use Illuminate\Support\Facades\Log;
class BudgetEnrichment implements EnrichmentInterface
{
private Collection $collection;
private User $user;
private UserGroup $userGroup;
private array $ids = [];
private array $notes = [];
private array $autoBudgets = [];
private array $currencies = [];
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $spent = [];
private array $pcSpent = [];
private array $objectGroups = [];
private array $mappedObjects = [];
private array $autoBudgets = [];
private Collection $collection;
private array $currencies = [];
private ?Carbon $end = null;
private array $ids = [];
private array $mappedObjects = [];
private array $notes = [];
private array $objectGroups = [];
private array $pcSpent = [];
private array $spent = [];
private ?Carbon $start = null;
private User $user;
private UserGroup $userGroup;
public function __construct() {}
@@ -70,7 +70,7 @@ class BudgetEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -79,6 +79,16 @@ class BudgetEnrichment implements EnrichmentInterface
return $collection->first();
}
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
}
public function setUser(User $user): void
{
$this->user = $user;
@@ -90,33 +100,11 @@ class BudgetEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var Budget $budget */
foreach ($this->collection as $budget) {
$this->ids[] = (int)$budget->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', Budget::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 (Budget $item) {
$id = (int)$item->id;
$meta = [
$id = (int)$item->id;
$meta = [
'object_group_id' => null,
'object_group_order' => null,
'object_group_title' => null,
@@ -130,7 +118,7 @@ class BudgetEnrichment implements EnrichmentInterface
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
$meta['object_group_id'] = (string)$this->objectGroups[$key]['id'];
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
}
@@ -168,7 +156,7 @@ class BudgetEnrichment implements EnrichmentInterface
$opsRepository->setUserGroup($this->userGroup);
// $spent = $this->beautify();
// $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget))
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null);
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null);
foreach ($this->collection as $item) {
$id = (int)$item->id;
$this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false));
@@ -177,25 +165,35 @@ class BudgetEnrichment implements EnrichmentInterface
}
}
public function setEnd(?Carbon $end): void
private function collectIds(): void
{
$this->end = $end;
/** @var Budget $budget */
foreach ($this->collection as $budget) {
$this->ids[] = (int)$budget->id;
}
$this->ids = array_unique($this->ids);
}
public function setStart(?Carbon $start): void
private function collectNotes(): void
{
$this->start = $start;
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Budget::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 collectObjectGroups(): void
{
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Budget::class)
->get(['object_groupable_id', 'object_group_id'])
;
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Budget::class)
->get(['object_groupable_id', 'object_group_id']);
$ids = array_unique($set->pluck('object_group_id')->toArray());
$ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;

View File

@@ -40,19 +40,19 @@ use Illuminate\Support\Facades\Log;
class BudgetLimitEnrichment implements EnrichmentInterface
{
private User $user;
private UserGroup $userGroup; // @phpstan-ignore-line
private Collection $collection;
private array $ids = [];
private array $notes = [];
private Carbon $start;
private bool $convertToPrimary = true; // @phpstan-ignore-line
private array $currencies = [];
private array $currencyIds = [];
private Carbon $end;
private array $expenses = [];
private array $ids = [];
private array $notes = [];
private array $pcExpenses = [];
private array $currencyIds = [];
private array $currencies = [];
private bool $convertToPrimary = true;
private readonly TransactionCurrency $primaryCurrency;
private Carbon $start;
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -73,7 +73,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -93,36 +93,6 @@ class BudgetLimitEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
/** @var BudgetLimit $limit */
foreach ($this->collection as $limit) {
$id = (int)$limit->id;
$this->ids[] = $id;
if (0 !== (int)$limit->transaction_currency_id) {
$this->currencyIds[$id] = (int)$limit->transaction_currency_id;
}
}
$this->ids = array_unique($this->ids);
$this->currencyIds = array_unique($this->currencyIds);
}
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) {
@@ -145,12 +115,12 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function collectBudgets(): void
{
$budgetIds = $this->collection->pluck('budget_id')->unique()->toArray();
$budgets = Budget::whereIn('id', $budgetIds)->get();
$budgetIds = $this->collection->pluck('budget_id')->unique()->toArray();
$budgets = Budget::whereIn('id', $budgetIds)->get();
$repository = app(OperationsRepository::class);
$repository->setUser($this->user);
$expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null);
$expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null);
/** @var BudgetLimit $budgetLimit */
foreach ($this->collection as $budgetLimit) {
@@ -179,26 +149,55 @@ class BudgetLimitEnrichment implements EnrichmentInterface
}
}
private function stringifyIds(): void
private function collectIds(): void
{
$this->expenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
return $second;
}, $first), $this->expenses);
/** @var BudgetLimit $limit */
foreach ($this->collection as $limit) {
$id = (int)$limit->id;
$this->ids[] = $id;
if (0 !== (int)$limit->transaction_currency_id) {
$this->currencyIds[$id] = (int)$limit->transaction_currency_id;
}
}
$this->ids = array_unique($this->ids);
$this->currencyIds = array_unique($this->currencyIds);
}
$this->pcExpenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first), $this->expenses);
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 filterToBudget(array $expenses, int $budget): array
{
$result = array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget);
$result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget);
Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result)));
return $result;
}
private function stringifyIds(): void
{
$this->expenses = array_map(fn($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first), $this->expenses);
$this->pcExpenses = array_map(fn($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second;
}, $first), $this->expenses);
}
}

View File

@@ -38,18 +38,18 @@ use Illuminate\Support\Facades\Log;
class CategoryEnrichment implements EnrichmentInterface
{
private Collection $collection;
private User $user;
private UserGroup $userGroup;
private array $earned = [];
private ?Carbon $end = null;
private array $ids = [];
private array $notes = [];
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $spent = [];
private array $pcSpent = [];
private array $earned = [];
private array $pcEarned = [];
private array $transfers = [];
private array $pcSpent = [];
private array $pcTransfers = [];
private array $spent = [];
private ?Carbon $start = null;
private array $transfers = [];
private User $user;
private UserGroup $userGroup;
public function enrich(Collection $collection): Collection
{
@@ -62,7 +62,7 @@ class CategoryEnrichment implements EnrichmentInterface
return $collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -71,6 +71,16 @@ class CategoryEnrichment implements EnrichmentInterface
return $collection->first();
}
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
}
public function setUser(User $user): void
{
$this->user = $user;
@@ -82,15 +92,6 @@ class CategoryEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var Category $category */
foreach ($this->collection as $category) {
$this->ids[] = (int)$category->id;
}
$this->ids = array_unique($this->ids);
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (Category $item) {
@@ -110,23 +111,21 @@ class CategoryEnrichment implements EnrichmentInterface
});
}
public function setEnd(?Carbon $end): void
private function collectIds(): void
{
$this->end = $end;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
/** @var Category $category */
foreach ($this->collection as $category) {
$this->ids[] = (int)$category->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', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray();
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
}
@@ -140,9 +139,9 @@ class CategoryEnrichment implements EnrichmentInterface
$opsRepository = app(OperationsRepositoryInterface::class);
$opsRepository->setUser($this->user);
$opsRepository->setUserGroup($this->userGroup);
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection);
$income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection);
$transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection);
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection);
$income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection);
$transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection);
foreach ($this->collection as $item) {
$id = (int)$item->id;
$this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false));

View File

@@ -33,7 +33,7 @@ interface EnrichmentInterface
{
public function enrich(Collection $collection): Collection;
public function enrichSingle(array|Model $model): array|Model;
public function enrichSingle(array | Model $model): array | Model;
public function setUser(User $user): void;

View File

@@ -43,20 +43,20 @@ use Illuminate\Support\Facades\Log;
class PiggyBankEnrichment implements EnrichmentInterface
{
private User $user; // @phpstan-ignore-line
private UserGroup $userGroup; // @phpstan-ignore-line
private Collection $collection;
private array $ids = [];
private array $currencyIds = [];
private array $currencies = [];
private array $accountIds = [];
private array $accountIds = []; // @phpstan-ignore-line
private array $accounts = []; // @phpstan-ignore-line
private array $amounts = [];
private Collection $collection;
private array $currencies = [];
private array $currencyIds = [];
private array $ids = [];
// private array $accountCurrencies = [];
private array $notes = [];
private array $mappedObjects = [];
private array $mappedObjects = [];
private array $notes = [];
private array $objectGroups = [];
private readonly TransactionCurrency $primaryCurrency;
private array $amounts = [];
private array $accounts = [];
private array $objectGroups = [];
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -77,7 +77,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -97,80 +97,17 @@ class PiggyBankEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var PiggyBank $piggy */
foreach ($this->collection as $piggy) {
$id = (int)$piggy->id;
$this->ids[] = $id;
$this->currencyIds[$id] = (int)$piggy->transaction_currency_id;
}
$this->ids = array_unique($this->ids);
// collect currencies.
$currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get();
foreach ($currencies as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
// collect accounts
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']);
foreach ($set as $item) {
$id = (int)$item->piggy_bank_id;
$accountId = (int)$item->account_id;
$this->amounts[$id] ??= [];
if (!array_key_exists($id, $this->accountIds)) {
$this->accountIds[$id] = (int)$item->account_id;
}
if (!array_key_exists($accountId, $this->amounts[$id])) {
$this->amounts[$id][$accountId] = [
'current_amount' => '0',
'pc_current_amount' => '0',
];
}
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string) $item->current_amount);
if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) {
$this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string) $item->native_current_amount);
}
}
// get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */
foreach ($set as $item) {
$accountId = (int)$item->account_id;
$currencyId = (int)$item->data;
if (!array_key_exists($currencyId, $this->currencies)) {
$this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId);
}
// $this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
}
// get account info.
$set = Account::whereIn('id', array_values($this->accountIds))->get();
/** @var Account $item */
foreach ($set as $item) {
$id = (int)$item->id;
$this->accounts[$id] = [
'id' => $id,
'name' => $item->name,
];
}
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (PiggyBank $item) {
$id = (int)$item->id;
$currencyId = (int)$item->transaction_currency_id;
$currency = $this->currencies[$currencyId] ?? $this->primaryCurrency;
$targetAmount = null;
$id = (int)$item->id;
$currencyId = (int)$item->transaction_currency_id;
$currency = $this->currencies[$currencyId] ?? $this->primaryCurrency;
$targetAmount = null;
if (0 !== bccomp($item->target_amount, '0')) {
$targetAmount = $item->target_amount;
}
$meta = [
$meta = [
'notes' => $this->notes[$id] ?? null,
'currency' => $this->currencies[$currencyId] ?? null,
// 'auto_budget' => $this->autoBudgets[$id] ?? null,
@@ -193,23 +130,23 @@ class PiggyBankEnrichment implements EnrichmentInterface
// add object group if available
if (array_key_exists($id, $this->mappedObjects)) {
$key = $this->mappedObjects[$id];
$meta['object_group_id'] = (string) $this->objectGroups[$key]['id'];
$meta['object_group_id'] = (string)$this->objectGroups[$key]['id'];
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
}
// add current amount(s).
foreach ($this->amounts[$id] as $accountId => $row) {
$meta['accounts'][] = [
$meta['accounts'][] = [
'account_id' => (string)$accountId,
'name' => $this->accounts[$accountId]['name'] ?? '',
'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places),
'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places),
];
$meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']);
$meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']);
// only add pc_current_amount when the pc_current_amount is set
$meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']);
}
$meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places);
$meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places);
// only round this number when pc_current_amount is set.
$meta['pc_current_amount'] = null === $meta['pc_current_amount'] ? null : Steam::bcround($meta['pc_current_amount'], $this->primaryCurrency->decimal_places);
@@ -223,19 +160,83 @@ class PiggyBankEnrichment implements EnrichmentInterface
$meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places);
$meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places);
$item->meta = $meta;
$item->meta = $meta;
return $item;
});
}
private function collectCurrentAmounts(): void {}
private function collectIds(): void
{
/** @var PiggyBank $piggy */
foreach ($this->collection as $piggy) {
$id = (int)$piggy->id;
$this->ids[] = $id;
$this->currencyIds[$id] = (int)$piggy->transaction_currency_id;
}
$this->ids = array_unique($this->ids);
// collect currencies.
$currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get();
foreach ($currencies as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
// collect accounts
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']);
foreach ($set as $item) {
$id = (int)$item->piggy_bank_id;
$accountId = (int)$item->account_id;
$this->amounts[$id] ??= [];
if (!array_key_exists($id, $this->accountIds)) {
$this->accountIds[$id] = (int)$item->account_id;
}
if (!array_key_exists($accountId, $this->amounts[$id])) {
$this->amounts[$id][$accountId] = [
'current_amount' => '0',
'pc_current_amount' => '0',
];
}
$this->amounts[$id][$accountId]['current_amount'] = bcadd($this->amounts[$id][$accountId]['current_amount'], (string)$item->current_amount);
if (null !== $this->amounts[$id][$accountId]['pc_current_amount'] && null !== $item->native_current_amount) {
$this->amounts[$id][$accountId]['pc_current_amount'] = bcadd($this->amounts[$id][$accountId]['pc_current_amount'], (string)$item->native_current_amount);
}
}
// get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */
foreach ($set as $item) {
$accountId = (int)$item->account_id;
$currencyId = (int)$item->data;
if (!array_key_exists($currencyId, $this->currencies)) {
$this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId);
}
// $this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
}
// get account info.
$set = Account::whereIn('id', array_values($this->accountIds))->get();
/** @var Account $item */
foreach ($set as $item) {
$id = (int)$item->id;
$this->accounts[$id] = [
'id' => $id,
'name' => $item->name,
];
}
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray();
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
}
@@ -244,13 +245,12 @@ class PiggyBankEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void
{
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', PiggyBank::class)
->get(['object_groupable_id', 'object_group_id'])
;
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', PiggyBank::class)
->get(['object_groupable_id', 'object_group_id']);
$ids = array_unique($set->pluck('object_group_id')->toArray());
$ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
@@ -264,8 +264,6 @@ class PiggyBankEnrichment implements EnrichmentInterface
}
}
private function collectCurrentAmounts(): void {}
/**
* Returns the suggested amount the user should save per month, or "".
*/

View File

@@ -38,16 +38,16 @@ use Illuminate\Support\Facades\Log;
class PiggyBankEventEnrichment implements EnrichmentInterface
{
private User $user; // @phpstan-ignore-line
private UserGroup $userGroup; // @phpstan-ignore-line
private array $accountCurrencies = []; // @phpstan-ignore-line
private array $accountIds = []; // @phpstan-ignore-line
private Collection $collection;
private array $currencies = [];
private array $groupIds = [];
private array $ids = [];
private array $journalIds = [];
private array $groupIds = [];
private array $accountIds = [];
private array $piggyBankIds = [];
private array $accountCurrencies = [];
private array $currencies = [];
private User $user;
private UserGroup $userGroup;
// private bool $convertToPrimary = false;
// private TransactionCurrency $primaryCurrency;
@@ -66,7 +66,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -86,53 +86,13 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var PiggyBankEvent $event */
foreach ($this->collection as $event) {
$this->ids[] = (int)$event->id;
$this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id;
$this->piggyBankIds[(int)$event->id] = (int)$event->piggy_bank_id;
}
$this->ids = array_unique($this->ids);
// collect groups with journal info.
$set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']);
/** @var TransactionJournal $item */
foreach ($set as $item) {
$this->groupIds[(int)$item->id] = (int)$item->transaction_group_id;
}
// collect account info.
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']);
foreach ($set as $item) {
$id = (int)$item->piggy_bank_id;
if (!array_key_exists($id, $this->accountIds)) {
$this->accountIds[$id] = (int)$item->account_id;
}
}
// get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */
foreach ($set as $item) {
$accountId = (int)$item->account_id;
$currencyId = (int)$item->data;
if (!array_key_exists($currencyId, $this->currencies)) {
$this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId);
}
$this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
}
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (PiggyBankEvent $item) {
$id = (int)$item->id;
$piggyId = (int)$item->piggy_bank_id;
$journalId = (int)$item->transaction_journal_id;
$currency = null;
$id = (int)$item->id;
$piggyId = (int)$item->piggy_bank_id;
$journalId = (int)$item->transaction_journal_id;
$currency = null;
if (array_key_exists($piggyId, $this->accountIds)) {
$accountId = $this->accountIds[$piggyId];
if (array_key_exists($accountId, $this->accountCurrencies)) {
@@ -149,4 +109,44 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
});
}
private function collectIds(): void
{
/** @var PiggyBankEvent $event */
foreach ($this->collection as $event) {
$this->ids[] = (int)$event->id;
$this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id;
$this->piggyBankIds[(int)$event->id] = (int)$event->piggy_bank_id;
}
$this->ids = array_unique($this->ids);
// collect groups with journal info.
$set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']);
/** @var TransactionJournal $item */
foreach ($set as $item) {
$this->groupIds[(int)$item->id] = (int)$item->transaction_group_id;
}
// collect account info.
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']);
foreach ($set as $item) {
$id = (int)$item->piggy_bank_id;
if (!array_key_exists($id, $this->accountIds)) {
$this->accountIds[$id] = (int)$item->account_id;
}
}
// get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */
foreach ($set as $item) {
$accountId = (int)$item->account_id;
$currencyId = (int)$item->data;
if (!array_key_exists($currencyId, $this->currencies)) {
$this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId);
}
$this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
}
}
}

View File

@@ -51,30 +51,29 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use function Safe\json_decode;
class RecurringEnrichment implements EnrichmentInterface
{
private Collection $collection;
private array $ids = [];
private array $accounts = [];
private Collection $collection;
// private array $transactionTypeIds = [];
// private array $transactionTypes = [];
private array $notes = [];
private array $repetitions = [];
private array $transactions = [];
private User $user;
private UserGroup $userGroup;
private string $language = 'en_US';
private array $currencyIds = [];
private array $foreignCurrencyIds = [];
private array $sourceAccountIds = [];
private array $destinationAccountIds = [];
private array $accounts = [];
private array $currencies = [];
private array $recurrenceIds = [];
private bool $convertToPrimary = false;
private array $currencies = [];
private array $currencyIds = [];
private array $destinationAccountIds = [];
private array $foreignCurrencyIds = [];
private array $ids = [];
private string $language = 'en_US';
private array $notes = [];
private readonly TransactionCurrency $primaryCurrency;
private bool $convertToPrimary = false;
private array $recurrenceIds = [];
private array $repetitions = [];
private array $sourceAccountIds = [];
private array $transactions = [];
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -98,7 +97,7 @@ class RecurringEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -107,139 +106,6 @@ class RecurringEnrichment implements EnrichmentInterface
return $collection->first();
}
public function setUser(User $user): void
{
$this->user = $user;
$this->setUserGroup($user->userGroup);
$this->getLanguage();
}
public function setUserGroup(UserGroup $userGroup): void
{
$this->userGroup = $userGroup;
}
private function collectIds(): void
{
/** @var Recurrence $recurrence */
foreach ($this->collection as $recurrence) {
$id = (int)$recurrence->id;
// $typeId = (int)$recurrence->transaction_type_id;
$this->ids[] = $id;
// $this->transactionTypeIds[$id] = $typeId;
}
$this->ids = array_unique($this->ids);
// collect transaction types.
// $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
// foreach ($transactionTypes as $transactionType) {
// $id = (int)$transactionType->id;
// $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
// }
}
private function collectRepetitions(): void
{
Log::debug('Start of enrichment: collectRepetitions()');
$repository = app(RecurringRepositoryInterface::class);
$repository->setUserGroup($this->userGroup);
$set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get();
/** @var RecurrenceRepetition $repetition */
foreach ($set as $repetition) {
$recurrence = $this->collection->filter(fn (Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first();
$fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date);
$id = (int)$repetition->recurrence_id;
$repId = (int)$repetition->id;
$this->repetitions[$id] ??= [];
// get the (future) occurrences for this specific type of repetition:
$amount = 'daily' === $repetition->repetition_type ? 9 : 5;
$set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
$occurrences = [];
/** @var Carbon $carbon */
foreach ($set as $carbon) {
$occurrences[] = $carbon->toAtomString();
}
$this->repetitions[$id][$repId] = [
'id' => (string)$repId,
'created_at' => $repetition->created_at->toAtomString(),
'updated_at' => $repetition->updated_at->toAtomString(),
'type' => $repetition->repetition_type,
'moment' => (string)$repetition->repetition_moment,
'skip' => (int)$repetition->repetition_skip,
'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend)->value,
'description' => $this->getRepetitionDescription($repetition),
'occurrences' => $occurrences,
];
}
Log::debug('End of enrichment: collectRepetitions()');
}
private function collectTransactions(): void
{
$set = RecurrenceTransaction::whereIn('recurrence_id', $this->ids)->get();
/** @var RecurrenceTransaction $transaction */
foreach ($set as $transaction) {
$id = (int)$transaction->recurrence_id;
$transactionId = (int)$transaction->id;
$this->recurrenceIds[$transactionId] = $id;
$this->transactions[$id] ??= [];
$amount = $transaction->amount;
$foreignAmount = $transaction->foreign_amount;
$this->transactions[$id][$transactionId] = [
'id' => (string)$transactionId,
// 'recurrence_id' => $id,
'transaction_currency_id' => (int)$transaction->transaction_currency_id,
'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id,
'source_id' => (int)$transaction->source_id,
'object_has_currency_setting' => true,
'destination_id' => (int)$transaction->destination_id,
'amount' => $amount,
'foreign_amount' => $foreignAmount,
'pc_amount' => null,
'pc_foreign_amount' => null,
'description' => $transaction->description,
'tags' => [],
'category_id' => null,
'category_name' => null,
'budget_id' => null,
'budget_name' => null,
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'subscription_id' => null,
'subscription_name' => null,
];
// collect all kinds of meta data to be collected later.
$this->currencyIds[$transactionId] = (int)$transaction->transaction_currency_id;
$this->sourceAccountIds[$transactionId] = (int)$transaction->source_id;
$this->destinationAccountIds[$transactionId] = (int)$transaction->destination_id;
if (null !== $transaction->foreign_currency_id) {
$this->foreignCurrencyIds[$transactionId] = (int)$transaction->foreign_currency_id;
}
}
}
private function appendCollectedData(): void
{
$this->collection = $this->collection->map(function (Recurrence $item) {
$id = (int)$item->id;
$meta = [
'notes' => $this->notes[$id] ?? null,
'repetitions' => array_values($this->repetitions[$id] ?? []),
'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])),
];
$item->meta = $meta;
return $item;
});
}
/**
* Parse the repetition in a string that is user readable.
* TODO duplicate with repository.
@@ -265,7 +131,7 @@ class RecurringEnrichment implements EnrichmentInterface
return (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $this->language);
}
if ('ndom' === $repetition->repetition_type) {
$parts = explode(',', $repetition->repetition_moment);
$parts = explode(',', $repetition->repetition_moment);
// first part is number of week, second is weekday.
$dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $this->language);
if ($repetition->repetition_skip > 0) {
@@ -282,7 +148,7 @@ class RecurringEnrichment implements EnrichmentInterface
}
// $diffInYears = (int)$today->diffInYears($repDate, true);
// $repDate->addYears($diffInYears); // technically not necessary.
$string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js'));
$string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js'));
return (string)trans('firefly.recurring_yearly', ['date' => $string], $this->language);
}
@@ -290,96 +156,32 @@ class RecurringEnrichment implements EnrichmentInterface
return '';
}
private function getLanguage(): void
public function setUser(User $user): void
{
/** @var Preference $preference */
$preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US'));
$language = $preference->data;
if (is_array($language)) {
$language = 'en_US';
}
$language = (string)$language;
$this->language = $language;
$this->user = $user;
$this->setUserGroup($user->userGroup);
$this->getLanguage();
}
private function collectCurrencies(): void
public function setUserGroup(UserGroup $userGroup): void
{
$all = array_merge(array_unique($this->currencyIds), array_unique($this->foreignCurrencyIds));
$currencies = TransactionCurrency::whereIn('id', array_unique($all))->get();
foreach ($currencies as $currency) {
$id = (int)$currency->id;
$this->currencies[$id] = $currency;
}
$this->userGroup = $userGroup;
}
private function processTransactions(array $transactions): array
private function appendCollectedData(): void
{
$return = [];
$converter = new ExchangeRateConverter();
foreach ($transactions as $transaction) {
$currencyId = $transaction['transaction_currency_id'];
$pcAmount = null;
$pcForeignAmount = null;
// set the same amount in the primary currency, if both are the same anyway.
if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) {
$pcAmount = $transaction['amount'];
}
// convert the amount to the primary currency, if it is not the same.
if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) {
$pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']);
}
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$foreignCurrencyId = $transaction['foreign_currency_id'];
if ($foreignCurrencyId !== $this->primaryCurrency->id) {
$pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']);
}
}
$this->collection = $this->collection->map(function (Recurrence $item) {
$id = (int)$item->id;
$meta = [
'notes' => $this->notes[$id] ?? null,
'repetitions' => array_values($this->repetitions[$id] ?? []),
'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])),
];
$transaction['pc_amount'] = $pcAmount;
$transaction['pc_foreign_amount'] = $pcForeignAmount;
$item->meta = $meta;
$sourceId = $transaction['source_id'];
$transaction['source_name'] = $this->accounts[$sourceId]->name;
$transaction['source_iban'] = $this->accounts[$sourceId]->iban;
$transaction['source_type'] = $this->accounts[$sourceId]->accountType->type;
$transaction['source_id'] = (string)$transaction['source_id'];
$destId = $transaction['destination_id'];
$transaction['destination_name'] = $this->accounts[$destId]->name;
$transaction['destination_iban'] = $this->accounts[$destId]->iban;
$transaction['destination_type'] = $this->accounts[$destId]->accountType->type;
$transaction['destination_id'] = (string)$transaction['destination_id'];
$transaction['currency_id'] = (string)$currencyId;
$transaction['currency_name'] = $this->currencies[$currencyId]->name;
$transaction['currency_code'] = $this->currencies[$currencyId]->code;
$transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol;
$transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
$transaction['primary_currency_id'] = (string)$this->primaryCurrency->id;
$transaction['primary_currency_name'] = $this->primaryCurrency->name;
$transaction['primary_currency_code'] = $this->primaryCurrency->code;
$transaction['primary_currency_symbol'] = $this->primaryCurrency->symbol;
$transaction['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places;
// $transaction['foreign_currency_id'] = null;
$transaction['foreign_currency_name'] = null;
$transaction['foreign_currency_code'] = null;
$transaction['foreign_currency_symbol'] = null;
$transaction['foreign_currency_decimal_places'] = null;
if (null !== $transaction['foreign_currency_id']) {
$currencyId = $transaction['foreign_currency_id'];
$transaction['foreign_currency_id'] = (string)$currencyId;
$transaction['foreign_currency_name'] = $this->currencies[$currencyId]->name;
$transaction['foreign_currency_code'] = $this->currencies[$currencyId]->code;
$transaction['foreign_currency_symbol'] = $this->currencies[$currencyId]->symbol;
$transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
}
unset($transaction['transaction_currency_id']);
$return[] = $transaction;
}
return $return;
return $item;
});
}
private function collectAccounts(): void
@@ -394,10 +196,183 @@ class RecurringEnrichment implements EnrichmentInterface
}
}
private function collectBillInfo(array $billIds): void
{
if (0 === count($billIds)) {
return;
}
$ids = Arr::pluck($billIds, 'bill_id');
$bills = Bill::whereIn('id', $ids)->get();
$mapped = [];
foreach ($bills as $bill) {
$mapped[(int)$bill->id] = $bill;
}
foreach ($billIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['subscription_name'] = $mapped[$info['bill_id']]->name ?? '';
}
}
private function collectBudgetInfo(array $budgetIds): void
{
if (0 === count($budgetIds)) {
return;
}
$ids = Arr::pluck($budgetIds, 'budget_id');
$categories = Budget::whereIn('id', $ids)->get();
$mapped = [];
foreach ($categories as $category) {
$mapped[(int)$category->id] = $category;
}
foreach ($budgetIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['budget_name'] = $mapped[$info['budget_id']]->name ?? '';
}
}
private function collectCategoryIdInfo(array $categoryIds): void
{
if (0 === count($categoryIds)) {
return;
}
$ids = Arr::pluck($categoryIds, 'category_id');
$categories = Category::whereIn('id', $ids)->get();
$mapped = [];
foreach ($categories as $category) {
$mapped[(int)$category->id] = $category;
}
foreach ($categoryIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['category_name'] = $mapped[$info['category_id']]->name ?? '';
}
}
/**
* TODO This method does look-up in a loop.
*/
private function collectCategoryNameInfo(array $categoryNames): void
{
if (0 === count($categoryNames)) {
return;
}
$factory = app(CategoryFactory::class);
$factory->setUser($this->user);
foreach ($categoryNames as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$category = $factory->findOrCreate(null, $info['category_name']);
if (null !== $category) {
$this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$category->id;
$this->transactions[$recurrenceId][$transactionId]['category_name'] = $category->name;
}
}
}
private function collectCurrencies(): void
{
$all = array_merge(array_unique($this->currencyIds), array_unique($this->foreignCurrencyIds));
$currencies = TransactionCurrency::whereIn('id', array_unique($all))->get();
foreach ($currencies as $currency) {
$id = (int)$currency->id;
$this->currencies[$id] = $currency;
}
}
private function collectIds(): void
{
/** @var Recurrence $recurrence */
foreach ($this->collection as $recurrence) {
$id = (int)$recurrence->id;
// $typeId = (int)$recurrence->transaction_type_id;
$this->ids[] = $id;
// $this->transactionTypeIds[$id] = $typeId;
}
$this->ids = array_unique($this->ids);
// collect transaction types.
// $transactionTypes = TransactionType::whereIn('id', array_unique($this->transactionTypeIds))->get();
// foreach ($transactionTypes as $transactionType) {
// $id = (int)$transactionType->id;
// $this->transactionTypes[$id] = TransactionTypeEnum::from($transactionType->type);
// }
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Recurrence::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 collectPiggyBankInfo(array $piggyBankIds): void
{
if (0 === count($piggyBankIds)) {
return;
}
$ids = Arr::pluck($piggyBankIds, 'piggy_bank_id');
$piggyBanks = PiggyBank::whereIn('id', $ids)->get();
$mapped = [];
foreach ($piggyBanks as $piggyBank) {
$mapped[(int)$piggyBank->id] = $piggyBank;
}
foreach ($piggyBankIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['piggy_bank_name'] = $mapped[$info['piggy_bank_id']]->name ?? '';
}
}
private function collectRepetitions(): void
{
Log::debug('Start of enrichment: collectRepetitions()');
$repository = app(RecurringRepositoryInterface::class);
$repository->setUserGroup($this->userGroup);
$set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get();
/** @var RecurrenceRepetition $repetition */
foreach ($set as $repetition) {
$recurrence = $this->collection->filter(fn(Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first();
$fromDate = clone($recurrence->latest_date ?? $recurrence->first_date);
$id = (int)$repetition->recurrence_id;
$repId = (int)$repetition->id;
$this->repetitions[$id] ??= [];
// get the (future) occurrences for this specific type of repetition:
$amount = 'daily' === $repetition->repetition_type ? 9 : 5;
$set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
$occurrences = [];
/** @var Carbon $carbon */
foreach ($set as $carbon) {
$occurrences[] = $carbon->toAtomString();
}
$this->repetitions[$id][$repId] = [
'id' => (string)$repId,
'created_at' => $repetition->created_at->toAtomString(),
'updated_at' => $repetition->updated_at->toAtomString(),
'type' => $repetition->repetition_type,
'moment' => (string)$repetition->repetition_moment,
'skip' => (int)$repetition->repetition_skip,
'weekend' => RecurrenceRepetitionWeekend::from((int)$repetition->weekend)->value,
'description' => $this->getRepetitionDescription($repetition),
'occurrences' => $occurrences,
];
}
Log::debug('End of enrichment: collectRepetitions()');
}
private function collectTransactionMetaData(): void
{
$ids = array_keys($this->transactions);
$meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get();
$ids = array_keys($this->transactions);
$meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get();
// other meta-data to be collected:
$billIds = [];
$piggyBankIds = [];
@@ -409,8 +384,8 @@ class RecurringEnrichment implements EnrichmentInterface
$transactionId = (int)$entry->rt_id;
// this should refer to another array, were rtIds can be used to find the recurrence.
$recurrenceId = $this->recurrenceIds[$transactionId] ?? 0;
$name = (string)($entry->name ?? '');
$recurrenceId = $this->recurrenceIds[$transactionId] ?? 0;
$name = (string)($entry->name ?? '');
if (0 === $recurrenceId) {
Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId));
@@ -504,109 +479,132 @@ class RecurringEnrichment implements EnrichmentInterface
$this->collectBudgetInfo($budgetIds);
}
private function collectBillInfo(array $billIds): void
private function collectTransactions(): void
{
if (0 === count($billIds)) {
return;
}
$ids = Arr::pluck($billIds, 'bill_id');
$bills = Bill::whereIn('id', $ids)->get();
$mapped = [];
foreach ($bills as $bill) {
$mapped[(int)$bill->id] = $bill;
}
foreach ($billIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['subscription_name'] = $mapped[$info['bill_id']]->name ?? '';
}
}
$set = RecurrenceTransaction::whereIn('recurrence_id', $this->ids)->get();
private function collectPiggyBankInfo(array $piggyBankIds): void
{
if (0 === count($piggyBankIds)) {
return;
}
$ids = Arr::pluck($piggyBankIds, 'piggy_bank_id');
$piggyBanks = PiggyBank::whereIn('id', $ids)->get();
$mapped = [];
foreach ($piggyBanks as $piggyBank) {
$mapped[(int)$piggyBank->id] = $piggyBank;
}
foreach ($piggyBankIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['piggy_bank_name'] = $mapped[$info['piggy_bank_id']]->name ?? '';
}
}
/** @var RecurrenceTransaction $transaction */
foreach ($set as $transaction) {
$id = (int)$transaction->recurrence_id;
$transactionId = (int)$transaction->id;
$this->recurrenceIds[$transactionId] = $id;
$this->transactions[$id] ??= [];
$amount = $transaction->amount;
$foreignAmount = $transaction->foreign_amount;
private function collectCategoryIdInfo(array $categoryIds): void
{
if (0 === count($categoryIds)) {
return;
}
$ids = Arr::pluck($categoryIds, 'category_id');
$categories = Category::whereIn('id', $ids)->get();
$mapped = [];
foreach ($categories as $category) {
$mapped[(int)$category->id] = $category;
}
foreach ($categoryIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['category_name'] = $mapped[$info['category_id']]->name ?? '';
}
}
$this->transactions[$id][$transactionId] = [
'id' => (string)$transactionId,
// 'recurrence_id' => $id,
'transaction_currency_id' => (int)$transaction->transaction_currency_id,
'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id,
'source_id' => (int)$transaction->source_id,
'object_has_currency_setting' => true,
'destination_id' => (int)$transaction->destination_id,
'amount' => $amount,
'foreign_amount' => $foreignAmount,
'pc_amount' => null,
'pc_foreign_amount' => null,
'description' => $transaction->description,
'tags' => [],
'category_id' => null,
'category_name' => null,
'budget_id' => null,
'budget_name' => null,
'piggy_bank_id' => null,
'piggy_bank_name' => null,
'subscription_id' => null,
'subscription_name' => null,
/**
* TODO This method does look-up in a loop.
*/
private function collectCategoryNameInfo(array $categoryNames): void
{
if (0 === count($categoryNames)) {
return;
}
$factory = app(CategoryFactory::class);
$factory->setUser($this->user);
foreach ($categoryNames as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$category = $factory->findOrCreate(null, $info['category_name']);
if (null !== $category) {
$this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$category->id;
$this->transactions[$recurrenceId][$transactionId]['category_name'] = $category->name;
];
// collect all kinds of meta data to be collected later.
$this->currencyIds[$transactionId] = (int)$transaction->transaction_currency_id;
$this->sourceAccountIds[$transactionId] = (int)$transaction->source_id;
$this->destinationAccountIds[$transactionId] = (int)$transaction->destination_id;
if (null !== $transaction->foreign_currency_id) {
$this->foreignCurrencyIds[$transactionId] = (int)$transaction->foreign_currency_id;
}
}
}
private function collectBudgetInfo(array $budgetIds): void
private function getLanguage(): void
{
if (0 === count($budgetIds)) {
return;
}
$ids = Arr::pluck($budgetIds, 'budget_id');
$categories = Budget::whereIn('id', $ids)->get();
$mapped = [];
foreach ($categories as $category) {
$mapped[(int)$category->id] = $category;
}
foreach ($budgetIds as $info) {
$recurrenceId = $info['recurrence_id'];
$transactionId = $info['transaction_id'];
$this->transactions[$recurrenceId][$transactionId]['budget_name'] = $mapped[$info['budget_id']]->name ?? '';
/** @var Preference $preference */
$preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US'));
$language = $preference->data;
if (is_array($language)) {
$language = 'en_US';
}
$language = (string)$language;
$this->language = $language;
}
private function collectNotes(): void
private function processTransactions(array $transactions): array
{
$notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text'];
$return = [];
$converter = new ExchangeRateConverter();
foreach ($transactions as $transaction) {
$currencyId = $transaction['transaction_currency_id'];
$pcAmount = null;
$pcForeignAmount = null;
// set the same amount in the primary currency, if both are the same anyway.
if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) {
$pcAmount = $transaction['amount'];
}
// convert the amount to the primary currency, if it is not the same.
if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) {
$pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']);
}
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
$foreignCurrencyId = $transaction['foreign_currency_id'];
if ($foreignCurrencyId !== $this->primaryCurrency->id) {
$pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']);
}
}
$transaction['pc_amount'] = $pcAmount;
$transaction['pc_foreign_amount'] = $pcForeignAmount;
$sourceId = $transaction['source_id'];
$transaction['source_name'] = $this->accounts[$sourceId]->name;
$transaction['source_iban'] = $this->accounts[$sourceId]->iban;
$transaction['source_type'] = $this->accounts[$sourceId]->accountType->type;
$transaction['source_id'] = (string)$transaction['source_id'];
$destId = $transaction['destination_id'];
$transaction['destination_name'] = $this->accounts[$destId]->name;
$transaction['destination_iban'] = $this->accounts[$destId]->iban;
$transaction['destination_type'] = $this->accounts[$destId]->accountType->type;
$transaction['destination_id'] = (string)$transaction['destination_id'];
$transaction['currency_id'] = (string)$currencyId;
$transaction['currency_name'] = $this->currencies[$currencyId]->name;
$transaction['currency_code'] = $this->currencies[$currencyId]->code;
$transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol;
$transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
$transaction['primary_currency_id'] = (string)$this->primaryCurrency->id;
$transaction['primary_currency_name'] = $this->primaryCurrency->name;
$transaction['primary_currency_code'] = $this->primaryCurrency->code;
$transaction['primary_currency_symbol'] = $this->primaryCurrency->symbol;
$transaction['primary_currency_decimal_places'] = $this->primaryCurrency->decimal_places;
// $transaction['foreign_currency_id'] = null;
$transaction['foreign_currency_name'] = null;
$transaction['foreign_currency_code'] = null;
$transaction['foreign_currency_symbol'] = null;
$transaction['foreign_currency_decimal_places'] = null;
if (null !== $transaction['foreign_currency_id']) {
$currencyId = $transaction['foreign_currency_id'];
$transaction['foreign_currency_id'] = (string)$currencyId;
$transaction['foreign_currency_name'] = $this->currencies[$currencyId]->name;
$transaction['foreign_currency_code'] = $this->currencies[$currencyId]->code;
$transaction['foreign_currency_symbol'] = $this->currencies[$currencyId]->symbol;
$transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
}
unset($transaction['transaction_currency_id']);
$return[] = $transaction;
}
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
return $return;
}
}

View File

@@ -46,20 +46,20 @@ use Illuminate\Support\Facades\Log;
class SubscriptionEnrichment implements EnrichmentInterface
{
private User $user;
private UserGroup $userGroup; // @phpstan-ignore-line
private Collection $collection;
private BillDateCalculator $calculator;
private Collection $collection; // @phpstan-ignore-line
private readonly bool $convertToPrimary;
private ?Carbon $start = null;
private ?Carbon $end = null;
private array $subscriptionIds = [];
private array $objectGroups = [];
private array $mappedObjects = [];
private array $paidDates = [];
private array $notes = [];
private array $payDates = [];
private ?Carbon $end = null;
private array $mappedObjects = [];
private array $notes = [];
private array $objectGroups = [];
private array $paidDates = [];
private array $payDates = [];
private readonly TransactionCurrency $primaryCurrency;
private BillDateCalculator $calculator;
private ?Carbon $start = null;
private array $subscriptionIds = [];
private User $user;
private UserGroup $userGroup;
public function __construct()
{
@@ -86,11 +86,11 @@ class SubscriptionEnrichment implements EnrichmentInterface
$paidDates = $this->paidDates;
$payDates = $this->payDates;
$this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) {
$id = (int)$item->id;
$currency = $item->transactionCurrency;
$nem = $this->getNextExpectedMatch($payDates[$id] ?? []);
$id = (int)$item->id;
$currency = $item->transactionCurrency;
$nem = $this->getNextExpectedMatch($payDates[$id] ?? []);
$meta = [
$meta = [
'notes' => null,
'object_group_id' => null,
'object_group_title' => null,
@@ -101,7 +101,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
'nem' => $nem,
'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []),
];
$amounts = [
$amounts = [
'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places),
'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places),
'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places),
@@ -142,7 +142,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -151,17 +151,14 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $collection->first();
}
private function collectNotes(): void
public function setEnd(?Carbon $end): void
{
$notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Bill::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)));
$this->end = $end;
}
public function setStart(?Carbon $start): void
{
$this->start = $start;
}
public function setUser(User $user): void
@@ -175,24 +172,49 @@ class SubscriptionEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function collectSubscriptionIds(): void
/**
* Returns the latest date in the set, or start when set is empty.
*/
protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon
{
/** @var Bill $bill */
foreach ($this->collection as $bill) {
$this->subscriptionIds[] = (int)$bill->id;
$filtered = $dates->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id));
if (0 === $filtered->count()) {
return $default;
}
$this->subscriptionIds = array_unique($this->subscriptionIds);
$latest = $filtered->first()->date;
/** @var TransactionJournal $journal */
foreach ($filtered as $journal) {
if ($journal->date->gte($latest)) {
$latest = $journal->date;
}
}
return $latest;
}
private function collectNotes(): void
{
$notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds)
->whereNotNull('notes.text')
->where('notes.text', '!=', '')
->where('noteable_type', Bill::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 collectObjectGroups(): void
{
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->subscriptionIds)
->where('object_groupable_type', Bill::class)
->get(['object_groupable_id', 'object_group_id'])
;
$set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->subscriptionIds)
->where('object_groupable_type', Bill::class)
->get(['object_groupable_id', 'object_group_id']);
$ids = array_unique($set->pluck('object_group_id')->toArray());
$ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
@@ -220,13 +242,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
// 2023-07-18 this particular date is used to search for the last paid date.
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
/** @var Carbon $start */
$start = clone $this->start;
$searchStart = clone $start;
$start = clone $this->start;
$searchStart = clone $start;
$start->subDay();
/** @var Carbon $end */
$end = clone $this->end;
$searchEnd = clone $end;
$end = clone $this->end;
$searchEnd = clone $end;
// move the search dates to the start of the day.
$searchStart->startOfDay();
@@ -235,13 +257,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s')));
// Get from database when bills were paid.
$set = $this->user->transactionJournals()
->whereIn('bill_id', $this->subscriptionIds)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id')
->where('transactions.amount', '>', 0)
->before($searchEnd)->after($searchStart)->get(
$set = $this->user->transactionJournals()
->whereIn('bill_id', $this->subscriptionIds)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id')
->where('transactions.amount', '>', 0)
->before($searchEnd)->after($searchStart)->get(
[
'transaction_journals.id',
'transaction_journals.date',
@@ -258,25 +280,24 @@ class SubscriptionEnrichment implements EnrichmentInterface
'transactions.amount',
'transactions.foreign_amount',
]
)
;
);
Log::debug(sprintf('Count %d entries in set', $set->count()));
// for each bill, do a loop.
$converter = new ExchangeRateConverter();
$converter = new ExchangeRateConverter();
/** @var Bill $subscription */
foreach ($this->collection as $subscription) {
// Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date.
Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d')));
$lastPaidDate = $this->lastPaidDate($subscription, $set, $start);
$lastPaidDate = $this->lastPaidDate($subscription, $set, $start);
Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d')));
// At this point the "next match" is exactly after the last time the bill was paid.
$result = [];
$filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
$result = [];
$filtered = $set->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
foreach ($filtered as $entry) {
$array = [
$array = [
'transaction_group_id' => (string)$entry->transaction_group_id,
'transaction_journal_id' => (string)$entry->id,
'date' => $entry->date->toAtomString(),
@@ -329,37 +350,47 @@ class SubscriptionEnrichment implements EnrichmentInterface
}
public function setStart(?Carbon $start): void
private function collectPayDates(): void
{
$this->start = $start;
}
if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) {
Log::debug('Parameters are NULL, set empty array');
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
/**
* Returns the latest date in the set, or start when set is empty.
*/
protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon
{
$filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id));
if (0 === $filtered->count()) {
return $default;
return;
}
$latest = $filtered->first()->date;
/** @var TransactionJournal $journal */
foreach ($filtered as $journal) {
if ($journal->date->gte($latest)) {
$latest = $journal->date;
/** @var Bill $subscription */
foreach ($this->collection as $subscription) {
$id = (int)$subscription->id;
$lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []);
$payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate);
$payDatesFormatted = [];
foreach ($payDates as $string) {
$date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'));
if (!$date instanceof Carbon) {
$date = today(config('app.timezone'));
}
$payDatesFormatted[] = $date->toAtomString();
}
$this->payDates[$id] = $payDatesFormatted;
}
}
return $latest;
private function collectSubscriptionIds(): void
{
/** @var Bill $bill */
foreach ($this->collection as $bill) {
$this->subscriptionIds[] = (int)$bill->id;
}
$this->subscriptionIds = array_unique($this->subscriptionIds);
}
private function filterPaidDates(array $entries): array
{
return array_map(function (array $entry) {
unset($entry['date_object']);
return $entry;
}, $entries);
}
private function getLastPaidDate(array $paidData): ?Carbon
@@ -386,40 +417,6 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $return;
}
private function collectPayDates(): void
{
if (!$this->start instanceof Carbon || !$this->end instanceof Carbon) {
Log::debug('Parameters are NULL, set empty array');
return;
}
/** @var Bill $subscription */
foreach ($this->collection as $subscription) {
$id = (int)$subscription->id;
$lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []);
$payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate);
$payDatesFormatted = [];
foreach ($payDates as $string) {
$date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'));
if (!$date instanceof Carbon) {
$date = today(config('app.timezone'));
}
$payDatesFormatted[] = $date->toAtomString();
}
$this->payDates[$id] = $payDatesFormatted;
}
}
private function filterPaidDates(array $entries): array
{
return array_map(function (array $entry) {
unset($entry['date_object']);
return $entry;
}, $entries);
}
private function getNextExpectedMatch(array $payDates): ?Carbon
{
// next expected match

View File

@@ -45,17 +45,17 @@ use Override;
class TransactionGroupEnrichment implements EnrichmentInterface
{
private array $attachmentCount = [];
private Collection $collection;
private readonly array $dateFields;
private array $journalIds = [];
private array $locations = [];
private array $metaData = [];
private array $notes = [];
private array $tags = [];
private User $user; // @phpstan-ignore-line
private array $attachmentCount = [];
private Collection $collection;
private readonly array $dateFields;
private array $journalIds = [];
private array $locations = [];
private array $metaData = [];
private array $notes = [];
private readonly TransactionCurrency $primaryCurrency;
private UserGroup $userGroup; // @phpstan-ignore-line
private array $tags = []; // @phpstan-ignore-line
private User $user;
private UserGroup $userGroup; // @phpstan-ignore-line
public function __construct()
{
@@ -63,20 +63,6 @@ class TransactionGroupEnrichment implements EnrichmentInterface
$this->primaryCurrency = Amount::getPrimaryCurrency();
}
#[Override]
public function enrichSingle(array|Model $model): array|TransactionGroup
{
Log::debug(__METHOD__);
if (is_array($model)) {
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
}
throw new FireflyException('Cannot enrich single model.');
}
#[Override]
public function enrich(Collection $collection): Collection
{
@@ -96,119 +82,55 @@ class TransactionGroupEnrichment implements EnrichmentInterface
return $this->collection;
}
private function collectJournalIds(): void
#[Override]
public function enrichSingle(array | Model $model): array | TransactionGroup
{
/** @var array $group */
foreach ($this->collection as $group) {
foreach ($group['transactions'] as $journal) {
$this->journalIds[] = $journal['transaction_journal_id'];
}
Log::debug(__METHOD__);
if (is_array($model)) {
$collection = new Collection()->push($model);
$collection = $this->enrich($collection);
return $collection->first();
}
$this->journalIds = array_unique($this->journalIds);
throw new FireflyException('Cannot enrich single model.');
}
private function collectNotes(): void
public function setUser(User $user): 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)));
$this->user = $user;
$this->userGroup = $user->userGroup;
}
private function collectTags(): void
public function setUserGroup(UserGroup $userGroup): 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)) {
// Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
$this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone'));
// Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
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'];
}
$this->userGroup = $userGroup;
}
private function appendCollectedData(): void
{
$notes = $this->notes;
$tags = $this->tags;
$metaData = $this->metaData;
$locations = $this->locations;
$attachmentCount = $this->attachmentCount;
$primaryCurrency = $this->primaryCurrency;
$notes = $this->notes;
$tags = $this->tags;
$metaData = $this->metaData;
$locations = $this->locations;
$attachmentCount = $this->attachmentCount;
$primaryCurrency = $this->primaryCurrency;
$this->collection = $this->collection->map(function (array $item) use ($primaryCurrency, $notes, $tags, $metaData, $locations, $attachmentCount) {
foreach ($item['transactions'] as $index => $transaction) {
$journalId = (int) $transaction['transaction_journal_id'];
$journalId = (int)$transaction['transaction_journal_id'];
// attach notes if they exist:
$item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null;
$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] : [];
$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'] = [
$item['transactions'][$index]['location'] = [
'latitude' => null,
'longitude' => null,
'zoom_level' => null,
@@ -216,16 +138,16 @@ class TransactionGroupEnrichment implements EnrichmentInterface
// primary currency
$item['transactions'][$index]['primary_currency'] = [
'id' => (string) $primaryCurrency->id,
'code' => $primaryCurrency->code,
'name' => $primaryCurrency->name,
'symbol' => $primaryCurrency->symbol,
'decimal_places' => $primaryCurrency->decimal_places,
'id' => (string)$primaryCurrency->id,
'code' => $primaryCurrency->code,
'name' => $primaryCurrency->name,
'symbol' => $primaryCurrency->symbol,
'decimal_places' => $primaryCurrency->decimal_places,
];
// append meta data
$item['transactions'][$index]['meta'] = [];
$item['transactions'][$index]['meta_date'] = [];
$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) {
@@ -248,14 +170,88 @@ class TransactionGroupEnrichment implements EnrichmentInterface
});
}
public function setUser(User $user): void
private function collectAttachmentCount(): void
{
$this->user = $user;
$this->userGroup = $user->userGroup;
// 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'];
}
}
public function setUserGroup(UserGroup $userGroup): void
private function collectJournalIds(): void
{
$this->userGroup = $userGroup;
/** @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);
}
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 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)) {
// Log::debug(sprintf('Meta data for "%s" is a date : "%s"', $name, $data));
$this->metaData[$entry['transaction_journal_id']][$name] = Carbon::parse($data, config('app.timezone'));
// Log::debug(sprintf('Meta data for "%s" converts to: "%s"', $name, $this->metaData[$entry['transaction_journal_id']][$name]->toW3CString()));
continue;
}
$this->metaData[(int)$entry['transaction_journal_id']][$name] = $data;
}
}
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'];
}
}
}

View File

@@ -43,16 +43,15 @@ use stdClass;
class WebhookEnrichment implements EnrichmentInterface
{
private Collection $collection;
private User $user; // @phpstan-ignore-line
private UserGroup $userGroup; // @phpstan-ignore-line
private array $ids = [];
private array $deliveries = [];
private array $responses = [];
private array $triggers = [];
private array $webhookDeliveries = [];
private array $webhookResponses = [];
private array $webhookTriggers = [];
private array $deliveries = []; // @phpstan-ignore-line
private array $ids = []; // @phpstan-ignore-line
private array $responses = [];
private array $triggers = [];
private User $user;
private UserGroup $userGroup;
private array $webhookDeliveries = [];
private array $webhookResponses = [];
private array $webhookTriggers = [];
public function enrich(Collection $collection): Collection
{
@@ -67,7 +66,7 @@ class WebhookEnrichment implements EnrichmentInterface
return $this->collection;
}
public function enrichSingle(array|Model $model): array|Model
public function enrichSingle(array | Model $model): array | Model
{
Log::debug(__METHOD__);
$collection = new Collection()->push($model);
@@ -86,6 +85,20 @@ class WebhookEnrichment implements EnrichmentInterface
$this->userGroup = $userGroup;
}
private function appendCollectedInfo(): void
{
$this->collection = $this->collection->map(function (Webhook $item) {
$meta = [
'deliveries' => $this->webhookDeliveries[$item->id] ?? [],
'responses' => $this->webhookResponses[$item->id] ?? [],
'triggers' => $this->webhookTriggers[$item->id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
private function collectIds(): void
{
/** @var Webhook $webhook */
@@ -147,18 +160,4 @@ class WebhookEnrichment implements EnrichmentInterface
$this->webhookTriggers[$id][] = WebhookTriggerEnum::from($this->triggers[$triggerId])->name;
}
}
private function appendCollectedInfo(): void
{
$this->collection = $this->collection->map(function (Webhook $item) {
$meta = [
'deliveries' => $this->webhookDeliveries[$item->id] ?? [],
'responses' => $this->webhookResponses[$item->id] ?? [],
'triggers' => $this->webhookTriggers[$item->id] ?? [],
];
$item->meta = $meta;
return $item;
});
}
}