/** * * @param array $ids * @param \Carbon\Carbon $date * * @return float */ public function balancesById(array $ids, Carbon $date) { // abuse chart properties: $cache = new CacheProperties(); $cache->addProperty($ids); $cache->addProperty('balances'); $cache->addProperty($date); if ($cache->has()) { return $cache->get(); // @codeCoverageIgnore } bcscale(2); $balances = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')->where('transaction_journals.date', '<=', $date->format('Y-m-d'))->groupBy('transactions.account_id')->whereIn('transactions.account_id', $ids)->get(['transactions.account_id', DB::Raw('sum(`transactions`.`amount`) as aggregate')]); $result = []; foreach ($balances as $entry) { $accountId = intval($entry->account_id); $balance = round($entry->aggregate, 2); $result[$accountId] = $balance; } $cache->store($result); return $result; }
/** * @param TransactionJournal $journal * @param array $transaction * * @return Collection */ public function storeTransaction(TransactionJournal $journal, array $transaction) : Collection { // store accounts (depends on type) list($sourceAccount, $destinationAccount) = $this->storeAccounts($journal->transactionType->type, $transaction); // store transaction one way: /** @var Transaction $one */ $one = Transaction::create(['account_id' => $sourceAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'] * -1, 'description' => $transaction['description']]); $two = Transaction::create(['account_id' => $destinationAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $transaction['amount'], 'description' => $transaction['description']]); if (strlen($transaction['category']) > 0) { $category = Category::firstOrCreateEncrypted(['name' => $transaction['category'], 'user_id' => $journal->user_id]); $one->categories()->save($category); $two->categories()->save($category); } if (intval($transaction['budget_id']) > 0) { $budget = Budget::find($transaction['budget_id']); $one->budgets()->save($budget); $two->budgets()->save($budget); } if ($transaction['piggy_bank_id'] > 0) { $transaction['date'] = $journal->date->format('Y-m-d'); event(new TransactionStored($transaction)); } return new Collection([$one, $two]); }
/** * @param Carbon $date * * @return TransactionJournal */ protected function createSavings(Carbon $date) { $date = new Carbon($date->format('Y-m') . '-24'); // paid on 24th. $toAccount = $this->findAccount('Savings'); $fromAccount = $this->findAccount('MyBank Checking Account'); $category = Category::firstOrCreateEncrypted(['name' => 'Money management', 'user_id' => $this->user->id]); // create journal: $journal = TransactionJournal::create(['user_id' => $this->user->id, 'transaction_type_id' => 3, 'transaction_currency_id' => 1, 'description' => 'Save money', 'completed' => 1, 'date' => $date]); Transaction::create(['account_id' => $fromAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => -150]); Transaction::create(['account_id' => $toAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => 150]); $journal->categories()->save($category); return $journal; }
/** * @param array $data * * @return TransactionJournal */ public function createJournal(array $data) { $user = User::whereEmail('*****@*****.**')->first(); $billID = isset($data['bill']) ? $data['bill']->id : null; /** @var TransactionJournal $journal */ $journal = TransactionJournal::create(['user_id' => $user->id, 'transaction_type_id' => $data['transactionType']->id, 'transaction_currency_id' => $data['transactionCurrency']->id, 'bill_id' => $billID, 'description' => $data['description'], 'completed' => 1, 'date' => $data['date']]); Transaction::create(['account_id' => $data['from']->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount'] * -1]); Transaction::create(['account_id' => $data['to']->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount']]); if (isset($data['budget'])) { $journal->budgets()->save($data['budget']); } if (isset($data['category'])) { $journal->categories()->save($data['category']); } return $journal; }
/** * @param $description * @param Carbon $date * @param $amount * * @return TransactionJournal */ protected function createWater($description, Carbon $date, $amount) { $date = new Carbon($date->format('Y-m') . '-10'); // paid on 10th $fromAccount = TestData::findAccount($this->user, 'TestData Checking Account'); $toAccount = TestData::findAccount($this->user, 'Vitens'); $category = Category::firstOrCreateEncrypted(['name' => 'House', 'user_id' => $this->user->id]); $budget = Budget::firstOrCreateEncrypted(['name' => 'Bills', 'user_id' => $this->user->id]); $journal = TransactionJournal::create(['user_id' => $this->user->id, 'transaction_type_id' => 1, 'transaction_currency_id' => 1, 'description' => $description, 'completed' => 1, 'date' => $date]); Transaction::create(['account_id' => $fromAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount * -1]); Transaction::create(['account_id' => $toAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount]); $journal->categories()->save($category); $journal->budgets()->save($budget); return $journal; }
/** * @covers FireflyIII\Repositories\Bill\BillRepository::scan * @covers FireflyIII\Repositories\Bill\BillRepository::doWordMatch * @covers FireflyIII\Repositories\Bill\BillRepository::doAmountMatch */ public function testScanNoMatchButAttached() { $bill = FactoryMuffin::create('FireflyIII\\Models\\Bill'); $bill->date = new Carbon('2012-01-07'); $bill->match = 'blablabla'; $bill->repeat_freq = 'monthly'; $bill->save(); $this->be($bill->user); // journal: $journal = FactoryMuffin::create('FireflyIII\\Models\\TransactionJournal'); $journal->date = Carbon::now()->format('Y-m-d'); $journal->user_id = $bill->user_id; $journal->bill_id = $bill->id; $journal->save(); // two transactions: $account1 = FactoryMuffin::create('FireflyIII\\Models\\Account'); $account2 = FactoryMuffin::create('FireflyIII\\Models\\Account'); Transaction::create(['account_id' => $account1->id, 'transaction_journal_id' => $journal->id, 'amount' => 100]); Transaction::create(['account_id' => $account2->id, 'transaction_journal_id' => $journal->id, 'amount' => 100]); $this->object->scan($bill, $journal); $newJournal = TransactionJournal::find($journal->id); $this->assertNull($newJournal->bill_id); }
/** * @param User $user */ private function openingBalanceSavings(User $user) { // opposing account for opening balance: $opposing = Account::create(['user_id' => $user->id, 'account_type_id' => 6, 'name' => 'Opposing for savings', 'active' => 1, 'encrypted' => 1]); // savings $savings = TestData::findAccount($user, 'TestData Savings'); $journal = TransactionJournal::create(['user_id' => $user->id, 'transaction_type_id' => 4, 'transaction_currency_id' => 1, 'description' => 'Opening balance for savings account', 'completed' => 1, 'date' => $this->start->format('Y-m-d')]); // transactions Transaction::create(['account_id' => $opposing->id, 'transaction_journal_id' => $journal->id, 'amount' => -10000]); Transaction::create(['account_id' => $savings->id, 'transaction_journal_id' => $journal->id, 'amount' => 10000]); }
/** * @param Account $account * @param Account $opposing * @param array $data * * @return TransactionJournal */ protected function storeInitialBalance(Account $account, Account $opposing, array $data) { $transactionType = TransactionType::whereType('Opening balance')->first(); $journal = TransactionJournal::create(['user_id' => $data['user'], 'transaction_type_id' => $transactionType->id, 'bill_id' => null, 'transaction_currency_id' => $data['openingBalanceCurrency'], 'description' => 'Initial balance for "' . $account->name . '"', 'completed' => true, 'date' => $data['openingBalanceDate'], 'encrypted' => true]); if ($data['openingBalance'] < 0) { $firstAccount = $opposing; $secondAccount = $account; $firstAmount = $data['openingBalance'] * -1; $secondAmount = $data['openingBalance']; } else { $firstAccount = $account; $secondAccount = $opposing; $firstAmount = $data['openingBalance']; $secondAmount = $data['openingBalance'] * -1; } $one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]); $one->save(); // first transaction: from $two = new Transaction(['account_id' => $secondAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $secondAmount]); $two->save(); // second transaction: to return $journal; }
/** * * @return TransactionJournal|string */ protected function createTransactionJournal() { bcscale(2); $date = $this->importData['date']; if (is_null($this->importData['date'])) { $date = $this->importData['date-rent']; } $transactionType = $this->getTransactionType(); // defaults to deposit $errors = new MessageBag(); $journal = TransactionJournal::create(['user_id' => Auth::user()->id, 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $this->importData['currency']->id, 'description' => $this->importData['description'], 'completed' => 0, 'date' => $date, 'bill_id' => $this->importData['bill-id']]); if ($journal->getErrors()->count() == 0) { // first transaction $accountId = $this->importData['asset-account-object']->id; // create first transaction: $amount = $this->importData['amount']; $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); $errors = $transaction->getErrors(); // second transaction $accountId = $this->importData['opposing-account-object']->id; // create second transaction: $amount = bcmul($this->importData['amount'], -1); $transaction = Transaction::create(['transaction_journal_id' => $journal->id, 'account_id' => $accountId, 'amount' => $amount]); $errors = $transaction->getErrors()->merge($errors); } if ($errors->count() == 0) { $journal->completed = 1; $journal->save(); } else { $text = join(',', $errors->all()); return $text; } $this->saveBudget($journal); $this->saveCategory($journal); $this->saveTags($journal); // some debug info: $journalId = $journal->id; $type = $journal->getTransactionType(); /** @var Account $asset */ $asset = $this->importData['asset-account-object']; /** @var Account $opposing */ $opposing = $this->importData['opposing-account-object']; Log::info('Created journal #' . $journalId . ' of type ' . $type . '!'); Log::info('Asset account ****** (#' . $asset->id . ') lost/gained: ' . $this->importData['amount']); Log::info($opposing->accountType->type . ' ****** (#' . $opposing->id . ') lost/gained: ' . bcmul($this->importData['amount'], -1)); return $journal; }
/** * @param int $index * @param ImportEntry $entry * * @return TransactionJournal * @throws FireflyException */ private function storeSingle(int $index, ImportEntry $entry) : TransactionJournal { if ($entry->valid === false) { Log::warning(sprintf('Cannot import row %d, because the entry is not valid.', $index)); $errors = join(', ', $entry->errors->all()); $errorText = sprintf('Row #%d: ' . $errors, $index); $extendedStatus = $this->job->extended_status; $extendedStatus['errors'][] = $errorText; $this->job->extended_status = $extendedStatus; $this->job->save(); return new TransactionJournal(); } $alreadyImported = $this->alreadyImported($entry->hash); if (!is_null($alreadyImported->id)) { Log::warning(sprintf('Cannot import row %d, because it has already been imported (journal #%d).', $index, $alreadyImported->id)); $errorText = trans('firefly.import_double', ['row' => $index, 'link' => route('transactions.show', [$alreadyImported->id]), 'description' => $alreadyImported->description]); $extendedStatus = $this->job->extended_status; $extendedStatus['errors'][] = $errorText; $this->job->extended_status = $extendedStatus; $this->job->save(); return new TransactionJournal(); } Log::debug(sprintf('Going to store row %d', $index)); $journal = $this->storeJournal($entry); $amount = $this->makePositive($entry->fields['amount']); $accounts = $this->storeAccounts($entry); // create new transactions. This is something that needs a rewrite for multiple/split transactions. $sourceData = ['account_id' => $accounts['source']->id, 'transaction_journal_id' => $journal->id, 'description' => $journal->description, 'amount' => bcmul($amount, '-1')]; $destinationData = ['account_id' => $accounts['destination']->id, 'transaction_journal_id' => $journal->id, 'description' => $journal->description, 'amount' => $amount]; $one = Transaction::create($sourceData); $two = Transaction::create($destinationData); $error = false; if (is_null($one->id)) { Log::error('Could not create transaction 1.', $one->getErrors()->all()); $error = true; } if (is_null($two->id)) { Log::error('Could not create transaction 1.', $two->getErrors()->all()); $error = true; } // respond to error if ($error === true) { $errorText = sprintf('Cannot import row %d, because an error occured when storing data.', $index); Log::error($errorText); $extendedStatus = $this->job->extended_status; $extendedStatus['errors'][] = $errorText; $this->job->extended_status = $extendedStatus; $this->job->save(); return new TransactionJournal(); } Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $accounts['source']->name]); Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $accounts['destination']->name]); $journal->completed = 1; $journal->save(); // attach import tag. $journal->tags()->save($this->importTag); // now attach budget and so on. $this->storeBudget($journal, $entry); $this->storeCategory($journal, $entry); $this->storeBill($journal, $entry); return $journal; }
/** * @param array $data * * @return TransactionJournal */ public function store(array $data) : TransactionJournal { // find transaction type. $transactionType = TransactionType::where('type', ucfirst($data['what']))->first(); // store actual journal. $journal = new TransactionJournal(['user_id' => $data['user'], 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $data['amount_currency_id_amount'], 'description' => $data['description'], 'completed' => 0, 'date' => $data['date'], 'interest_date' => $data['interest_date'], 'book_date' => $data['book_date'], 'process_date' => $data['process_date']]); $journal->save(); // store or get category if (strlen($data['category']) > 0) { $category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]); $journal->categories()->save($category); } // store or get budget if (intval($data['budget_id']) > 0 && $transactionType->type !== TransactionType::TRANSFER) { /** @var \FireflyIII\Models\Budget $budget */ $budget = Budget::find($data['budget_id']); $journal->budgets()->save($budget); } // store accounts (depends on type) list($sourceAccount, $destinationAccount) = $this->storeAccounts($transactionType, $data); // store accompanying transactions. Transaction::create(['account_id' => $sourceAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount'] * -1]); Transaction::create(['account_id' => $destinationAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount']]); $journal->completed = 1; $journal->save(); // store tags if (isset($data['tags']) && is_array($data['tags'])) { $this->saveTags($journal, $data['tags']); } foreach ($data as $key => $value) { if (in_array($key, $this->validMetaFields)) { $journal->setMeta($key, $value); continue; } Log::debug(sprintf('Could not store meta field "%s" with value "%s" for journal #%d', json_encode($key), json_encode($value), $journal->id)); } return $journal; }
/** * Reports on deleted transactions that are connected to a not deleted journal. */ private function reportTransactions() { $set = Transaction::leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')->whereNotNull('transactions.deleted_at')->whereNull('transaction_journals.deleted_at')->get(['transactions.id as transaction_id', 'transactions.deleted_at as transaction_deleted', 'transaction_journals.id as journal_id', 'transaction_journals.deleted_at']); /** @var stdClass $entry */ foreach ($set as $entry) { $this->error('Error: Transaction journal #' . $entry->journal_id . ' should have been deleted, but has not.' . ' Find it in the table called `transaction_journals` and change the `deleted_at` field to: "' . $entry->transaction_deleted . '"'); } }
/** * @param array $data * * @return TransactionJournal */ public function store(array $data) { // find transaction type. $transactionType = TransactionType::where('type', ucfirst($data['what']))->first(); // store actual journal. $journal = new TransactionJournal(['user_id' => $data['user'], 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $data['amount_currency_id'], 'description' => $data['description'], 'completed' => 0, 'date' => $data['date']]); $journal->save(); // store or get category if (strlen($data['category']) > 0) { $category = Category::firstOrCreateEncrypted(['name' => $data['category'], 'user_id' => $data['user']]); $journal->categories()->save($category); } // store or get budget if (intval($data['budget_id']) > 0) { /** @var \FireflyIII\Models\Budget $budget */ $budget = Budget::find($data['budget_id']); $journal->budgets()->save($budget); } // store accounts (depends on type) list($from, $to) = $this->storeAccounts($transactionType, $data); // store accompanying transactions. Transaction::create(['account_id' => $from->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount'] * -1]); Transaction::create(['account_id' => $to->id, 'transaction_journal_id' => $journal->id, 'amount' => $data['amount']]); $journal->completed = 1; $journal->save(); // store tags if (isset($data['tags']) && is_array($data['tags'])) { $this->saveTags($journal, $data['tags']); } return $journal; }
return $email; }, 'password' => bcrypt('james')]); FactoryMuffin::define('FireflyIII\\Models\\Transaction', ['transaction_journal_id' => 'factory|FireflyIII\\Models\\TransactionJournal', 'amount' => function () { return rand(1, 100); }, 'description' => 'sentence', 'account_id' => 'factory|FireflyIII\\Models\\Account']); FactoryMuffin::define('FireflyIII\\Models\\PiggyBank', ['account_id' => 'factory|FireflyIII\\Models\\Account', 'name' => 'sentence', 'targetamount' => function () { return rand(1, 100); }, 'startdate' => 'date', 'targetdate' => 'date', 'remind_me' => false, 'reminder_skip' => 0, 'order' => 0]); FactoryMuffin::define('FireflyIII\\Models\\PiggyBankRepetition', ['piggy_bank_id' => 'factory|FireflyIII\\Models\\PiggyBank', 'startdate' => 'date', 'targetdate' => 'date', 'currentamount' => function () { return rand(1, 100); }]); FactoryMuffin::define('FireflyIII\\Models\\PiggyBankEvent', ['piggy_bank_id' => 'factory|FireflyIII\\Models\\PiggyBank', 'transaction_journal_id' => 'factory|FireflyIII\\Models\\TransactionJournal', 'date' => 'date', 'amount' => function () { return rand(1, 100); }]); FactoryMuffin::define('FireflyIII\\Models\\TransactionType', ['type' => function () { $types = ['Withdrawal', 'Deposit', 'Transfer']; $count = DB::table('transaction_types')->count(); if ($count < 3) { return $types[$count]; } else { return RandomString::generateRandomString(10); } }]); FactoryMuffin::define('FireflyIII\\Models\\TransactionJournal', ['user_id' => 'factory|FireflyIII\\User', 'transaction_type_id' => 'factory|FireflyIII\\Models\\TransactionType', 'transaction_currency_id' => 'factory|FireflyIII\\Models\\TransactionCurrency', 'description' => 'sentence', 'completed' => '1', 'date' => 'date', 'encrypted' => '1', 'order' => '0'], function (TransactionJournal $object, $saved) { if ($saved) { $one = FactoryMuffin::create('FireflyIII\\Models\\Account'); $two = FactoryMuffin::create('FireflyIII\\Models\\Account'); Transaction::create(['account_id' => $one->id, 'transaction_journal_id' => $object->id, 'amount' => 100]); Transaction::create(['account_id' => $two->id, 'transaction_journal_id' => $object->id, 'amount' => -100]); } });
/** * @covers FireflyIII\Repositories\Journal\JournalRepository::update * @covers FireflyIII\Repositories\Journal\JournalRepository::updateTags * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testUpdateNoTags() { $user = FactoryMuffin::create('FireflyIII\\User'); $this->be($user); for ($i = 0; $i < 4; $i++) { FactoryMuffin::create('FireflyIII\\Models\\AccountType'); } FactoryMuffin::create('FireflyIII\\Models\\TransactionType'); FactoryMuffin::create('FireflyIII\\Models\\TransactionType'); $journal = FactoryMuffin::create('FireflyIII\\Models\\TransactionJournal'); $currency = FactoryMuffin::create('FireflyIII\\Models\\TransactionCurrency'); $budget = FactoryMuffin::create('FireflyIII\\Models\\Budget'); $account1 = FactoryMuffin::create('FireflyIII\\Models\\Account'); $account2 = FactoryMuffin::create('FireflyIII\\Models\\Account'); // two transactions Transaction::create(['account_id' => $account1->id, 'transaction_journal_id' => $journal->id, 'amount' => 100]); Transaction::create(['account_id' => $account1->id, 'transaction_journal_id' => $journal->id, 'amount' => -100]); $data = ['amount_currency_id' => $currency->id, 'description' => 'New description ' . rand(1, 100), 'date' => '2015-01-01', 'category' => 'SomenewCat', 'amount' => 50, 'user' => $journal->user_id, 'budget_id' => $budget->id, 'account_from_id' => $account1->id, 'account_to_id' => $account2->id, 'revenue_account' => 'Some revenue account', 'expense_account' => 'Some expense account', 'tags' => []]; $result = $this->object->update($journal, $data); $this->assertEquals($result->description, $data['description']); $this->assertEquals($result->amount, 50); }
/** * @param Account $account * @param array $data * * @return TransactionJournal */ protected function storeInitialBalance(Account $account, array $data) : TransactionJournal { $amount = $data['openingBalance']; $user = $data['user']; $name = $data['name']; $opposing = $this->storeOpposingAccount($amount, $user, $name); $transactionType = TransactionType::whereType(TransactionType::OPENING_BALANCE)->first(); $journal = TransactionJournal::create(['user_id' => $data['user'], 'transaction_type_id' => $transactionType->id, 'transaction_currency_id' => $data['openingBalanceCurrency'], 'description' => 'Initial balance for "' . $account->name . '"', 'completed' => true, 'date' => $data['openingBalanceDate'], 'encrypted' => true]); $firstAccount = $account; $secondAccount = $opposing; $firstAmount = $amount; $secondAmount = $amount * -1; if ($data['openingBalance'] < 0) { $firstAccount = $opposing; $secondAccount = $account; $firstAmount = $amount * -1; $secondAmount = $amount; } $one = new Transaction(['account_id' => $firstAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $firstAmount]); $one->save(); // first transaction: from $two = new Transaction(['account_id' => $secondAccount->id, 'transaction_journal_id' => $journal->id, 'amount' => $secondAmount]); $two->save(); // second transaction: to return $journal; }