Exemplo n.º 1
0
 /**
  * @param TransactionJournal $journal
  *
  * @return bool
  */
 public function triggered(TransactionJournal $journal) : bool
 {
     $toAccountName = '';
     /** @var Account $account */
     foreach (TransactionJournal::destinationAccountList($journal) as $account) {
         $toAccountName .= strtolower($account->name);
     }
     $search = strtolower($this->triggerValue);
     if ($toAccountName === $search) {
         Log::debug(sprintf('RuleTrigger ToAccountIs for journal #%d: "%s" is "%s", return true.', $journal->id, $toAccountName, $search));
         return true;
     }
     Log::debug(sprintf('RuleTrigger ToAccountIs for journal #%d: "%s" is NOT "%s", return true.', $journal->id, $toAccountName, $search));
     return false;
 }
 /**
  * @param TransactionJournal $journal
  *
  * @return bool
  */
 public function triggered(TransactionJournal $journal) : bool
 {
     $toAccountName = '';
     /** @var Account $account */
     foreach (TransactionJournal::destinationAccountList($journal) as $account) {
         $toAccountName .= strtolower($account->name);
     }
     $search = strtolower($this->triggerValue);
     $part = substr($toAccountName, 0, strlen($search));
     if ($part === $search) {
         Log::debug(sprintf('RuleTrigger ToAccountStarts for journal #%d: "%s" starts with "%s", return true.', $journal->id, $toAccountName, $search));
         return true;
     }
     Log::debug(sprintf('RuleTrigger ToAccountStarts for journal #%d: "%s" does not start with "%s", return false.', $journal->id, $toAccountName, $search));
     return false;
 }
Exemplo n.º 3
0
 /**
  * @param TransactionJournal $journal
  *
  * @return Entry
  */
 public static function fromJournal(TransactionJournal $journal)
 {
     $entry = new self();
     $entry->description = $journal->description;
     $entry->date = $journal->date->format('Y-m-d');
     $entry->amount = TransactionJournal::amount($journal);
     $entry->budget = new EntryBudget($journal->budgets->first());
     $entry->category = new EntryCategory($journal->categories->first());
     $entry->bill = new EntryBill($journal->bill);
     $sources = TransactionJournal::sourceAccountList($journal);
     $destinations = TransactionJournal::destinationAccountList($journal);
     $entry->sourceAccount = new EntryAccount($sources->first());
     $entry->destinationAccount = new EntryAccount($destinations->first());
     foreach ($sources as $source) {
         $entry->sourceAccounts->push(new EntryAccount($source));
     }
     foreach ($destinations as $destination) {
         $entry->destinationAccounts->push(new EntryAccount($destination));
     }
     return $entry;
 }
Exemplo n.º 4
0
 /**
  * @param TransactionJournal $journal
  *
  * @return bool
  */
 public function triggered(TransactionJournal $journal) : bool
 {
     $toAccountName = '';
     /** @var Account $account */
     foreach (TransactionJournal::destinationAccountList($journal) as $account) {
         $toAccountName .= strtolower($account->name);
     }
     $toAccountNameLength = strlen($toAccountName);
     $search = strtolower($this->triggerValue);
     $searchLength = strlen($search);
     // if the string to search for is longer than the account name,
     // shorten the search string.
     if ($searchLength > $toAccountNameLength) {
         $search = substr($search, $toAccountNameLength * -1);
         $searchLength = strlen($search);
     }
     $part = substr($toAccountName, $searchLength * -1);
     if ($part === $search) {
         Log::debug(sprintf('RuleTrigger ToAccountEnds for journal #%d: "%s" ends with "%s", return true.', $journal->id, $toAccountName, $search));
         return true;
     }
     Log::debug(sprintf('RuleTrigger ToAccountEnds for journal #%d: "%s" does not end with "%s", return false.', $journal->id, $toAccountName, $search));
     return false;
 }
 /**
  * This method will call AccountRepositoryInterface::journalsInPeriod and get all deposits made to the given $accounts,
  * as well as the transfers that move away to those $accounts. This is a slightly sharper selection
  * than made by journalsInPeriod itself.
  *
  * @param Collection $accounts
  * @param Carbon     $start
  * @param Carbon     $end
  *
  * @see AccountRepositoryInterface::journalsInPeriod
  *
  * @return Collection
  */
 public function incomesInPeriod(Collection $accounts, Carbon $start, Carbon $end) : Collection
 {
     $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
     $journals = $this->journalsInPeriod($accounts, $types, $start, $end);
     $accountIds = $accounts->pluck('id')->toArray();
     // filter because some of these journals are still too much.
     $journals = $journals->filter(function (TransactionJournal $journal) use($accountIds) {
         if ($journal->transaction_type_type == TransactionType::DEPOSIT) {
             return true;
         }
         /*
          * The destination of a transfer must be one of the $accounts in order to
          * be included. Otherwise, it would not be income.
          */
         $destinations = TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray();
         if (count(array_intersect($destinations, $accountIds)) > 0) {
             // at least one of $target is in $haystack
             return true;
         }
         return false;
     });
     return $journals;
 }
 /**
  * @param Carbon     $start
  * @param Carbon     $end
  * @param Collection $accounts
  *
  * @return View
  */
 private function auditReport(Carbon $start, Carbon $end, Collection $accounts)
 {
     /** @var ARI $repos */
     $repos = app(ARI::class);
     $auditData = [];
     $dayBefore = clone $start;
     $dayBefore->subDay();
     /** @var Account $account */
     foreach ($accounts as $account) {
         // balance the day before:
         $id = $account->id;
         $first = $repos->oldestJournalDate($account);
         $last = $repos->newestJournalDate($account);
         $exists = false;
         $journals = new Collection();
         $dayBeforeBalance = Steam::balance($account, $dayBefore);
         /*
          * Is there even activity on this account between the requested dates?
          */
         if ($start->between($first, $last) || $end->between($first, $last)) {
             $exists = true;
             $journals = $repos->journalsInPeriod(new Collection([$account]), [], $start, $end);
         }
         /*
          * Reverse set, get balances.
          */
         $journals = $journals->reverse();
         $startBalance = $dayBeforeBalance;
         /** @var TransactionJournal $journal */
         foreach ($journals as $journal) {
             $journal->before = $startBalance;
             $transactionAmount = $journal->source_amount;
             // get currently relevant transaction:
             $destinations = TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray();
             if (in_array($account->id, $destinations)) {
                 $transactionAmount = TransactionJournal::amountPositive($journal);
             }
             $newBalance = bcadd($startBalance, $transactionAmount);
             $journal->after = $newBalance;
             $startBalance = $newBalance;
         }
         /*
          * Reverse set again.
          */
         $auditData[$id]['journals'] = $journals->reverse();
         $auditData[$id]['exists'] = $exists;
         $auditData[$id]['end'] = $end->formatLocalized(strval(trans('config.month_and_day')));
         $auditData[$id]['endBalance'] = Steam::balance($account, $end);
         $auditData[$id]['dayBefore'] = $dayBefore->formatLocalized(strval(trans('config.month_and_day')));
         $auditData[$id]['dayBeforeBalance'] = $dayBeforeBalance;
     }
     $reportType = 'audit';
     $accountIds = join(',', $accounts->pluck('id')->toArray());
     $hideable = ['buttons', 'icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date', 'from', 'to', 'budget', 'category', 'bill', 'internal_reference', 'notes', 'create_date', 'update_date'];
     $defaultShow = ['icon', 'description', 'balance_before', 'amount', 'balance_after', 'date', 'to'];
     return view('reports.audit.report', compact('start', 'end', 'reportType', 'accountIds', 'accounts', 'auditData', 'hideable', 'defaultShow'));
 }
 /**
  * Returns all the incomes that went to the given asset account.
  *
  * @param $attributes
  *
  * @return string
  * @throws FireflyException
  */
 private function incomeEntry(array $attributes) : string
 {
     /** @var AccountRepositoryInterface $repository */
     $repository = app(AccountRepositoryInterface::class);
     $crud = app('FireflyIII\\Crud\\Account\\AccountCrudInterface');
     $account = $crud->find(intval($attributes['accountId']));
     $types = [TransactionType::DEPOSIT, TransactionType::TRANSFER];
     $journals = $repository->journalsInPeriod(new Collection([$account]), $types, $attributes['startDate'], $attributes['endDate']);
     $destinations = $attributes['accounts']->pluck('id')->toArray();
     // filter for transfers and withdrawals FROM the given $account
     $journals = $journals->filter(function (TransactionJournal $journal) use($account, $destinations) {
         $currentSources = TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray();
         $currentDest = TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray();
         if (!empty(array_intersect([$account->id], $currentSources)) && !empty(array_intersect($destinations, $currentDest))) {
             return true;
         }
         return false;
     });
     $view = view('popup.report.income-entry', compact('journals', 'account'))->render();
     return $view;
 }
Exemplo n.º 8
0
 /**
  * @param Bill               $bill
  * @param TransactionJournal $journal
  *
  * @return bool
  */
 public function scan(Bill $bill, TransactionJournal $journal) : bool
 {
     /*
      * Can only support withdrawals.
      */
     if (false === $journal->isWithdrawal()) {
         return false;
     }
     $destinationAccounts = TransactionJournal::destinationAccountList($journal);
     $sourceAccounts = TransactionJournal::sourceAccountList($journal);
     $matches = explode(',', $bill->match);
     $description = strtolower($journal->description) . ' ';
     $description .= strtolower(join(' ', $destinationAccounts->pluck('name')->toArray()));
     $description .= strtolower(join(' ', $sourceAccounts->pluck('name')->toArray()));
     $wordMatch = $this->doWordMatch($matches, $description);
     $amountMatch = $this->doAmountMatch(TransactionJournal::amountPositive($journal), $bill->amount_min, $bill->amount_max);
     /*
      * If both, update!
      */
     if ($wordMatch && $amountMatch) {
         $journal->bill()->associate($bill);
         $journal->save();
         return true;
     }
     if ($bill->id == $journal->bill_id) {
         // if no match, but bill used to match, remove it:
         $journal->bill_id = null;
         $journal->save();
         return true;
     }
     return false;
 }
Exemplo n.º 9
0
 /**
  * The incoming journal ($journal)'s accounts (source accounts for a withdrawal, destination accounts for a deposit)
  * must match the already existing transaction's accounts exactly.
  *
  * @param TransactionJournal $journal
  * @param Tag                $tag
  *
  *
  * @return bool
  */
 protected function matchAll(TransactionJournal $journal, Tag $tag) : bool
 {
     $checkSources = join(',', TransactionJournal::sourceAccountList($journal)->pluck('id')->toArray());
     $checkDestinations = join(',', TransactionJournal::destinationAccountList($journal)->pluck('id')->toArray());
     $match = true;
     /** @var TransactionJournal $check */
     foreach ($tag->transactionjournals as $check) {
         // $checkAccount is the source_account for a withdrawal
         // $checkAccount is the destination_account for a deposit
         $thisSources = join(',', TransactionJournal::sourceAccountList($check)->pluck('id')->toArray());
         $thisDestinations = join(',', TransactionJournal::destinationAccountList($check)->pluck('id')->toArray());
         if ($check->isWithdrawal() && $thisSources !== $checkSources) {
             $match = false;
         }
         if ($check->isDeposit() && $thisDestinations !== $checkDestinations) {
             $match = false;
         }
     }
     if ($match) {
         $journal->tags()->save($tag);
         $journal->save();
         return true;
     }
     return false;
 }
Exemplo n.º 10
0
 /**
  * @param Request            $request
  * @param TransactionJournal $journal
  *
  * @return array
  */
 private function transactionsFromJournal(Request $request, TransactionJournal $journal) : array
 {
     /** @var Collection $transactions */
     $transactions = $journal->transactions()->get();
     /*
      * Splitted journals always have ONE source OR ONE destination.
      * Withdrawals have ONE source (asset account)
      * Deposits have ONE destination (asset account)
      * Transfers have ONE of both (asset account)
      */
     /** @var Account $singular */
     $singular = TransactionJournal::sourceAccountList($journal)->first();
     if ($journal->transactionType->type == TransactionType::DEPOSIT) {
         /** @var Account $singular */
         $singular = TransactionJournal::destinationAccountList($journal)->first();
     }
     /*
      * Loop all transactions. Collect info ONLY from the transaction that is NOT related to
      * the singular account.
      */
     $index = 0;
     $return = ['description' => [], 'source_account_id' => [], 'source_account_name' => [], 'destination_account_id' => [], 'destination_account_name' => [], 'amount' => [], 'budget_id' => [], 'category' => []];
     Log::debug('now at transactionsFromJournal');
     /**
      * @var int         $current
      * @var Transaction $transaction
      */
     foreach ($transactions as $current => $transaction) {
         $budget = $transaction->budgets()->first();
         $category = $transaction->categories()->first();
         $budgetId = 0;
         $categoryName = '';
         if (!is_null($budget)) {
             $budgetId = $budget->id;
         }
         if (!is_null($category)) {
             $categoryName = $category->name;
         }
         $budgetId = $request->old('budget_id')[$index] ?? $budgetId;
         $categoryName = $request->old('category')[$index] ?? $categoryName;
         $amount = $request->old('amount')[$index] ?? $transaction->amount;
         $description = $request->old('description')[$index] ?? $transaction->description;
         $destinationName = $request->old('destination_account_name')[$index] ?? $transaction->account->name;
         $sourceName = $request->old('source_account_name')[$index] ?? $transaction->account->name;
         $amount = bccomp($amount, '0') === -1 ? bcmul($amount, '-1') : $amount;
         if ($transaction->account_id !== $singular->id) {
             $return['description'][] = $description;
             $return['destination_account_id'][] = $transaction->account_id;
             $return['destination_account_name'][] = $destinationName;
             $return['source_account_name'][] = $sourceName;
             $return['amount'][] = $amount;
             $return['budget_id'][] = intval($budgetId);
             $return['category'][] = $categoryName;
             // only add one when "valid" transaction
             $index++;
         }
     }
     return $return;
 }
Exemplo n.º 11
0
 /**
  * @return Twig_SimpleFunction
  */
 public function getDestinationAccount() : Twig_SimpleFunction
 {
     return new Twig_SimpleFunction('destinationAccount', function (TransactionJournal $journal) {
         $cache = new CacheProperties();
         $cache->addProperty($journal->id);
         $cache->addProperty('transaction-journal');
         $cache->addProperty('destination-account-string');
         if ($cache->has()) {
             return $cache->get();
         }
         $list = TransactionJournal::destinationAccountList($journal);
         $array = [];
         /** @var Account $entry */
         foreach ($list as $entry) {
             if ($entry->accountType->type == 'Cash account') {
                 $array[] = '<span class="text-success">(cash)</span>';
                 continue;
             }
             $array[] = '<a title="' . e($entry->name) . '" href="' . route('accounts.show', $entry->id) . '">' . e($entry->name) . '</a>';
         }
         $array = array_unique($array);
         $result = join(', ', $array);
         $cache->store($result);
         return $result;
     });
 }
 /**
  * @param TransactionJournal $journal
  *
  * @return mixed
  */
 public function edit(TransactionJournal $journal)
 {
     $count = $journal->transactions()->count();
     if ($count > 2) {
         return redirect(route('split.journal.edit', [$journal->id]));
     }
     // code to get list data:
     $budgetRepository = app('FireflyIII\\Repositories\\Budget\\BudgetRepositoryInterface');
     $piggyRepository = app('FireflyIII\\Repositories\\PiggyBank\\PiggyBankRepositoryInterface');
     $crud = app('FireflyIII\\Crud\\Account\\AccountCrudInterface');
     $assetAccounts = ExpandedForm::makeSelectList($crud->getAccountsByType(['Default account', 'Asset account']));
     $budgetList = ExpandedForm::makeSelectListWithEmpty($budgetRepository->getActiveBudgets());
     $piggyBankList = ExpandedForm::makeSelectListWithEmpty($piggyRepository->getPiggyBanks());
     // view related code
     $subTitle = trans('breadcrumbs.edit_journal', ['description' => $journal->description]);
     $what = strtolower(TransactionJournal::transactionTypeStr($journal));
     // journal related code
     $sourceAccounts = TransactionJournal::sourceAccountList($journal);
     $destinationAccounts = TransactionJournal::destinationAccountList($journal);
     $optionalFields = Preferences::get('transaction_journal_optional_fields', [])->data;
     $preFilled = ['date' => TransactionJournal::dateAsString($journal), 'interest_date' => TransactionJournal::dateAsString($journal, 'interest_date'), 'book_date' => TransactionJournal::dateAsString($journal, 'book_date'), 'process_date' => TransactionJournal::dateAsString($journal, 'process_date'), 'category' => TransactionJournal::categoryAsString($journal), 'budget_id' => TransactionJournal::budgetId($journal), 'piggy_bank_id' => TransactionJournal::piggyBankId($journal), 'tags' => join(',', $journal->tags->pluck('tag')->toArray()), 'source_account_id' => $sourceAccounts->first()->id, 'source_account_name' => $sourceAccounts->first()->name, 'destination_account_id' => $destinationAccounts->first()->id, 'destination_account_name' => $destinationAccounts->first()->name, 'amount' => TransactionJournal::amountPositive($journal), 'due_date' => TransactionJournal::dateAsString($journal, 'due_date'), 'payment_date' => TransactionJournal::dateAsString($journal, 'payment_date'), 'invoice_date' => TransactionJournal::dateAsString($journal, 'invoice_date'), 'interal_reference' => $journal->getMeta('internal_reference'), 'notes' => $journal->getMeta('notes')];
     if ($journal->isWithdrawal() && $destinationAccounts->first()->accountType->type == AccountType::CASH) {
         $preFilled['destination_account_name'] = '';
     }
     if ($journal->isDeposit() && $sourceAccounts->first()->accountType->type == AccountType::CASH) {
         $preFilled['source_account_name'] = '';
     }
     Session::flash('preFilled', $preFilled);
     Session::flash('gaEventCategory', 'transactions');
     Session::flash('gaEventAction', 'edit-' . $what);
     // put previous url in session if not redirect from store (not "return_to_edit").
     if (session('transactions.edit.fromUpdate') !== true) {
         Session::put('transactions.edit.url', URL::previous());
     }
     Session::forget('transactions.edit.fromUpdate');
     return view('transactions.edit', compact('journal', 'optionalFields', 'assetAccounts', 'what', 'budgetList', 'piggyBankList', 'subTitle'))->with('data', $preFilled);
 }
Exemplo n.º 13
0
 /**
  * @param Collection $journals
  *
  * @return View
  */
 public function massEdit(Collection $journals)
 {
     $subTitle = trans('firefly.mass_edit_journals');
     $crud = app('FireflyIII\\Crud\\Account\\AccountCrudInterface');
     $accountList = ExpandedForm::makeSelectList($crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]));
     // skip transactions that have multiple destinations
     // or multiple sources:
     $filtered = new Collection();
     $messages = [];
     /**
      * @var int                $index
      * @var TransactionJournal $journal
      */
     foreach ($journals as $index => $journal) {
         $sources = TransactionJournal::sourceAccountList($journal);
         $destinations = TransactionJournal::destinationAccountList($journal);
         if ($sources->count() > 1) {
             $messages[] = trans('firefly.cannot_edit_multiple_source', ['description' => $journal->description, 'id' => $journal->id]);
             continue;
         }
         if ($destinations->count() > 1) {
             $messages[] = trans('firefly.cannot_edit_multiple_dest', ['description' => $journal->description, 'id' => $journal->id]);
             continue;
         }
         $filtered->push($journal);
     }
     if (count($messages)) {
         Session::flash('info', $messages);
     }
     // put previous url in session
     Session::put('transactions.mass-edit.url', URL::previous());
     Session::flash('gaEventCategory', 'transactions');
     Session::flash('gaEventAction', 'mass-edit');
     // set some values to be used in the edit routine:
     $filtered->each(function (TransactionJournal $journal) {
         $journal->amount = TransactionJournal::amountPositive($journal);
         $sources = TransactionJournal::sourceAccountList($journal);
         $destinations = TransactionJournal::destinationAccountList($journal);
         $journal->transaction_count = $journal->transactions()->count();
         if (!is_null($sources->first())) {
             $journal->source_account_id = $sources->first()->id;
             $journal->source_account_name = $sources->first()->name;
         }
         if (!is_null($destinations->first())) {
             $journal->destination_account_id = $destinations->first()->id;
             $journal->destination_account_name = $destinations->first()->name;
         }
     });
     if ($filtered->count() === 0) {
         Session::flash('error', trans('firefly.no_edit_multiple_left'));
     }
     $journals = $filtered;
     return view('transactions.mass-edit', compact('journals', 'subTitle', 'accountList'));
 }