. */ declare(strict_types=1); namespace FireflyIII\Support\Twig; use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalMeta; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; /** * Class TransactionGroupTwig */ class TransactionGroupTwig extends AbstractExtension { public function getFunctions(): array { return [ $this->journalArrayAmount(), $this->journalObjectAmount(), $this->journalHasMeta(), $this->journalGetMetaDate(), $this->journalGetMetaField(), ]; } /** * Shows the amount for a single journal array. */ public function journalArrayAmount(): TwigFunction { return new TwigFunction( 'journalArrayAmount', function (array $array): string { // if is not a withdrawal, amount positive. $result = $this->normalJournalArrayAmount($array); // now append foreign amount, if any. if (null !== $array['foreign_amount']) { $foreign = $this->foreignJournalArrayAmount($array); $result = sprintf('%s (%s)', $result, $foreign); } return $result; }, ['is_safe' => ['html']] ); } /** * Generate normal amount for transaction from a transaction group. */ private function normalJournalArrayAmount(array $array): string { $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; $amount = $array['amount'] ?? '0'; $colored = true; $sourceType = $array['source_account_type'] ?? 'invalid'; $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } $result = app('amount')->formatFlat($array['currency_symbol'], (int) $array['currency_decimal_places'], $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { $result = sprintf('%s', $result); } return $result; } private function signAmount(string $amount, string $transactionType, string $sourceType): string { // withdrawals stay negative if (TransactionTypeEnum::WITHDRAWAL->value !== $transactionType) { $amount = bcmul($amount, '-1'); } // opening balance and it comes from initial balance? its expense. if (TransactionTypeEnum::OPENING_BALANCE->value === $transactionType && AccountTypeEnum::INITIAL_BALANCE->value !== $sourceType) { $amount = bcmul($amount, '-1'); } // reconciliation and it comes from reconciliation? if (TransactionTypeEnum::RECONCILIATION->value === $transactionType && AccountTypeEnum::RECONCILIATION->value !== $sourceType) { $amount = bcmul($amount, '-1'); } return $amount; } /** * Generate foreign amount for transaction from a transaction group. */ private function foreignJournalArrayAmount(array $array): string { $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; $amount = $array['foreign_amount'] ?? '0'; $colored = true; $sourceType = $array['source_account_type'] ?? 'invalid'; $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int) $array['foreign_currency_decimal_places'], $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { $result = sprintf('%s', $result); } return $result; } /** * Shows the amount for a single journal object. */ public function journalObjectAmount(): TwigFunction { return new TwigFunction( 'journalObjectAmount', function (TransactionJournal $journal): string { $result = $this->normalJournalObjectAmount($journal); // now append foreign amount, if any. if ($this->journalObjectHasForeign($journal)) { $foreign = $this->foreignJournalObjectAmount($journal); $result = sprintf('%s (%s)', $result, $foreign); } return $result; }, ['is_safe' => ['html']] ); } /** * Generate normal amount for transaction from a transaction group. */ private function normalJournalObjectAmount(TransactionJournal $journal): string { $type = $journal->transactionType->type; /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); $currency = $journal->transactionCurrency; $amount = $first->amount ?? '0'; $colored = true; $sourceType = $first->account->accountType()->first()->type; $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { $result = sprintf('%s', $result); } return $result; } private function journalObjectHasForeign(TransactionJournal $journal): bool { /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); return '' !== $first->foreign_amount; } /** * Generate foreign amount for journal from a transaction group. */ private function foreignJournalObjectAmount(TransactionJournal $journal): string { $type = $journal->transactionType->type; /** @var Transaction $first */ $first = $journal->transactions()->where('amount', '<', 0)->first(); $currency = $first->foreignCurrency; $amount = '' === $first->foreign_amount ? '0' : $first->foreign_amount; $colored = true; $sourceType = $first->account->accountType()->first()->type; $amount = $this->signAmount($amount, $type, $sourceType); if (TransactionTypeEnum::TRANSFER->value === $type) { $colored = false; } $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); if (TransactionTypeEnum::TRANSFER->value === $type) { $result = sprintf('%s', $result); } return $result; } public function journalHasMeta(): TwigFunction { return new TwigFunction( 'journalHasMeta', static function (int $journalId, string $metaField) { $count = \DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) ->whereNull('deleted_at') ->count() ; return 1 === $count; } ); } public function journalGetMetaDate(): TwigFunction { return new TwigFunction( 'journalGetMetaDate', static function (int $journalId, string $metaField) { /** @var null|TransactionJournalMeta $entry */ $entry = \DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) ->whereNull('deleted_at') ->first() ; if (null === $entry) { return today(config('app.timezone')); } return new Carbon(json_decode($entry->data, false)); } ); } public function journalGetMetaField(): TwigFunction { return new TwigFunction( 'journalGetMetaField', static function (int $journalId, string $metaField) { /** @var null|TransactionJournalMeta $entry */ $entry = \DB::table('journal_meta') ->where('name', $metaField) ->where('transaction_journal_id', $journalId) ->whereNull('deleted_at') ->first() ; if (null === $entry) { return ''; } return json_decode($entry->data, true); } ); } }