diff --git a/app/Transformers/V2/AbstractTransformer.php b/app/Transformers/V2/AbstractTransformer.php deleted file mode 100644 index 84c2a3e255..0000000000 --- a/app/Transformers/V2/AbstractTransformer.php +++ /dev/null @@ -1,54 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use Illuminate\Support\Collection; -use League\Fractal\TransformerAbstract; -use Symfony\Component\HttpFoundation\ParameterBag; - -/** - * Class AbstractTransformer - * - * @deprecated - */ -abstract class AbstractTransformer extends TransformerAbstract -{ - protected ParameterBag $parameters; - - /** - * This method is called exactly ONCE from FireflyIII\Api\V2\Controllers\Controller::jsonApiList - */ - abstract public function collectMetaData(Collection $objects): Collection; - - final public function getParameters(): ParameterBag - { - return $this->parameters; - } - - final public function setParameters(ParameterBag $parameters): void - { - $this->parameters = $parameters; - } -} diff --git a/app/Transformers/V2/AccountTransformer.php b/app/Transformers/V2/AccountTransformer.php deleted file mode 100644 index ed7cec916e..0000000000 --- a/app/Transformers/V2/AccountTransformer.php +++ /dev/null @@ -1,440 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Account; -use FireflyIII\Models\AccountType; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Repositories\Account\AccountRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Log; - -/** - * Class AccountTransformer - * - * @deprecated - */ -class AccountTransformer extends AbstractTransformer -{ - private array $accountMeta; - private array $accountTypes; - private array $balanceDifferences; - private array $balances; - private array $convertedBalances; - private array $currencies; - private TransactionCurrency $default; - private array $fullTypes; - private array $lastActivity; - private array $objectGroups; - - /** - * This method collects meta-data for one or all accounts in the transformer's collection. - */ - public function collectMetaData(Collection $objects): Collection - { - $this->currencies = []; - $this->balances = []; - $this->accountMeta = []; - $this->accountTypes = []; - $this->fullTypes = []; - $this->lastActivity = []; - $this->objectGroups = []; - $this->convertedBalances = []; - $this->balanceDifferences = []; - - Log::debug(sprintf('collectMetaData on %d object(s)', $objects->count())); - - // first collect all the "heavy" stuff that relies on ALL data to be present. - // get last activity: - $this->getLastActivity($objects); - - // get balances of all accounts - $this->getMetaBalances($objects); - - // get default currency: - $this->getDefaultCurrency(); - - // collect currency and other meta-data: - $this->collectAccountMetaData($objects); - - // get account types: - $this->collectAccountTypes($objects); - - // add balance difference - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $this->getBalanceDifference($objects, $this->parameters->get('start'), $this->parameters->get('end')); - } - - // get object groups - $this->getObjectGroups($objects); - - // sort: - $objects = $this->sortAccounts($objects); - - // if pagination is disabled, do it now: - if (true === $this->parameters->get('disablePagination')) { - $page = (int) $this->parameters->get('page'); - $size = (int) $this->parameters->get('pageSize'); - $objects = $objects->slice(($page - 1) * $size, $size); - } - - return $objects; - } - - private function getLastActivity(Collection $accounts): void - { - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - $lastActivity = $accountRepository->getLastActivity($accounts); - foreach ($lastActivity as $row) { - $this->lastActivity[(int) $row['account_id']] = Carbon::parse($row['date_max'], config('app.timezone')); - } - } - - private function getMetaBalances(Collection $accounts): void - { - try { - $this->convertedBalances = app('steam')->finalAccountsBalance($accounts, $this->getDate()); - } catch (FireflyException $e) { - Log::error($e->getMessage()); - } - } - - private function getDate(): Carbon - { - if (null !== $this->parameters->get('date')) { - return $this->parameters->get('date'); - } - - return today(config('app.timezone')); - } - - private function getDefaultCurrency(): void - { - $this->default = app('amount')->getPrimaryCurrency(); - } - - private function collectAccountMetaData(Collection $accounts): void - { - /** @var CurrencyRepositoryInterface $repository */ - $repository = app(CurrencyRepositoryInterface::class); - - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - $metaFields = $accountRepository->getMetaValues($accounts, ['currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']); - $currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray(); - - $currencies = $repository->getByIds($currencyIds); - foreach ($currencies as $currency) { - $id = $currency->id; - $this->currencies[$id] = $currency; - } - foreach ($metaFields as $entry) { - $id = $entry->account_id; - $this->accountMeta[$id][$entry->name] = $entry->data; - } - } - - private function collectAccountTypes(Collection $accounts): void - { - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - $accountTypes = $accountRepository->getAccountTypes($accounts); - - /** @var AccountType $row */ - foreach ($accountTypes as $row) { - $this->accountTypes[$row->id] = (string) config(sprintf('firefly.shortNamesByFullName.%s', $row->type)); - $this->fullTypes[$row->id] = $row->type; - } - } - - private function getBalanceDifference(Collection $accounts, Carbon $start, Carbon $end): void - { - if ('en_US' === config('app.fallback_locale')) { - throw new FireflyException('Used deprecated method, rethink this.'); - } - // collect balances, start and end for both native and converted. - // yes the b is usually used for boolean by idiots but here it's for balance. - $bStart = []; - $bEnd = []; - - try { - Log::debug(sprintf('v2 transformer: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s'))); - Log::debug(sprintf('v2 transformer: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s'))); - $bStart = app('steam')->finalAccountsBalance($accounts, $start); - $bEnd = app('steam')->finalAccountsBalance($accounts, $end); - } catch (FireflyException $e) { - Log::error($e->getMessage()); - } - - /** @var Account $account */ - foreach ($accounts as $account) { - $id = $account->id; - if (array_key_exists($id, $bStart) && array_key_exists($id, $bEnd)) { - $this->balanceDifferences[$id] = [ - 'balance' => bcsub((string) $bEnd[$id]['balance'], (string) $bStart[$id]['balance']), - 'native_balance' => bcsub((string) $bEnd[$id]['native_balance'], (string) $bStart[$id]['native_balance']), - ]; - } - } - } - - private function getObjectGroups(Collection $accounts): void - { - /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); - $this->objectGroups = $accountRepository->getObjectGroups($accounts); - } - - private function sortAccounts(Collection $accounts): Collection - { - /** @var null|array $sort */ - $sort = $this->parameters->get('sort'); - - if (null === $sort || 0 === count($sort)) { - return $accounts; - } - - /** - * @var string $column - * @var string $direction - */ - foreach ($sort as $column => $direction) { - // account_number + iban - if ('iban' === $column) { - $accounts = $this->sortByIban($accounts, $direction); - } - if ('balance' === $column) { - $accounts = $this->sortByBalance($accounts, $direction); - } - if ('last_activity' === $column) { - $accounts = $this->sortByLastActivity($accounts, $direction); - } - if ('balance_difference' === $column) { - $accounts = $this->sortByBalanceDifference($accounts, $direction); - } - if ('current_debt' === $column) { - $accounts = $this->sortByCurrentDebt($accounts, $direction); - } - } - - return $accounts; - } - - private function sortByIban(Collection $accounts, string $direction): Collection - { - $meta = $this->accountMeta; - - return $accounts->sort(function (Account $left, Account $right) use ($meta, $direction) { - $leftIban = trim(sprintf('%s%s', $left->iban, $meta[$left->id]['account_number'] ?? '')); - $rightIban = trim(sprintf('%s%s', $right->iban, $meta[$right->id]['account_number'] ?? '')); - if ('asc' === $direction) { - return strcasecmp($leftIban, $rightIban); - } - - return strcasecmp($rightIban, $leftIban); - }); - } - - private function sortByBalance(Collection $accounts, string $direction): Collection - { - $balances = $this->convertedBalances; - - return $accounts->sort(function (Account $left, Account $right) use ($balances, $direction) { - $leftBalance = (float) ($balances[$left->id]['native_balance'] ?? 0); - $rightBalance = (float) ($balances[$right->id]['native_balance'] ?? 0); - if ('asc' === $direction) { - return $leftBalance <=> $rightBalance; - } - - return $rightBalance <=> $leftBalance; - }); - } - - private function sortByLastActivity(Collection $accounts, string $direction): Collection - { - $dates = $this->lastActivity; - - return $accounts->sort(function (Account $left, Account $right) use ($dates, $direction) { - $leftDate = $dates[$left->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0); - $rightDate = $dates[$right->id] ?? Carbon::create(1900, 1, 1, 0, 0, 0); - if ('asc' === $direction) { - return $leftDate->gt($rightDate) ? 1 : -1; - } - - return $rightDate->gt($leftDate) ? 1 : -1; - }); - } - - private function sortByBalanceDifference(Collection $accounts, string $direction): Collection - { - $balances = $this->balanceDifferences; - - return $accounts->sort(function (Account $left, Account $right) use ($balances, $direction) { - $leftBalance = (float) ($balances[$left->id]['native_balance'] ?? 0); - $rightBalance = (float) ($balances[$right->id]['native_balance'] ?? 0); - if ('asc' === $direction) { - return $leftBalance <=> $rightBalance; - } - - return $rightBalance <=> $leftBalance; - }); - } - - private function sortByCurrentDebt(Collection $accounts, string $direction): Collection - { - $amounts = $this->accountMeta; - - return $accounts->sort(function (Account $left, Account $right) use ($amounts, $direction) { - $leftCurrent = (float) ($amounts[$left->id]['current_debt'] ?? 0); - $rightCurrent = (float) ($amounts[$right->id]['current_debt'] ?? 0); - if ('asc' === $direction) { - return $leftCurrent <=> $rightCurrent; - } - - return $rightCurrent <=> $leftCurrent; - }); - } - - /** - * Transform the account. - */ - public function transform(Account $account): array - { - $id = $account->id; - - // various meta - $accountRole = $this->accountMeta[$id]['account_role'] ?? null; - $accountType = $this->accountTypes[$id]; - $order = $account->order; - - // liability type - $liabilityType = 'liabilities' === $accountType ? $this->fullTypes[$id] : null; - $liabilityDirection = $this->accountMeta[$id]['liability_direction'] ?? null; - $interest = $this->accountMeta[$id]['interest'] ?? null; - $interestPeriod = $this->accountMeta[$id]['interest_period'] ?? null; - $currentDebt = $this->accountMeta[$id]['current_debt'] ?? null; - - // no currency? use default - $currency = $this->default; - if (array_key_exists($id, $this->accountMeta) && 0 !== (int) ($this->accountMeta[$id]['currency_id'] ?? 0)) { - $currency = $this->currencies[(int) $this->accountMeta[$id]['currency_id']]; - } - // amounts and calculation. - $balance = $this->balances[$id]['balance'] ?? null; - $nativeBalance = $this->convertedBalances[$id]['native_balance'] ?? null; - - // no order for some accounts: - if (!in_array(strtolower((string) $accountType), ['liability', 'liabilities', 'asset'], true)) { - $order = null; - } - - // object group - $objectGroupId = $this->objectGroups[$id]['id'] ?? null; - $objectGroupOrder = $this->objectGroups[$id]['order'] ?? null; - $objectGroupTitle = $this->objectGroups[$id]['title'] ?? null; - - // balance difference - $diffStart = null; - $diffEnd = null; - $balanceDiff = null; - $nativeBalanceDiff = null; - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $diffStart = $this->parameters->get('start')->toAtomString(); - $diffEnd = $this->parameters->get('end')->toAtomString(); - $balanceDiff = $this->balanceDifferences[$id]['balance'] ?? null; - $nativeBalanceDiff = $this->balanceDifferences[$id]['native_balance'] ?? null; - } - - return [ - 'id' => (string) $account->id, - 'created_at' => $account->created_at->toAtomString(), - 'updated_at' => $account->updated_at->toAtomString(), - 'active' => $account->active, - 'order' => $order, - 'name' => $account->name, - 'iban' => '' === (string) $account->iban ? null : $account->iban, - 'account_number' => $this->accountMeta[$id]['account_number'] ?? null, - 'type' => strtolower((string) $accountType), - 'account_role' => $accountRole, - 'currency_id' => (string) $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - - 'native_currency_id' => (string) $this->default->id, - 'native_currency_code' => $this->default->code, - 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => $this->default->decimal_places, - - // balance: - 'current_balance' => $balance, - 'native_current_balance' => $nativeBalance, - 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(), - - // balance difference - 'balance_difference' => $balanceDiff, - 'native_balance_difference' => $nativeBalanceDiff, - 'balance_difference_start' => $diffStart, - 'balance_difference_end' => $diffEnd, - - // more meta - 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null, - - // liability stuff - 'liability_type' => $liabilityType, - 'liability_direction' => $liabilityDirection, - 'interest' => $interest, - 'interest_period' => $interestPeriod, - 'current_debt' => $currentDebt, - - // object group - 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null, - 'object_group_order' => $objectGroupOrder, - 'object_group_title' => $objectGroupTitle, - - // 'notes' => $this->repository->getNoteText($account), - // 'monthly_payment_date' => $monthlyPaymentDate, - // 'credit_card_type' => $creditCardType, - // 'bic' => $this->repository->getMetaValue($account, 'BIC'), - // 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''), - // 'opening_balance' => $openingBalance, - // 'opening_balance_date' => $openingBalanceDate, - // 'include_net_worth' => $includeNetWorth, - // 'longitude' => $longitude, - // 'latitude' => $latitude, - // 'zoom_level' => $zoomLevel, - 'links' => [ - [ - 'rel' => 'self', - 'uri' => '/accounts/'.$account->id, - ], - ], - ]; - } -} diff --git a/app/Transformers/V2/BillTransformer.php b/app/Transformers/V2/BillTransformer.php deleted file mode 100644 index 36bfaffaf8..0000000000 --- a/app/Transformers/V2/BillTransformer.php +++ /dev/null @@ -1,367 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use Carbon\Carbon; -use Carbon\CarbonInterface; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Bill; -use FireflyIII\Models\Note; -use FireflyIII\Models\ObjectGroup; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; - -/** - * Class BillTransformer - * - * @deprecated - */ -class BillTransformer extends AbstractTransformer -{ - private ExchangeRateConverter $converter; - private array $currencies; - private TransactionCurrency $default; - private array $groups; - private array $notes; - private array $paidDates; - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.ExcessiveMethodLength") - */ - public function collectMetaData(Collection $objects): Collection - { - /** @var array $currencies */ - $currencies = []; - $bills = []; - $this->notes = []; - $this->groups = []; - $this->paidDates = []; - - /** @var Bill $object */ - foreach ($objects as $object) { - $id = $object->transaction_currency_id; - $bills[] = $object->id; - $currencies[$id] ??= TransactionCurrency::find($id); - } - $this->currencies = $currencies; - $notes = Note::whereNoteableType(Bill::class)->whereIn('noteable_id', array_keys($bills))->get(); - - /** @var Note $note */ - foreach ($notes as $note) { - $id = $note->noteable_id; - $this->notes[$id] = $note; - } - // grab object groups: - $set = DB::table('object_groupables') - ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') - ->where('object_groupables.object_groupable_type', Bill::class) - ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']) - ; - - /** @var ObjectGroup $entry */ - foreach ($set as $entry) { - $billId = (int) $entry->object_groupable_id; - $id = (int) $entry->object_group_id; - $order = $entry->order; - $this->groups[$billId] = [ - 'object_group_id' => $id, - 'object_group_title' => $entry->title, - 'object_group_order' => $order, - ]; - } - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $this->default = app('amount')->getPrimaryCurrency(); - $this->converter = new ExchangeRateConverter(); - - // grab all paid dates: - if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { - $journals = TransactionJournal::whereIn('bill_id', $bills) - ->where('date', '>=', $this->parameters->get('start')) - ->where('date', '<=', $this->parameters->get('end')) - ->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']) - ; - $journalIds = $journals->pluck('id')->toArray(); - - // grab transactions for amount: - $set = Transaction::whereIn('transaction_journal_id', $journalIds) - ->where('transactions.amount', '<', 0) - ->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']) - ; - $transactions = []; - - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $journalId = $transaction->transaction_journal_id; - $transactions[$journalId] = $transaction->toArray(); - } - - /** @var TransactionJournal $journal */ - foreach ($journals as $journal) { - app('log')->debug(sprintf('Processing journal #%d', $journal->id)); - $transaction = $transactions[$journal->id] ?? []; - $billId = (int) $journal->bill_id; - $currencyId = (int) ($transaction['transaction_currency_id'] ?? 0); - $currencies[$currencyId] ??= TransactionCurrency::find($currencyId); - - // foreign currency - $foreignCurrencyId = null; - $foreignCurrencyCode = null; - $foreignCurrencyName = null; - $foreignCurrencySymbol = null; - $foreignCurrencyDp = null; - app('log')->debug('Foreign currency is NULL'); - if (null !== $transaction['foreign_currency_id']) { - app('log')->debug(sprintf('Foreign currency is #%d', $transaction['foreign_currency_id'])); - $foreignCurrencyId = (int) $transaction['foreign_currency_id']; - $currencies[$foreignCurrencyId] ??= TransactionCurrency::find($foreignCurrencyId); - $foreignCurrencyCode = $currencies[$foreignCurrencyId]->code; // @phpstan-ignore property.notFound - $foreignCurrencyName = $currencies[$foreignCurrencyId]->name; // @phpstan-ignore property.notFound - $foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol; // @phpstan-ignore property.notFound - $foreignCurrencyDp = $currencies[$foreignCurrencyId]->decimal_places; // @phpstan-ignore property.notFound - } - - $this->paidDates[$billId][] = [ - 'transaction_group_id' => (string) $journal->id, - 'transaction_journal_id' => (string) $journal->transaction_group_id, - 'date' => $journal->date->toAtomString(), - 'currency_id' => $currencies[$currencyId]->id, // @phpstan-ignore property.notFound - 'currency_code' => $currencies[$currencyId]->code, // @phpstan-ignore property.notFound - 'currency_name' => $currencies[$currencyId]->name, // @phpstan-ignore property.notFound - 'currency_symbol' => $currencies[$currencyId]->symbol, // @phpstan-ignore property.notFound - 'currency_decimal_places' => $currencies[$currencyId]->decimal_places, // @phpstan-ignore property.notFound - 'native_currency_id' => $currencies[$currencyId]->id, // @phpstan-ignore property.notFound - 'native_currency_code' => $currencies[$currencyId]->code, // @phpstan-ignore property.notFound - 'native_currency_symbol' => $currencies[$currencyId]->symbol, // @phpstan-ignore property.notFound - 'native_currency_decimal_places' => $currencies[$currencyId]->decimal_places, // @phpstan-ignore property.notFound - 'foreign_currency_id' => $foreignCurrencyId, - 'foreign_currency_code' => $foreignCurrencyCode, - 'foreign_currency_name' => $foreignCurrencyName, - 'foreign_currency_symbol' => $foreignCurrencySymbol, - 'foreign_currency_decimal_places' => $foreignCurrencyDp, - 'amount' => $transaction['amount'], - 'foreign_amount' => $transaction['foreign_amount'], - 'native_amount' => null, - 'foreign_native_amount' => null, - ]; - } - } - - return $objects; - } - - /** - * Transform the bill. - */ - public function transform(Bill $bill): array - { - $paidData = $this->paidDates[$bill->id] ?? []; - $nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[$bill->id] ?? []); - $payDates = $this->payDates($bill); - $currency = $this->currencies[$bill->transaction_currency_id]; - $group = $this->groups[$bill->id] ?? null; - - // date for currency conversion - /** @var null|Carbon $startParam */ - $startParam = $this->parameters->get('start'); - - /** @var null|Carbon $date */ - $date = null === $startParam ? today() : clone $startParam; - - $nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates); - $this->converter->summarize(); - - return [ - 'id' => $bill->id, - 'created_at' => $bill->created_at->toAtomString(), - 'updated_at' => $bill->updated_at->toAtomString(), - 'name' => $bill->name, - 'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places), - 'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places), - 'native_amount_min' => $this->converter->convert($currency, $this->default, $date, $bill->amount_min), - 'native_amount_max' => $this->converter->convert($currency, $this->default, $date, $bill->amount_max), - 'currency_id' => (string) $bill->transaction_currency_id, - 'currency_code' => $currency->code, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'native_currency_id' => $this->default->id, - 'native_currency_code' => $this->default->code, - 'native_currency_name' => $this->default->name, - 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => $this->default->decimal_places, - 'date' => $bill->date->toAtomString(), - 'end_date' => $bill->end_date?->toAtomString(), - 'extension_date' => $bill->extension_date?->toAtomString(), - 'repeat_freq' => $bill->repeat_freq, - 'skip' => $bill->skip, - 'active' => $bill->active, - 'order' => $bill->order, - 'notes' => $this->notes[$bill->id] ?? null, - 'object_group_id' => $group ? $group['object_group_id'] : null, - 'object_group_order' => $group ? $group['object_group_order'] : null, - 'object_group_title' => $group ? $group['object_group_title'] : null, - 'next_expected_match' => $nextExpectedMatch->toAtomString(), - 'next_expected_match_diff' => $nextExpectedMatchDiff, - 'pay_dates' => $payDates, - 'paid_dates' => $paidData, - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/bills/%d', $bill->id), - ], - ], - ]; - } - - /** - * Get the data the bill was paid and predict the next expected match. - */ - protected function nextExpectedMatch(Bill $bill, array $dates): Carbon - { - // 2023-07-1 sub one day from the start date to fix a possible bug (see #7704) - // 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 null|Carbon $startParam */ - $startParam = $this->parameters->get('start'); - - /** @var null|Carbon $start */ - $start = null === $startParam ? today() : clone $startParam; - $start->subDay(); - - $lastPaidDate = $this->lastPaidDate($dates, $start); - $nextMatch = clone $bill->date; - while ($nextMatch < $lastPaidDate) { - /* - * As long as this date is smaller than the last time the bill was paid, keep jumping ahead. - * For example: 1 jan, 1 feb, etc. - */ - $nextMatch = app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); - } - if ($nextMatch->isSameDay($lastPaidDate)) { - // Add another period because it's the same day as the last paid date. - return app('navigation')->addPeriod($nextMatch, $bill->repeat_freq, $bill->skip); - } - - return $nextMatch; - } - - /** - * Returns the latest date in the set, or start when set is empty. - */ - protected function lastPaidDate(array $dates, Carbon $default): Carbon - { - if (0 === count($dates)) { - return $default; - } - $latest = $dates[0]['date']; - - /** @var array $row */ - foreach ($dates as $row) { - $carbon = new Carbon($row['date']); - if ($carbon->gte($latest)) { - $latest = $row['date']; - } - } - - return new Carbon($latest); - } - - protected function payDates(Bill $bill): array - { - // app('log')->debug(sprintf('Now in payDates() for bill #%d', $bill->id)); - if (null === $this->parameters->get('start') || null === $this->parameters->get('end')) { - // app('log')->debug('No start or end date, give empty array.'); - - return []; - } - $set = new Collection(); - $currentStart = clone $this->parameters->get('start'); - // 2023-06-23 subDay to fix 7655 - $currentStart->subDay(); - $loop = 0; - while ($currentStart <= $this->parameters->get('end')) { - $nextExpectedMatch = $this->nextDateMatch($bill, $currentStart); - // If nextExpectedMatch is after end, we continue: - if ($nextExpectedMatch > $this->parameters->get('end')) { - break; - } - // add to set - $set->push(clone $nextExpectedMatch); - $nextExpectedMatch->addDay(); - $currentStart = clone $nextExpectedMatch; - ++$loop; - if ($loop > 4) { - break; - } - } - $simple = $set->map( // @phpstan-ignore-line - static fn (Carbon $date) => $date->toAtomString() - ); - - return $simple->toArray(); - } - - /** - * Given a bill and a date, this method will tell you at which moment this bill expects its next - * transaction. Whether or not it is there already, is not relevant. - * - * TODO this method is bad compared to the v1 one. - */ - protected function nextDateMatch(Bill $bill, Carbon $date): Carbon - { - // app('log')->debug(sprintf('Now in nextDateMatch(%d, %s)', $bill->id, $date->format('Y-m-d'))); - $start = clone $bill->date; - // app('log')->debug(sprintf('Bill start date is %s', $start->format('Y-m-d'))); - while ($start < $date) { - $start = app('navigation')->addPeriod($start, $bill->repeat_freq, $bill->skip); - } - - // app('log')->debug(sprintf('End of loop, bill start date is now %s', $start->format('Y-m-d'))); - - return $start; - } - - private function getNextExpectedMatchDiff(Carbon $next, array $dates): string - { - if ($next->isToday()) { - return trans('firefly.today'); - } - $current = $dates[0] ?? null; - if (null === $current) { - return trans('firefly.not_expected_period'); - } - $carbon = new Carbon($current); - - return $carbon->diffForHumans(today(config('app.timezone')), CarbonInterface::DIFF_RELATIVE_TO_NOW); - } -} diff --git a/app/Transformers/V2/BudgetLimitTransformer.php b/app/Transformers/V2/BudgetLimitTransformer.php deleted file mode 100644 index a1a82d166a..0000000000 --- a/app/Transformers/V2/BudgetLimitTransformer.php +++ /dev/null @@ -1,109 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Models\BudgetLimit; -use Illuminate\Support\Collection; -use League\Fractal\Resource\Item; - -/** - * Class BudgetLimitTransformer - * - * @deprecated - */ -class BudgetLimitTransformer extends AbstractTransformer -{ - protected array $availableIncludes - = [ - 'budget', - ]; - - public function collectMetaData(Collection $objects): Collection - { - // TODO: Implement collectMetaData() method. - return $objects; - } - - /** - * Include Budget - * - * @return Item - */ - public function includeBudget(BudgetLimit $limit) - { - return $this->item($limit->budget, new BudgetTransformer(), 'budgets'); - } - - /** - * Transform the note. - */ - public function transform(BudgetLimit $budgetLimit): array - { - // $repository = app(OperationsRepository::class); - // $repository->setUser($budgetLimit->budget->user); - // $expenses = $repository->sumExpenses( - // $budgetLimit->start_date, $budgetLimit->end_date, null, new Collection([$budgetLimit->budget]), $budgetLimit->transactionCurrency - // ); - $currency = $budgetLimit->transactionCurrency; - $amount = $budgetLimit->amount; - $currencyDecimalPlaces = 2; - $currencyId = null; - $currencyName = null; - $currencyCode = null; - $currencySymbol = null; - if (null !== $currency) { - $amount = $budgetLimit->amount; - $currencyId = $currency->id; - $currencyName = $currency->name; - $currencyCode = $currency->code; - $currencySymbol = $currency->symbol; - $currencyDecimalPlaces = $currency->decimal_places; - } - $amount = number_format((float) $amount, $currencyDecimalPlaces, '.', ''); - - return [ - 'id' => (string) $budgetLimit->id, - 'created_at' => $budgetLimit->created_at->toAtomString(), - 'updated_at' => $budgetLimit->updated_at->toAtomString(), - 'start' => $budgetLimit->start_date->toAtomString(), - 'end' => $budgetLimit->end_date->endOfDay()->toAtomString(), - 'budget_id' => (string) $budgetLimit->budget_id, - 'currency_id' => (string) $currencyId, - 'currency_code' => $currencyCode, - 'currency_name' => $currencyName, - 'currency_decimal_places' => $currencyDecimalPlaces, - 'currency_symbol' => $currencySymbol, - 'amount' => $amount, - 'period' => $budgetLimit->period, - // 'spent' => $expenses[$currencyId]['sum'] ?? '0', - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/budget-limits/%d', $budgetLimit->id), - ], - ], - ]; - } -} diff --git a/app/Transformers/V2/BudgetTransformer.php b/app/Transformers/V2/BudgetTransformer.php deleted file mode 100644 index b1e048130c..0000000000 --- a/app/Transformers/V2/BudgetTransformer.php +++ /dev/null @@ -1,113 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Models\Budget; -use Illuminate\Support\Collection; -use Symfony\Component\HttpFoundation\ParameterBag; - -/** - * Class BudgetTransformer - * - * @deprecated - */ -class BudgetTransformer extends AbstractTransformer -{ - // private OperationsRepositoryInterface $opsRepository; - // private BudgetRepositoryInterface $repository; - - /** - * BudgetTransformer constructor. - */ - public function __construct() - { - // $this->opsRepository = app(OperationsRepositoryInterface::class); - // $this->repository = app(BudgetRepositoryInterface::class); - $this->parameters = new ParameterBag(); - } - - public function collectMetaData(Collection $objects): Collection - { - // TODO: Implement collectMetaData() method. - return $objects; - } - - /** - * Transform a budget. - */ - public function transform(Budget $budget): array - { - // $this->opsRepository->setUser($budget->user); - // $start = $this->parameters->get('start'); - // $end = $this->parameters->get('end'); - // $autoBudget = $this->repository->getAutoBudget($budget); - // $spent = []; - // if (null !== $start && null !== $end) { - // $spent = $this->beautify($this->opsRepository->sumExpenses($start, $end, null, new Collection([$budget]))); - // } - - // $abCurrencyId = null; - // $abCurrencyCode = null; - // $abType = null; - // $abAmount = null; - // $abPeriod = null; - // $notes = $this->repository->getNoteText($budget); - // - // $types = [ - // AutoBudget::AUTO_BUDGET_RESET => 'reset', - // AutoBudget::AUTO_BUDGET_ROLLOVER => 'rollover', - // ]; - // - // if (null !== $autoBudget) { - // $abCurrencyId = (string) $autoBudget->transactionCurrency->id; - // $abCurrencyCode = $autoBudget->transactionCurrency->code; - // $abType = $types[$autoBudget->auto_budget_type]; - // $abAmount = number_format((float) $autoBudget->amount, $autoBudget->transactionCurrency->decimal_places, '.', ''); - // $abPeriod = $autoBudget->period; - // } - - return [ - 'id' => (string) $budget->id, - 'created_at' => $budget->created_at->toAtomString(), - 'updated_at' => $budget->updated_at->toAtomString(), - 'name' => $budget->name, - 'active' => $budget->active, - 'order' => $budget->order, - // 'notes' => $notes, - // 'auto_budget_type' => $abType, - // 'auto_budget_period' => $abPeriod, - // 'auto_budget_currency_id' => $abCurrencyId, - // 'auto_budget_currency_code' => $abCurrencyCode, - // 'auto_budget_amount' => $abAmount, - // 'spent' => $spent, - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/budgets/%d', $budget->id), - ], - ], - ]; - } -} diff --git a/app/Transformers/V2/CurrencyTransformer.php b/app/Transformers/V2/CurrencyTransformer.php deleted file mode 100644 index 53352d0843..0000000000 --- a/app/Transformers/V2/CurrencyTransformer.php +++ /dev/null @@ -1,66 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Models\TransactionCurrency; -use Illuminate\Support\Collection; - -/** - * Class CurrencyTransformer - * - * @deprecated - */ -class CurrencyTransformer extends AbstractTransformer -{ - public function collectMetaData(Collection $objects): Collection - { - return $objects; - } - - /** - * Transform the currency. - */ - public function transform(TransactionCurrency $currency): array - { - return [ - 'id' => $currency->id, - 'created_at' => $currency->created_at->toAtomString(), - 'updated_at' => $currency->updated_at->toAtomString(), - 'native' => $currency->userGroupNative, - 'default' => $currency->userGroupNative, - 'enabled' => $currency->userGroupEnabled, - 'name' => $currency->name, - 'code' => $currency->code, - 'symbol' => $currency->symbol, - 'decimal_places' => $currency->decimal_places, - 'links' => [ - [ - 'rel' => 'self', - 'uri' => '/currencies/'.$currency->id, - ], - ], - ]; - } -} diff --git a/app/Transformers/V2/ExchangeRateTransformer.php b/app/Transformers/V2/ExchangeRateTransformer.php deleted file mode 100644 index 7abed13832..0000000000 --- a/app/Transformers/V2/ExchangeRateTransformer.php +++ /dev/null @@ -1,75 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Models\CurrencyExchangeRate; -use Illuminate\Support\Collection; - -/** - * Class AccountTransformer - * - * @deprecated - */ -class ExchangeRateTransformer extends AbstractTransformer -{ - /** - * This method collects meta-data for one or all accounts in the transformer's collection. - */ - public function collectMetaData(Collection $objects): Collection - { - return $objects; - } - - /** - * Transform the account. - */ - public function transform(CurrencyExchangeRate $rate): array - { - return [ - 'id' => (string) $rate->id, - 'created_at' => $rate->created_at->toAtomString(), - 'updated_at' => $rate->updated_at->toAtomString(), - - 'from_currency_id' => (string) $rate->fromCurrency->id, - 'from_currency_code' => $rate->fromCurrency->code, - 'from_currency_symbol' => $rate->fromCurrency->symbol, - 'from_currency_decimal_places' => $rate->fromCurrency->decimal_places, - - 'to_currency_id' => (string) $rate->toCurrency->id, - 'to_currency_code' => $rate->toCurrency->code, - 'to_currency_symbol' => $rate->toCurrency->symbol, - 'to_currency_decimal_places' => $rate->toCurrency->decimal_places, - - 'rate' => $rate->rate, - 'date' => $rate->date->toAtomString(), - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/exchange-rates/%s', $rate->id), - ], - ], - ]; - } -} diff --git a/app/Transformers/V2/PiggyBankTransformer.php b/app/Transformers/V2/PiggyBankTransformer.php deleted file mode 100644 index 11f51e1781..0000000000 --- a/app/Transformers/V2/PiggyBankTransformer.php +++ /dev/null @@ -1,273 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Account; -use FireflyIII\Models\AccountMeta; -use FireflyIII\Models\Note; -use FireflyIII\Models\ObjectGroup; -use FireflyIII\Models\PiggyBank; -use FireflyIII\Models\PiggyBankRepetition; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; - -/** - * Class PiggyBankTransformer - * - * @deprecated - */ -class PiggyBankTransformer extends AbstractTransformer -{ - // private AccountRepositoryInterface $accountRepos; - // private CurrencyRepositoryInterface $currencyRepos; - // private PiggyBankRepositoryInterface $piggyRepos; - private array $accounts; - private ExchangeRateConverter $converter; - private array $currencies; - private TransactionCurrency $default; - private array $groups; - private array $notes; - private array $repetitions; - - /** - * PiggyBankTransformer constructor. - */ - public function __construct() - { - $this->notes = []; - $this->accounts = []; - $this->groups = []; - $this->currencies = []; - $this->repetitions = []; - // $this-> - // $this->currencyRepos = app(CurrencyRepositoryInterface::class); - // $this->piggyRepos = app(PiggyBankRepositoryInterface::class); - } - - public function collectMetaData(Collection $objects): Collection - { - // TODO move to repository (does not exist yet) - $piggyBanks = $objects->pluck('id')->toArray(); - $accountInfo = Account::whereIn('id', $objects->pluck('account_id')->toArray())->get(); - $currencyPreferences = AccountMeta::where('name', '"currency_id"')->whereIn('account_id', $objects->pluck('account_id')->toArray())->get(); - $currencies = []; - - /** @var Account $account */ - foreach ($accountInfo as $account) { - $id = $account->id; - $this->accounts[$id] = [ - 'name' => $account->name, - ]; - } - - /** @var AccountMeta $preference */ - foreach ($currencyPreferences as $preference) { - $currencyId = (int) $preference->data; - $accountId = $preference->account_id; - $currencies[$currencyId] ??= TransactionJournal::find($currencyId); - $this->currencies[$accountId] = $currencies[$currencyId]; - } - - // grab object groups: - $set = DB::table('object_groupables') - ->leftJoin('object_groups', 'object_groups.id', '=', 'object_groupables.object_group_id') - ->where('object_groupables.object_groupable_type', PiggyBank::class) - ->get(['object_groupables.*', 'object_groups.title', 'object_groups.order']) - ; - - /** @var ObjectGroup $entry */ - foreach ($set as $entry) { - $piggyBankId = (int) $entry->object_groupable_id; - $id = (int) $entry->object_group_id; - $order = $entry->order; - $this->groups[$piggyBankId] = [ - 'object_group_id' => (string) $id, - 'object_group_title' => $entry->title, - 'object_group_order' => $order, - ]; - } - - // grab repetitions (for current amount): - $repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get(); - if ('en_US' === config('app.fallback_locale')) { - throw new FireflyException('[d] Piggy bank repetitions are EOL.'); - } - - /** @var PiggyBankRepetition $repetition */ - foreach ($repetitions as $repetition) { - $this->repetitions[$repetition->piggy_bank_id] = [ - 'amount' => $repetition->current_amount, - ]; - } - - // grab notes - // continue with notes - $notes = Note::whereNoteableType(PiggyBank::class)->whereIn('noteable_id', array_keys($piggyBanks))->get(); - - /** @var Note $note */ - foreach ($notes as $note) { - $id = $note->noteable_id; - $this->notes[$id] = $note; - } - - Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); - $this->default = app('amount')->getPrimaryCurrencyByUserGroup(auth()->user()->userGroup); - $this->converter = new ExchangeRateConverter(); - - return $objects; - } - - /** - * Transform the piggy bank. - * - * @throws FireflyException - */ - public function transform(PiggyBank $piggyBank): array - { - // $account = $piggyBank->account; - // $this->accountRepos->setUser($account->user); - // $this->currencyRepos->setUser($account->user); - // $this->piggyRepos->setUser($account->user); - - // get currency from account, or use default. - // $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getNativeCurrencyByUser($account->user); - - // note - // $notes = $this->piggyRepos->getNoteText($piggyBank); - // $notes = '' === $notes ? null : $notes; - - // $objectGroupId = null; - // $objectGroupOrder = null; - // $objectGroupTitle = null; - // /** @var ObjectGroup $objectGroup */ - // $objectGroup = $piggyBank->objectGroups->first(); - // if (null !== $objectGroup) { - // $objectGroupId = (int)$objectGroup->id; - // $objectGroupOrder = (int)$objectGroup->order; - // $objectGroupTitle = $objectGroup->title; - // } - - // get currently saved amount: - // $currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places); - - $percentage = null; - $leftToSave = null; - $nativeLeftToSave = null; - $savePerMonth = null; - $nativeSavePerMonth = null; - $startDate = $piggyBank->start_date?->format('Y-m-d'); - $targetDate = $piggyBank->target_date?->format('Y-m-d'); - $accountId = $piggyBank->account_id; - $accountName = $this->accounts[$accountId]['name'] ?? null; - $currency = $this->currencies[$accountId] ?? $this->default; - $currentAmount = app('steam')->bcround($this->repetitions[$piggyBank->id]['amount'] ?? '0', $currency->decimal_places); - $nativeCurrentAmount = $this->converter->convert($this->default, $currency, today(), $currentAmount); - $targetAmount = $piggyBank->target_amount; - $nativeTargetAmount = $this->converter->convert($this->default, $currency, today(), $targetAmount); - $note = $this->notes[$piggyBank->id] ?? null; - $group = $this->groups[$piggyBank->id] ?? null; - - if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00 - $leftToSave = bcsub($targetAmount, (string) $currentAmount); - $nativeLeftToSave = $this->converter->convert($this->default, $currency, today(), $leftToSave); - $percentage = (int) bcmul(bcdiv((string) $currentAmount, $targetAmount), '100'); - $savePerMonth = $this->getSuggestedMonthlyAmount($currentAmount, $targetAmount, $piggyBank->start_date, $piggyBank->target_date); - $nativeSavePerMonth = $this->converter->convert($this->default, $currency, today(), $savePerMonth); - } - $this->converter->summarize(); - - return [ - 'id' => (string) $piggyBank->id, - 'created_at' => $piggyBank->created_at->toAtomString(), - 'updated_at' => $piggyBank->updated_at->toAtomString(), - 'account_id' => (string) $piggyBank->account_id, - 'account_name' => $accountName, - 'name' => $piggyBank->name, - 'currency_id' => (string) $currency->id, - 'currency_code' => $currency->code, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - 'native_currency_id' => (string) $this->default->id, - 'native_currency_code' => $this->default->code, - 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => $this->default->decimal_places, - 'current_amount' => $currentAmount, - 'native_current_amount' => $nativeCurrentAmount, - 'target_amount' => $targetAmount, - 'native_target_amount' => $nativeTargetAmount, - 'percentage' => $percentage, - 'left_to_save' => $leftToSave, - 'native_left_to_save' => $nativeLeftToSave, - 'save_per_month' => $savePerMonth, - 'native_save_per_month' => $nativeSavePerMonth, - 'start_date' => $startDate, - 'target_date' => $targetDate, - 'order' => $piggyBank->order, - 'active' => $piggyBank->active, - 'notes' => $note, - 'object_group_id' => $group ? $group['object_group_id'] : null, - 'object_group_order' => $group ? $group['object_group_order'] : null, - 'object_group_title' => $group ? $group['object_group_title'] : null, - 'links' => [ - [ - 'rel' => 'self', - 'uri' => '/piggy_banks/'.$piggyBank->id, - ], - ], - ]; - } - - private function getSuggestedMonthlyAmount(string $currentAmount, string $targetAmount, ?Carbon $startDate, ?Carbon $targetDate): string - { - $savePerMonth = '0'; - if (!$targetDate instanceof Carbon) { - return '0'; - } - if (bccomp($currentAmount, $targetAmount) < 1) { - $now = today(config('app.timezone')); - $startDate = $startDate instanceof Carbon && $startDate->gte($now) ? $startDate : $now; - $diffInMonths = (int) $startDate->diffInMonths($targetDate); - $remainingAmount = bcsub($targetAmount, $currentAmount); - - // more than 1 month to go and still need money to save: - if ($diffInMonths > 0 && 1 === bccomp($remainingAmount, '0')) { - $savePerMonth = bcdiv($remainingAmount, (string) $diffInMonths); - } - - // less than 1 month to go but still need money to save: - if (0 === $diffInMonths && 1 === bccomp($remainingAmount, '0')) { - $savePerMonth = $remainingAmount; - } - } - - return $savePerMonth; - } -} diff --git a/app/Transformers/V2/PreferenceTransformer.php b/app/Transformers/V2/PreferenceTransformer.php deleted file mode 100644 index a93c7e1aec..0000000000 --- a/app/Transformers/V2/PreferenceTransformer.php +++ /dev/null @@ -1,56 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Models\Preference; -use Illuminate\Support\Collection; - -/** - * Class PreferenceTransformer - * - * @deprecated - */ -class PreferenceTransformer extends AbstractTransformer -{ - public function collectMetaData(Collection $objects): Collection - { - // TODO: Implement collectMetaData() method. - return $objects; - } - - /** - * Transform the preference - */ - public function transform(Preference $preference): array - { - return [ - 'id' => $preference->id, - 'created_at' => $preference->created_at->toAtomString(), - 'updated_at' => $preference->updated_at->toAtomString(), - 'name' => $preference->name, - 'data' => $preference->data, - ]; - } -} diff --git a/app/Transformers/V2/TransactionGroupTransformer.php b/app/Transformers/V2/TransactionGroupTransformer.php deleted file mode 100644 index 66077a857a..0000000000 --- a/app/Transformers/V2/TransactionGroupTransformer.php +++ /dev/null @@ -1,625 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use Carbon\Carbon; -use FireflyIII\Enums\TransactionTypeEnum; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Budget; -use FireflyIII\Models\Category; -use FireflyIII\Models\Location; -use FireflyIII\Models\Note; -use FireflyIII\Models\Transaction; -use FireflyIII\Models\TransactionCurrency; -use FireflyIII\Models\TransactionGroup; -use FireflyIII\Models\TransactionJournal; -use FireflyIII\Models\TransactionJournalMeta; -use FireflyIII\Support\Http\Api\ExchangeRateConverter; -use FireflyIII\Support\NullArrayObject; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\DB; -use stdClass; - -/** - * Class TransactionGroupTransformer - * - * @deprecated - */ -class TransactionGroupTransformer extends AbstractTransformer -{ - private array $accountTypes = []; // account types collection. - private ExchangeRateConverter $converter; // collection of all journals and some important meta-data. - private array $currencies = []; - private TransactionCurrency $default; // collection of all currencies for this transformer. - private array $journals = []; - private array $meta = []; - - // private array $currencies = []; - // private array $transactionTypes = []; - private array $notes = []; - private array $objects = []; - // private array $locations = []; - private array $tags = []; - // private array $amounts = []; - // private array $foreignAmounts = []; - // private array $journalCurrencies = []; - // private array $foreignCurrencies = []; - - public function collectMetaData(Collection $objects): Collection - { - $collectForObjects = false; - - /** @var array|TransactionGroup $object */ - foreach ($objects as $object) { - if (is_array($object)) { - $this->collectForArray($object); - } - if ($object instanceof TransactionGroup) { - $this->collectForObject($object); - $collectForObjects = true; - } - } - - $this->default = app('amount')->getPrimaryCurrency(); - $this->converter = new ExchangeRateConverter(); - - $this->collectAllMetaData(); - $this->collectAllNotes(); - $this->collectAllLocations(); - $this->collectAllTags(); - if ($collectForObjects) { - $this->collectAllCurrencies(); - // $this->collectAllAmounts(); - // $this->collectTransactionTypes(); - // $this->collectAccounts(); - // source accounts - // destination accounts - } - - return $objects; - } - - private function collectForArray(array $object): void - { - foreach ($object['sums'] as $sum) { - $this->currencies[(int) $sum['currency_id']] ??= TransactionCurrency::find($sum['currency_id']); - } - - /** @var array $transaction */ - foreach ($object['transactions'] as $transaction) { - $this->journals[(int) $transaction['transaction_journal_id']] = []; - } - } - - private function collectForObject(TransactionGroup $object): void - { - foreach ($object->transactionJournals as $journal) { - $this->journals[$journal->id] = []; - $this->objects[] = $journal; - } - } - - private function collectAllMetaData(): void - { - $meta = TransactionJournalMeta::whereIn('transaction_journal_id', array_keys($this->journals))->get(); - - /** @var TransactionJournalMeta $entry */ - foreach ($meta as $entry) { - $id = $entry->transaction_journal_id; - $this->journals[$id]['meta'] ??= []; - $this->journals[$id]['meta'][$entry->name] = $entry->data; - } - } - - private function collectAllNotes(): void - { - // grab all notes for all journals: - $notes = Note::whereNoteableType(TransactionJournal::class)->whereIn('noteable_id', array_keys($this->journals))->get(); - - /** @var Note $note */ - foreach ($notes as $note) { - $id = $note->noteable_id; - $this->journals[$id]['notes'] = $note->text; - } - } - - private function collectAllLocations(): void - { - // grab all locations for all journals: - $locations = Location::whereLocatableType(TransactionJournal::class)->whereIn('locatable_id', array_keys($this->journals))->get(); - - /** @var Location $location */ - foreach ($locations as $location) { - $id = $location->locatable_id; - $this->journals[$id]['location'] = [ - 'latitude' => $location->latitude, - 'longitude' => $location->longitude, - 'zoom_level' => $location->zoom_level, - ]; - } - } - - private function collectAllTags(): void - { - // grab all tags for all journals: - $tags = DB::table('tag_transaction_journal') - ->leftJoin('tags', 'tags.id', 'tag_transaction_journal.tag_id') - ->whereIn('tag_transaction_journal.transaction_journal_id', array_keys($this->journals)) - ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag']) - ; - - /** @var stdClass $tag */ - foreach ($tags as $tag) { - $id = (int) $tag->transaction_journal_id; - $this->journals[$id]['tags'][] = $tag->tag; - } - } - - private function collectAllCurrencies(): void - { - /** @var TransactionJournal $journal */ - foreach ($this->objects as $journal) { - $id = $journal->id; - $this->journals[$id]['reconciled'] = false; - $this->journals[$id]['foreign_amount'] = null; - $this->journals[$id]['foreign_currency_id'] = null; - $this->journals[$id]['amount'] = null; - $this->journals[$id]['currency_id'] = null; - $this->journals[$id]['type'] = $journal->transactionType->type; - $this->journals[$id]['budget_id'] = null; - $this->journals[$id]['budget_name'] = null; - $this->journals[$id]['category_id'] = null; - $this->journals[$id]['category_name'] = null; - $this->journals[$id]['bill_id'] = null; - $this->journals[$id]['bill_name'] = null; - - // collect budget: - /** @var null|Budget $budget */ - $budget = $journal->budgets()->first(); - if (null !== $budget) { - $this->journals[$id]['budget_id'] = (string) $budget->id; - $this->journals[$id]['budget_name'] = $budget->name; - } - - // collect category: - /** @var null|Category $category */ - $category = $journal->categories()->first(); - if (null !== $category) { - $this->journals[$id]['category_id'] = (string) $category->id; - $this->journals[$id]['category_name'] = $category->name; - } - - // collect bill: - if (null !== $journal->bill_id) { - $bill = $journal->bill; - $this->journals[$id]['bill_id'] = (string) $bill->id; - $this->journals[$id]['bill_name'] = $bill->name; - } - - /** @var Transaction $transaction */ - foreach ($journal->transactions as $transaction) { - if (-1 === bccomp((string) $transaction->amount, '0')) { - // only collect source account info - $account = $transaction->account; - $this->accountTypes[$account->account_type_id] ??= $account->accountType->type; - $this->journals[$id]['source_account_name'] = $account->name; - $this->journals[$id]['source_account_iban'] = $account->iban; - $this->journals[$id]['source_account_type'] = $this->accountTypes[$account->account_type_id]; - $this->journals[$id]['source_account_id'] = $transaction->account_id; - $this->journals[$id]['reconciled'] = $transaction->reconciled; - - continue; - } - - // add account - $account = $transaction->account; - $this->accountTypes[$account->account_type_id] ??= $account->accountType->type; - $this->journals[$id]['destination_account_name'] = $account->name; - $this->journals[$id]['destination_account_iban'] = $account->iban; - $this->journals[$id]['destination_account_type'] = $this->accountTypes[$account->account_type_id]; - $this->journals[$id]['destination_account_id'] = $transaction->account_id; - - // find and set currency - $currencyId = $transaction->transaction_currency_id; - $this->currencies[$currencyId] ??= $transaction->transactionCurrency; - $this->journals[$id]['currency_id'] = $currencyId; - $this->journals[$id]['amount'] = $transaction->amount; - // find and set foreign currency - if (null !== $transaction->foreign_currency_id) { - $foreignCurrencyId = $transaction->foreign_currency_id; - $this->currencies[$foreignCurrencyId] ??= $transaction->foreignCurrency; - $this->journals[$id]['foreign_currency_id'] = $foreignCurrencyId; - $this->journals[$id]['foreign_amount'] = $transaction->foreign_amount; - } - - // find and set destination account info. - } - } - } - - public function transform(array|TransactionGroup $group): array - { - if (is_array($group)) { - $first = reset($group['transactions']); - - return [ - 'id' => (string) $group['id'], - 'created_at' => $group['created_at']->toAtomString(), - 'updated_at' => $group['updated_at']->toAtomString(), - 'user' => (string) $first['user_id'], - 'user_group' => (string) $first['user_group_id'], - 'group_title' => $group['title'] ?? null, - 'transactions' => $this->transformTransactions($group['transactions'] ?? []), - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/transactions/%d', $group['id']), - ], - ], - ]; - } - - return [ - 'id' => (string) $group->id, - 'created_at' => $group->created_at->toAtomString(), - 'updated_at' => $group->created_at->toAtomString(), - 'user' => (string) $group->user_id, - 'user_group' => (string) $group->user_group_id, - 'group_title' => $group->title ?? null, - 'transactions' => $this->transformJournals($group), - 'links' => [ - [ - 'rel' => 'self', - 'uri' => sprintf('/transactions/%d', $group->id), - ], - ], - ]; - } - - private function transformTransactions(array $transactions): array - { - $return = []; - - /** @var array $transaction */ - foreach ($transactions as $transaction) { - $return[] = $this->transformTransaction($transaction); - } - - return $return; - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.ExcessiveMethodLength") - */ - private function transformTransaction(array $transaction): array - { - $transaction = new NullArrayObject($transaction); - $type = $this->stringFromArray($transaction, 'transaction_type_type', TransactionTypeEnum::WITHDRAWAL->value); - $journalId = (int) $transaction['transaction_journal_id']; - $meta = new NullArrayObject($this->meta[$journalId] ?? []); - - /** - * Convert and use amount: - */ - $amount = app('steam')->positive((string) ($transaction['amount'] ?? '0')); - $currencyId = (int) $transaction['currency_id']; - $nativeAmount = $this->converter->convert($this->default, $this->currencies[$currencyId], $transaction['date'], $amount); - $foreignAmount = null; - $nativeForeignAmount = null; - if (null !== $transaction['foreign_amount']) { - $foreignCurrencyId = (int) $transaction['foreign_currency_id']; - $foreignAmount = app('steam')->positive($transaction['foreign_amount']); - $nativeForeignAmount = $this->converter->convert($this->default, $this->currencies[$foreignCurrencyId], $transaction['date'], $foreignAmount); - } - $this->converter->summarize(); - - $longitude = null; - $latitude = null; - $zoomLevel = null; - if (array_key_exists('location', $this->journals[$journalId])) { - $latitude = (string) $this->journals[$journalId]['location']['latitude']; - $longitude = (string) $this->journals[$journalId]['location']['longitude']; - $zoomLevel = $this->journals[$journalId]['location']['zoom_level']; - } - - return [ - 'user' => (string) $transaction['user_id'], - 'user_group' => (string) $transaction['user_group_id'], - 'transaction_journal_id' => (string) $transaction['transaction_journal_id'], - 'type' => strtolower((string) $type), - 'date' => $transaction['date']->toAtomString(), - 'order' => $transaction['order'], - 'amount' => $amount, - 'native_amount' => $nativeAmount, - 'foreign_amount' => $foreignAmount, - 'native_foreign_amount' => $nativeForeignAmount, - 'currency_id' => (string) $transaction['currency_id'], - 'currency_code' => $transaction['currency_code'], - 'currency_name' => $transaction['currency_name'], - 'currency_symbol' => $transaction['currency_symbol'], - 'currency_decimal_places' => (int) $transaction['currency_decimal_places'], - - // converted to native currency - 'native_currency_id' => (string) $this->default->id, - 'native_currency_code' => $this->default->code, - 'native_currency_name' => $this->default->name, - 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => $this->default->decimal_places, - - // foreign currency amount: - 'foreign_currency_id' => $this->stringFromArray($transaction, 'foreign_currency_id', null), - 'foreign_currency_code' => $transaction['foreign_currency_code'], - 'foreign_currency_name' => $transaction['foreign_currency_name'], - 'foreign_currency_symbol' => $transaction['foreign_currency_symbol'], - 'foreign_currency_decimal_places' => $transaction['foreign_currency_decimal_places'], - - // foreign converted to native: - 'description' => $transaction['description'], - 'source_id' => (string) $transaction['source_account_id'], - 'source_name' => $transaction['source_account_name'], - 'source_iban' => $transaction['source_account_iban'], - 'source_type' => $transaction['source_account_type'], - 'destination_id' => (string) $transaction['destination_account_id'], - 'destination_name' => $transaction['destination_account_name'], - 'destination_iban' => $transaction['destination_account_iban'], - 'destination_type' => $transaction['destination_account_type'], - 'budget_id' => $this->stringFromArray($transaction, 'budget_id', null), - 'budget_name' => $transaction['budget_name'], - 'category_id' => $this->stringFromArray($transaction, 'category_id', null), - 'category_name' => $transaction['category_name'], - 'bill_id' => $this->stringFromArray($transaction, 'bill_id', null), - 'bill_name' => $transaction['bill_name'], - 'reconciled' => $transaction['reconciled'], - 'notes' => $this->notes[$journalId] ?? null, - 'tags' => $this->tags[$journalId] ?? [], - 'internal_reference' => $meta['internal_reference'], - 'external_id' => $meta['external_id'], - 'original_source' => $meta['original_source'], - 'recurrence_id' => $meta['recurrence_id'], - 'recurrence_total' => $meta['recurrence_total'], - 'recurrence_count' => $meta['recurrence_count'], - 'external_url' => $meta['external_url'], - 'import_hash_v2' => $meta['import_hash_v2'], - 'sepa_cc' => $meta['sepa_cc'], - 'sepa_ct_op' => $meta['sepa_ct_op'], - 'sepa_ct_id' => $meta['sepa_ct_id'], - 'sepa_db' => $meta['sepa_db'], - 'sepa_country' => $meta['sepa_country'], - 'sepa_ep' => $meta['sepa_ep'], - 'sepa_ci' => $meta['sepa_ci'], - 'sepa_batch_id' => $meta['sepa_batch_id'], - 'interest_date' => $this->date($meta['interest_date']), - 'book_date' => $this->date($meta['book_date']), - 'process_date' => $this->date($meta['process_date']), - 'due_date' => $this->date($meta['due_date']), - 'payment_date' => $this->date($meta['payment_date']), - 'invoice_date' => $this->date($meta['invoice_date']), - - // location data - 'longitude' => $longitude, - 'latitude' => $latitude, - 'zoom_level' => $zoomLevel, - // - // 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']), - ]; - } - - /** - * TODO also in the old transformer. - * - * Used to extract a value from the given array, and fall back on a sensible default or NULL - * if it can't be helped. - */ - private function stringFromArray(NullArrayObject $array, string $key, ?string $default): ?string - { - // app('log')->debug(sprintf('%s: %s', $key, var_export($array[$key], true))); - if (null === $array[$key] && null === $default) { - return null; - } - if (0 === $array[$key]) { - return $default; - } - if ('0' === $array[$key]) { - return $default; - } - if (null !== $array[$key]) { - return (string) $array[$key]; - } - - if (null !== $default) { - return $default; - } - - return null; - } - - private function date(?string $string): ?Carbon - { - if (null === $string) { - return null; - } - // app('log')->debug(sprintf('Now in date("%s")', $string)); - if (10 === strlen($string)) { - $res = Carbon::createFromFormat('Y-m-d', $string, config('app.timezone')); - if (!$res instanceof Carbon) { - return null; - } - - return $res; - } - if (25 === strlen($string)) { - return Carbon::parse($string, config('app.timezone')); - } - if (19 === strlen($string) && str_contains($string, 'T')) { - $res = Carbon::createFromFormat('Y-m-d\TH:i:s', substr($string, 0, 19), config('app.timezone')); - if (!$res instanceof Carbon) { - return null; - } - - return $res; - } - - // 2022-01-01 01:01:01 - $res = Carbon::createFromFormat('Y-m-d H:i:s', substr($string, 0, 19), config('app.timezone')); - if (!$res instanceof Carbon) { - return null; - } - - return $res; - } - - private function transformJournals(TransactionGroup $group): array - { - $return = []; - - /** @var TransactionJournal $journal */ - foreach ($group->transactionJournals as $journal) { - $return[] = $this->transformJournal($journal); - } - - return $return; - } - - /** - * @throws FireflyException - * - * @SuppressWarnings("PHPMD.ExcessiveMethodLength") - */ - private function transformJournal(TransactionJournal $journal): array - { - $id = $journal->id; - - /** @var null|TransactionCurrency $foreignCurrency */ - $foreignCurrency = null; - - /** @var TransactionCurrency $currency */ - $currency = $this->currencies[$this->journals[$id]['currency_id']]; - $nativeForeignAmount = null; - $amount = $this->journals[$journal->id]['amount']; - $foreignAmount = $this->journals[$journal->id]['foreign_amount']; - $meta = new NullArrayObject($this->meta[$id] ?? []); - - // has foreign amount? - if (null !== $foreignAmount) { - $foreignCurrency = $this->currencies[$this->journals[$id]['foreign_currency_id']]; - $nativeForeignAmount = $this->converter->convert($this->default, $foreignCurrency, $journal->date, $foreignAmount); - } - - $nativeAmount = $this->converter->convert($this->default, $currency, $journal->date, $amount); - - $longitude = null; - $latitude = null; - $zoomLevel = null; - if (array_key_exists('location', $this->journals[$id])) { - $latitude = (string) $this->journals[$id]['location']['latitude']; - $longitude = (string) $this->journals[$id]['location']['longitude']; - $zoomLevel = $this->journals[$id]['location']['zoom_level']; - } - - return [ - 'user' => (string) $journal->user_id, - 'user_group' => (string) $journal->user_group_id, - 'transaction_journal_id' => (string) $journal->id, - 'type' => $this->journals[$journal->id]['type'], - 'date' => $journal->date->toAtomString(), - 'order' => $journal->order, - 'amount' => $amount, - 'native_amount' => $nativeAmount, - 'foreign_amount' => $foreignAmount, - 'native_foreign_amount' => $nativeForeignAmount, - 'currency_id' => (string) $currency->id, - 'currency_code' => $currency->code, - 'currency_name' => $currency->name, - 'currency_symbol' => $currency->symbol, - 'currency_decimal_places' => $currency->decimal_places, - - // converted to native currency - 'native_currency_id' => (string) $this->default->id, - 'native_currency_code' => $this->default->code, - 'native_currency_name' => $this->default->name, - 'native_currency_symbol' => $this->default->symbol, - 'native_currency_decimal_places' => $this->default->decimal_places, - - // foreign currency amount: - 'foreign_currency_id' => $foreignCurrency?->id, - 'foreign_currency_code' => $foreignCurrency?->code, - 'foreign_currency_name' => $foreignCurrency?->name, - 'foreign_currency_symbol' => $foreignCurrency?->symbol, - 'foreign_currency_decimal_places' => $foreignCurrency?->decimal_places, - - 'description' => $journal->description, - 'source_id' => (string) $this->journals[$id]['source_account_id'], - 'source_name' => $this->journals[$id]['source_account_name'], - 'source_iban' => $this->journals[$id]['source_account_iban'], - 'source_type' => $this->journals[$id]['source_account_type'], - - 'destination_id' => (string) $this->journals[$id]['destination_account_id'], - 'destination_name' => $this->journals[$id]['destination_account_name'], - 'destination_iban' => $this->journals[$id]['destination_account_iban'], - 'destination_type' => $this->journals[$id]['destination_account_type'], - - 'budget_id' => $this->journals[$id]['budget_id'], - 'budget_name' => $this->journals[$id]['budget_name'], - 'category_id' => $this->journals[$id]['category_id'], - 'category_name' => $this->journals[$id]['category_name'], - 'bill_id' => $this->journals[$id]['bill_id'], - 'bill_name' => $this->journals[$id]['bill_name'], - 'reconciled' => $this->journals[$id]['reconciled'], - 'notes' => $this->journals[$id]['notes'] ?? null, - 'tags' => $this->journals[$id]['tags'] ?? [], - 'internal_reference' => $meta['internal_reference'], - 'external_id' => $meta['external_id'], - 'original_source' => $meta['original_source'], - 'recurrence_id' => $meta['recurrence_id'], - 'recurrence_total' => $meta['recurrence_total'], - 'recurrence_count' => $meta['recurrence_count'], - 'external_url' => $meta['external_url'], - 'import_hash_v2' => $meta['import_hash_v2'], - 'sepa_cc' => $meta['sepa_cc'], - 'sepa_ct_op' => $meta['sepa_ct_op'], - 'sepa_ct_id' => $meta['sepa_ct_id'], - 'sepa_db' => $meta['sepa_db'], - 'sepa_country' => $meta['sepa_country'], - 'sepa_ep' => $meta['sepa_ep'], - 'sepa_ci' => $meta['sepa_ci'], - 'sepa_batch_id' => $meta['sepa_batch_id'], - 'interest_date' => $this->date($meta['interest_date']), - 'book_date' => $this->date($meta['book_date']), - 'process_date' => $this->date($meta['process_date']), - 'due_date' => $this->date($meta['due_date']), - 'payment_date' => $this->date($meta['payment_date']), - 'invoice_date' => $this->date($meta['invoice_date']), - - // location data - 'longitude' => $longitude, - 'latitude' => $latitude, - 'zoom_level' => $zoomLevel, - // - // 'has_attachments' => $this->hasAttachments((int) $row['transaction_journal_id']), - ]; - } -} diff --git a/app/Transformers/V2/UserGroupTransformer.php b/app/Transformers/V2/UserGroupTransformer.php deleted file mode 100644 index 5aba3042a9..0000000000 --- a/app/Transformers/V2/UserGroupTransformer.php +++ /dev/null @@ -1,120 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Transformers\V2; - -use FireflyIII\Enums\UserRoleEnum; -use FireflyIII\Models\GroupMembership; -use FireflyIII\Models\UserGroup; -use FireflyIII\User; -use Illuminate\Support\Collection; - -/** - * Class UserGroupTransformer - * - * @deprecated - */ -class UserGroupTransformer extends AbstractTransformer -{ - private array $inUse; - private array $memberships; - private array $membershipsVisible; - - public function __construct() - { - $this->memberships = []; - $this->membershipsVisible = []; - $this->inUse = []; - } - - public function collectMetaData(Collection $objects): Collection - { - if (auth()->check()) { - // collect memberships so they can be listed in the group. - /** @var User $user */ - $user = auth()->user(); - - /** @var UserGroup $userGroup */ - foreach ($objects as $userGroup) { - $userGroupId = $userGroup->id; - $this->inUse[$userGroupId] = $user->user_group_id === $userGroupId; - $access = $user->hasRoleInGroupOrOwner($userGroup, UserRoleEnum::VIEW_MEMBERSHIPS) || $user->hasRole('owner'); - $this->membershipsVisible[$userGroupId] = $access; - if ($access) { - $groupMemberships = $userGroup->groupMemberships()->get(); - - /** @var GroupMembership $groupMembership */ - foreach ($groupMemberships as $groupMembership) { - $this->memberships[$userGroupId][] = [ - 'user_id' => (string) $groupMembership->user_id, - 'user_email' => $groupMembership->user->email, - 'role' => $groupMembership->userRole->title, - 'you' => $groupMembership->user_id === $user->id, - ]; - } - } - } - $this->mergeMemberships(); - } - - return $objects; - } - - private function mergeMemberships(): void - { - $new = []; - foreach ($this->memberships as $groupId => $members) { - $new[$groupId] ??= []; - - foreach ($members as $member) { - $mail = $member['user_email']; - $new[$groupId][$mail] ??= [ - 'user_id' => $member['user_id'], - 'user_email' => $member['user_email'], - 'you' => $member['you'], - 'roles' => [], - ]; - $new[$groupId][$mail]['roles'][] = $member['role']; - } - } - $this->memberships = $new; - } - - /** - * Transform the user group. - */ - public function transform(UserGroup $userGroup): array - { - return [ - 'id' => $userGroup->id, - 'created_at' => $userGroup->created_at->toAtomString(), - 'updated_at' => $userGroup->updated_at->toAtomString(), - 'in_use' => $this->inUse[$userGroup->id] ?? false, - 'title' => $userGroup->title, - 'can_see_members' => $this->membershipsVisible[$userGroup->id] ?? false, - 'members' => array_values($this->memberships[$userGroup->id] ?? []), - ]; - // if the user has a specific role in this group, then collect the memberships. - } -}