mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-12 23:45:10 +00:00
Optimize recurrence enrichment.
This commit is contained in:
@@ -112,6 +112,13 @@ class ShowController extends Controller
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new RecurringEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$recurrence = $enrichment->enrichSingle($recurrence);
|
||||
|
||||
/** @var RecurrenceTransformer $transformer */
|
||||
$transformer = app(RecurrenceTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
@@ -5,15 +5,29 @@ namespace FireflyIII\Support\JsonApi\Enrichments;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\RecurrenceRepetitionWeekend;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\CategoryFactory;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\PiggyBank;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Recurrence;
|
||||
use FireflyIII\Models\RecurrenceRepetition;
|
||||
use FireflyIII\Models\RecurrenceTransaction;
|
||||
use FireflyIII\Models\RecurrenceTransactionMeta;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -23,10 +37,27 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
private array $ids = [];
|
||||
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 TransactionCurrency $primaryCurrency;
|
||||
private bool $convertToPrimary = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||
$this->convertToPrimary = Amount::convertToPrimary();
|
||||
}
|
||||
|
||||
public function enrich(Collection $collection): Collection
|
||||
{
|
||||
@@ -34,6 +65,10 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
$this->collectIds();
|
||||
$this->collectRepetitions();
|
||||
$this->collectTransactions();
|
||||
$this->collectCurrencies();
|
||||
$this->collectNotes();
|
||||
$this->collectAccounts();
|
||||
$this->collectTransactionMetaData();
|
||||
|
||||
$this->appendCollectedData();
|
||||
|
||||
@@ -82,6 +117,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
|
||||
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();
|
||||
@@ -115,10 +151,53 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
'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
|
||||
@@ -126,7 +205,9 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
$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;
|
||||
@@ -196,4 +277,297 @@ class RecurringEnrichment implements EnrichmentInterface
|
||||
$language = (string)$language;
|
||||
$this->language = $language;
|
||||
}
|
||||
|
||||
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 processTransactions(array $transactions): array
|
||||
{
|
||||
$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']) {
|
||||
$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;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function collectAccounts(): void
|
||||
{
|
||||
$all = array_merge(array_unique($this->sourceAccountIds), array_unique($this->destinationAccountIds));
|
||||
$accounts = Account::with(['accountType'])->whereIn('id', array_unique($all))->get();
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
$id = (int)$account->id;
|
||||
$this->accounts[$id] = $account;
|
||||
}
|
||||
}
|
||||
|
||||
private function collectTransactionMetaData(): void
|
||||
{
|
||||
$ids = array_keys($this->transactions);
|
||||
$meta = RecurrenceTransactionMeta::whereIn('rt_id', $ids)->get();
|
||||
// other meta-data to be collected:
|
||||
$billIds = [];
|
||||
$piggyBankIds = [];
|
||||
$categoryIds = [];
|
||||
$categoryNames = [];
|
||||
$budgetIds = [];
|
||||
foreach ($meta as $entry) {
|
||||
$id = (int)$entry->id;
|
||||
$transactionId = (int)$entry->rt_id;
|
||||
$recurrenceId = $this->recurrenceIds[$transactionId];
|
||||
$name = (string)$entry->name;
|
||||
|
||||
switch ($name) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $name));
|
||||
|
||||
case 'bill_id':
|
||||
if ((int)$entry->value > 0) {
|
||||
$this->transactions[$recurrenceId][$transactionId]['subscription_id'] = $entry->value;
|
||||
if (!array_key_exists($id, $billIds)) {
|
||||
$billIds[$id] = [
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'transaction_id' => $transactionId,
|
||||
'bill_id' => (int)$entry->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'tags':
|
||||
$this->transactions[$recurrenceId][$transactionId]['tags'] = json_decode((string)$entry->value);
|
||||
|
||||
break;
|
||||
|
||||
case 'piggy_bank_id':
|
||||
if ((int)$entry->value > 0) {
|
||||
$this->transactions[$recurrenceId][$transactionId]['piggy_bank_id'] = (string)$entry->value;
|
||||
if (!array_key_exists($id, $piggyBankIds)) {
|
||||
$piggyBankIds[$id] = [
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'transaction_id' => $transactionId,
|
||||
'piggy_bank_id' => (int)$entry->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'category_id':
|
||||
if ((int)$entry->value > 0) {
|
||||
$this->transactions[$recurrenceId][$transactionId]['category_id'] = (string)$entry->value;
|
||||
if (!array_key_exists($id, $categoryIds)) {
|
||||
$categoryIds[$id] = [
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'transaction_id' => $transactionId,
|
||||
'category_id' => (int)$entry->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'category_name':
|
||||
if ('' !== (string)$entry->value) {
|
||||
$this->transactions[$recurrenceId][$transactionId]['category_name'] = (string)$entry->value;
|
||||
if (!array_key_exists($id, $categoryIds)) {
|
||||
$categoryNames[$id] = [
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'transaction_id' => $transactionId,
|
||||
'category_name' => $entry->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'budget_id':
|
||||
if ((int)$entry->value > 0) {
|
||||
$this->transactions[$recurrenceId][$transactionId]['budget_id'] = (string)$entry->value;
|
||||
if (!array_key_exists($id, $budgetIds)) {
|
||||
$budgetIds[$id] = [
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'transaction_id' => $transactionId,
|
||||
'budget_id' => (int)$entry->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->collectBillInfo($billIds);
|
||||
$this->collectPiggyBankInfo($piggyBankIds);
|
||||
$this->collectCategoryIdInfo($categoryIds);
|
||||
$this->collectCategoryNameInfo($categoryNames);
|
||||
$this->collectBudgetInfo($budgetIds);
|
||||
}
|
||||
|
||||
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 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 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 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 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)));
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Support\Repositories\Recurring;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class CalculateXOccurrencesSince
|
||||
@@ -37,7 +38,7 @@ trait CalculateXOccurrencesSince
|
||||
*/
|
||||
protected function getXDailyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$return = [];
|
||||
$mutator = clone $date;
|
||||
$total = 0;
|
||||
@@ -62,7 +63,7 @@ trait CalculateXOccurrencesSince
|
||||
*/
|
||||
protected function getXMonthlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count));
|
||||
Log::debug(sprintf('Now in %s(%s, %s, %d)', __METHOD__, $date->format('Y-m-d'), $afterDate->format('Y-m-d'), $count));
|
||||
$return = [];
|
||||
$mutator = clone $date;
|
||||
$total = 0;
|
||||
@@ -70,24 +71,25 @@ trait CalculateXOccurrencesSince
|
||||
$dayOfMonth = (int) $moment;
|
||||
$dayOfMonth = 0 === $dayOfMonth ? 1 : $dayOfMonth;
|
||||
if ($mutator->day > $dayOfMonth) {
|
||||
app('log')->debug(sprintf('%d is after %d, add a month. Mutator is now', $mutator->day, $dayOfMonth));
|
||||
Log::debug(sprintf('%d is after %d, add a month. Mutator is now...', $mutator->day, $dayOfMonth));
|
||||
// day has passed already, add a month.
|
||||
$mutator->addMonth();
|
||||
app('log')->debug(sprintf('%s', $mutator->format('Y-m-d')));
|
||||
Log::debug(sprintf('%s', $mutator->toAtomString()));
|
||||
}
|
||||
|
||||
while ($total < $count) {
|
||||
$domCorrected = min($dayOfMonth, $mutator->daysInMonth);
|
||||
$mutator->day = $domCorrected;
|
||||
app('log')->debug(sprintf('Mutator is now %s', $mutator->format('Y-m-d')));
|
||||
$mutator->setTime(0,0,0);
|
||||
if (0 === $attempts % $skipMod && $mutator->gte($afterDate)) {
|
||||
app('log')->debug('Is added to the list.');
|
||||
Log::debug(sprintf('Mutator is now %s and is added to the list.', $mutator->toAtomString()));
|
||||
$return[] = clone $mutator;
|
||||
++$total;
|
||||
}
|
||||
++$attempts;
|
||||
$mutator = $mutator->endOfMonth()->addDay();
|
||||
}
|
||||
Log::debug('Collected enough occurrences.');
|
||||
|
||||
return $return;
|
||||
}
|
||||
@@ -100,7 +102,7 @@ trait CalculateXOccurrencesSince
|
||||
*/
|
||||
protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$return = [];
|
||||
$total = 0;
|
||||
$attempts = 0;
|
||||
@@ -134,7 +136,7 @@ trait CalculateXOccurrencesSince
|
||||
*/
|
||||
protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$return = [];
|
||||
$total = 0;
|
||||
$attempts = 0;
|
||||
@@ -173,7 +175,7 @@ trait CalculateXOccurrencesSince
|
||||
*/
|
||||
protected function getXYearlyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
|
||||
{
|
||||
app('log')->debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod));
|
||||
Log::debug(sprintf('Now in %s(%s, %d, %d, %s)', __METHOD__, $date->format('Y-m-d'), $date->format('Y-m-d'), $count, $skipMod));
|
||||
$return = [];
|
||||
$mutator = clone $date;
|
||||
$total = 0;
|
||||
@@ -181,19 +183,19 @@ trait CalculateXOccurrencesSince
|
||||
$date = new Carbon($moment);
|
||||
$date->year = $mutator->year;
|
||||
if ($mutator > $date) {
|
||||
app('log')->debug(
|
||||
Log::debug(
|
||||
sprintf('mutator (%s) > date (%s), so add a year to date (%s)', $mutator->format('Y-m-d'), $date->format('Y-m-d'), $date->format('Y-m-d'))
|
||||
);
|
||||
$date->addYear();
|
||||
app('log')->debug(sprintf('Date is now %s', $date->format('Y-m-d')));
|
||||
Log::debug(sprintf('Date is now %s', $date->format('Y-m-d')));
|
||||
}
|
||||
$obj = clone $date;
|
||||
while ($total < $count) {
|
||||
app('log')->debug(sprintf('total (%d) < count (%d) so go.', $total, $count));
|
||||
app('log')->debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod));
|
||||
app('log')->debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true)));
|
||||
Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count));
|
||||
Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod));
|
||||
Log::debug(sprintf('Obj (%s) gte afterdate (%s)? %s', $obj->format('Y-m-d'), $afterDate->format('Y-m-d'), var_export($obj->gte($afterDate), true)));
|
||||
if (0 === $attempts % $skipMod && $obj->gte($afterDate)) {
|
||||
app('log')->debug('All conditions true, add obj.');
|
||||
Log::debug('All conditions true, add obj.');
|
||||
$return[] = clone $obj;
|
||||
++$total;
|
||||
}
|
||||
|
@@ -45,22 +45,12 @@ use function Safe\json_decode;
|
||||
*/
|
||||
class RecurrenceTransformer extends AbstractTransformer
|
||||
{
|
||||
private readonly BillRepositoryInterface $billRepos;
|
||||
private readonly BudgetRepositoryInterface $budgetRepos;
|
||||
private readonly CategoryFactory $factory;
|
||||
private readonly PiggyBankRepositoryInterface $piggyRepos;
|
||||
private readonly RecurringRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* RecurrenceTransformer constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->repository = app(RecurringRepositoryInterface::class);
|
||||
$this->piggyRepos = app(PiggyBankRepositoryInterface::class);
|
||||
$this->factory = app(CategoryFactory::class);
|
||||
$this->budgetRepos = app(BudgetRepositoryInterface::class);
|
||||
$this->billRepos = app(BillRepositoryInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,15 +61,8 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
public function transform(Recurrence $recurrence): array
|
||||
{
|
||||
Log::debug('Now in Recurrence::transform()');
|
||||
$this->repository->setUser($recurrence->user);
|
||||
$this->piggyRepos->setUser($recurrence->user);
|
||||
$this->factory->setUser($recurrence->user);
|
||||
|
||||
$this->budgetRepos->setUser($recurrence->user);
|
||||
Log::debug('Set user.');
|
||||
|
||||
$shortType = (string)config(sprintf('firefly.transactionTypesToShort.%s', $recurrence->transactionType->type));
|
||||
$notes = $this->repository->getNoteText($recurrence);
|
||||
$reps = 0 === (int)$recurrence->repetitions ? null : (int)$recurrence->repetitions;
|
||||
Log::debug('Get basic data.');
|
||||
|
||||
@@ -97,10 +80,9 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
'apply_rules' => $recurrence->apply_rules,
|
||||
'active' => $recurrence->active,
|
||||
'nr_of_repetitions' => $reps,
|
||||
'notes' => '' === $notes ? null : $notes,
|
||||
'new_repetitions' => $recurrence->meta['repetitions'],
|
||||
'repetitions' => $this->getRepetitions($recurrence),
|
||||
'transactions' => $this->getTransactions($recurrence),
|
||||
'notes' => $recurrence->meta['notes'],
|
||||
'repetitions' => $recurrence->meta['repetitions'],
|
||||
'new_transactions' => $recurrence->meta['transactions'],
|
||||
'links' => [
|
||||
[
|
||||
'rel' => 'self',
|
||||
@@ -109,208 +91,4 @@ class RecurrenceTransformer extends AbstractTransformer
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getRepetitions(Recurrence $recurrence): array
|
||||
{
|
||||
Log::debug('Now in getRepetitions().');
|
||||
$fromDate = $recurrence->latest_date ?? $recurrence->first_date;
|
||||
$return = [];
|
||||
|
||||
/** @var RecurrenceRepetition $repetition */
|
||||
foreach ($recurrence->recurrenceRepetitions as $repetition) {
|
||||
$repetitionArray = [
|
||||
'id' => (string)$repetition->id,
|
||||
'created_at' => $repetition->created_at->toAtomString(),
|
||||
'updated_at' => $repetition->updated_at->toAtomString(),
|
||||
'type' => $repetition->repetition_type,
|
||||
'moment' => $repetition->repetition_moment,
|
||||
'skip' => $repetition->repetition_skip,
|
||||
'weekend' => $repetition->weekend,
|
||||
'description' => $this->repository->repetitionDescription($repetition),
|
||||
'occurrences' => [],
|
||||
];
|
||||
|
||||
// get the (future) occurrences for this specific type of repetition:
|
||||
$amount = 'daily' === $repetition->repetition_type ? 9 : 5;
|
||||
$occurrences = $this->repository->getXOccurrencesSince($repetition, $fromDate, now(), $amount);
|
||||
|
||||
/** @var Carbon $carbon */
|
||||
foreach ($occurrences as $carbon) {
|
||||
$repetitionArray['occurrences'][] = $carbon->toAtomString();
|
||||
}
|
||||
|
||||
$return[] = $repetitionArray;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getTransactions(Recurrence $recurrence): array
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$return = [];
|
||||
|
||||
// get all transactions:
|
||||
/** @var RecurrenceTransaction $transaction */
|
||||
foreach ($recurrence->recurrenceTransactions()->get() as $transaction) {
|
||||
/** @var null|Account $sourceAccount */
|
||||
$sourceAccount = $transaction->sourceAccount;
|
||||
|
||||
/** @var null|Account $destinationAccount */
|
||||
$destinationAccount = $transaction->destinationAccount;
|
||||
$foreignCurrencyCode = null;
|
||||
$foreignCurrencySymbol = null;
|
||||
$foreignCurrencyDp = null;
|
||||
$foreignCurrencyId = null;
|
||||
if (null !== $transaction->foreign_currency_id) {
|
||||
$foreignCurrencyId = (int)$transaction->foreign_currency_id;
|
||||
$foreignCurrencyCode = $transaction->foreignCurrency->code;
|
||||
$foreignCurrencySymbol = $transaction->foreignCurrency->symbol;
|
||||
$foreignCurrencyDp = $transaction->foreignCurrency->decimal_places;
|
||||
}
|
||||
|
||||
// source info:
|
||||
$sourceName = '';
|
||||
$sourceId = null;
|
||||
$sourceType = null;
|
||||
$sourceIban = null;
|
||||
if (null !== $sourceAccount) {
|
||||
$sourceName = $sourceAccount->name;
|
||||
$sourceId = $sourceAccount->id;
|
||||
$sourceType = $sourceAccount->accountType->type;
|
||||
$sourceIban = $sourceAccount->iban;
|
||||
}
|
||||
$destinationName = '';
|
||||
$destinationId = null;
|
||||
$destinationType = null;
|
||||
$destinationIban = null;
|
||||
if (null !== $destinationAccount) {
|
||||
$destinationName = $destinationAccount->name;
|
||||
$destinationId = $destinationAccount->id;
|
||||
$destinationType = $destinationAccount->accountType->type;
|
||||
$destinationIban = $destinationAccount->iban;
|
||||
}
|
||||
$amount = Steam::bcround($transaction->amount, $transaction->transactionCurrency->decimal_places);
|
||||
$foreignAmount = null;
|
||||
if (null !== $transaction->foreign_currency_id && null !== $transaction->foreign_amount) {
|
||||
$foreignAmount = Steam::bcround($transaction->foreign_amount, $foreignCurrencyDp);
|
||||
}
|
||||
$transactionArray = [
|
||||
'id' => (string)$transaction->id,
|
||||
'currency_id' => (string)$transaction->transaction_currency_id,
|
||||
'currency_code' => $transaction->transactionCurrency->code,
|
||||
'currency_symbol' => $transaction->transactionCurrency->symbol,
|
||||
'currency_decimal_places' => $transaction->transactionCurrency->decimal_places,
|
||||
'foreign_currency_id' => null === $foreignCurrencyId ? null : (string)$foreignCurrencyId,
|
||||
'foreign_currency_code' => $foreignCurrencyCode,
|
||||
'foreign_currency_symbol' => $foreignCurrencySymbol,
|
||||
'foreign_currency_decimal_places' => $foreignCurrencyDp,
|
||||
'source_id' => (string)$sourceId,
|
||||
'source_name' => $sourceName,
|
||||
'source_iban' => $sourceIban,
|
||||
'source_type' => $sourceType,
|
||||
'destination_id' => (string)$destinationId,
|
||||
'destination_name' => $destinationName,
|
||||
'destination_iban' => $destinationIban,
|
||||
'destination_type' => $destinationType,
|
||||
'amount' => $amount,
|
||||
'foreign_amount' => $foreignAmount,
|
||||
'description' => $transaction->description,
|
||||
];
|
||||
$transactionArray = $this->getTransactionMeta($transaction, $transactionArray);
|
||||
if (null !== $transaction->foreign_currency_id) {
|
||||
$transactionArray['foreign_currency_code'] = $transaction->foreignCurrency->code;
|
||||
$transactionArray['foreign_currency_symbol'] = $transaction->foreignCurrency->symbol;
|
||||
$transactionArray['foreign_currency_decimal_places'] = $transaction->foreignCurrency->decimal_places;
|
||||
}
|
||||
|
||||
// store transaction in recurrence array.
|
||||
$return[] = $transactionArray;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function getTransactionMeta(RecurrenceTransaction $transaction, array $array): array
|
||||
{
|
||||
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||
$array['tags'] = [];
|
||||
$array['category_id'] = null;
|
||||
$array['category_name'] = null;
|
||||
$array['budget_id'] = null;
|
||||
$array['budget_name'] = null;
|
||||
$array['piggy_bank_id'] = null;
|
||||
$array['piggy_bank_name'] = null;
|
||||
$array['bill_id'] = null;
|
||||
$array['bill_name'] = null;
|
||||
|
||||
/** @var RecurrenceTransactionMeta $transactionMeta */
|
||||
foreach ($transaction->recurrenceTransactionMeta as $transactionMeta) {
|
||||
switch ($transactionMeta->name) {
|
||||
default:
|
||||
throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $transactionMeta->name));
|
||||
|
||||
case 'bill_id':
|
||||
$bill = $this->billRepos->find((int)$transactionMeta->value);
|
||||
if (null !== $bill) {
|
||||
$array['bill_id'] = (string)$bill->id;
|
||||
$array['bill_name'] = $bill->name;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'tags':
|
||||
$array['tags'] = json_decode((string)$transactionMeta->value);
|
||||
|
||||
break;
|
||||
|
||||
case 'piggy_bank_id':
|
||||
$piggy = $this->piggyRepos->find((int)$transactionMeta->value);
|
||||
if (null !== $piggy) {
|
||||
$array['piggy_bank_id'] = (string)$piggy->id;
|
||||
$array['piggy_bank_name'] = $piggy->name;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'category_id':
|
||||
$category = $this->factory->findOrCreate((int)$transactionMeta->value, null);
|
||||
if (null !== $category) {
|
||||
$array['category_id'] = (string)$category->id;
|
||||
$array['category_name'] = $category->name;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'category_name':
|
||||
$category = $this->factory->findOrCreate(null, $transactionMeta->value);
|
||||
if (null !== $category) {
|
||||
$array['category_id'] = (string)$category->id;
|
||||
$array['category_name'] = $category->name;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'budget_id':
|
||||
$budget = $this->budgetRepos->find((int)$transactionMeta->value);
|
||||
if (null !== $budget) {
|
||||
$array['budget_id'] = (string)$budget->id;
|
||||
$array['budget_name'] = $budget->name;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user