_user = \Auth::user(); } /** * @return mixed */ public function count() { return $this->_user->accounts()->count(); } /** * @param \Account $account * * @return bool|mixed */ public function destroy(\Account $account) { // find all transaction journals related to this account: $journals = \TransactionJournal::withRelevantData()->accountIs($account)->get(['transaction_journals.*']); $accountIDs = []; /** @var \TransactionJournal $journal */ foreach ($journals as $journal) { // remember the account id's of the transactions involved: foreach ($journal->transactions as $t) { $accountIDs[] = $t->account_id; } $journal->delete(); } $accountIDs = array_unique($accountIDs); if (count($accountIDs) > 0) { // find the "initial balance" type accounts in this list. Should be just 1. $query = $this->_user->accounts()->accountTypeIn(['Initial balance account']) ->whereIn('accounts.id', $accountIDs); if ($query->count() == 1) { $iba = $query->first(['accounts.*']); $iba->delete(); } } $account->delete(); /** * * TODO * Also delete: initial balance, initial balance account, and transactions */ return true; } /** * @param $id * * @return |Account|null */ public function findAssetAccountById($id) { return $this->_user->accounts()->find($id); } /** * This method finds the expense account mentioned by name. This method is a sneaky little hobbits, * because when you feed it "Import account" it will always return an import account of that type. * * @param $name * * @return null|\Account */ public function findExpenseAccountByName($name) { $cashType = $this->findAccountType('Cash account'); $importType = $this->findAccountType('Import account'); // catch Import account: if ($name == 'Import account') { $import = $this->firstOrCreate( [ 'name' => 'Import account', 'user_id' => $this->_user->id, 'account_type_id' => $importType->id, 'active' => 1 ] ); return $import; } // find account: $account = $this->_user->accounts()->where('name', $name)->accountTypeIn( ['Expense account', 'Beneficiary account'] )->first(['accounts.*']); // create if not found: if (strlen($name) > 0 && is_null($account)) { $type = $this->findAccountType('Expense account'); $set = [ 'name' => $name, 'user_id' => $this->_user->id, 'active' => 1, 'account_type_id' => $type->id ]; $account = $this->firstOrCreate($set); } // find cash account as fall back: if (is_null($account)) { $account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first(); } // create cash account as ultimate fall back: if (is_null($account)) { $set = [ 'name' => 'Cash account', 'user_id' => $this->_user->id, 'active' => 1, 'account_type_id' => $cashType->id ]; $account = $this->firstOrCreate($set); } if ($account->active == 0 && $account->account_type_id != $cashType->id) { return null; } return $account; } /** * @param $type * * @return \AccountType|null */ public function findAccountType($type) { return \AccountType::where('type', $type)->first(); } public function firstOrCreate(array $data) { return \Account::firstOrCreate($data); } /** * @param $name * * @return |Account|null */ public function findRevenueAccountByName($name) { // catch Import account: if ($name == 'Import account') { $importType = $this->findAccountType('Import account'); $import = $this->firstOrCreate( [ 'name' => 'Import account', 'user_id' => $this->_user->id, 'account_type_id' => $importType->id, 'active' => 1 ] ); return $import; } // find account: $type = $this->findAccountType('Revenue account'); $account = $this->_user->accounts()->where('name', $name)->where('account_type_id', $type->id)->first(); // create if not found: if (strlen($name) > 0) { $set = [ 'name' => $name, 'user_id' => $this->_user->id, 'active' => 1, 'account_type_id' => $type->id ]; $account = $this->firstOrCreate($set); } // find cash account as fall back: if (is_null($account)) { $cashType = $this->findAccountType('Cash account'); $account = $this->_user->accounts()->where('account_type_id', $cashType->id)->first(); } // create cash account as ultimate fall back: if (is_null($account)) { $set = [ 'name' => 'Cash account', 'user_id' => $this->_user->id, 'active' => 1, 'account_type_id' => $cashType->id ]; $account = $this->firstOrCreate($set); } if ($account->active == 0) { return null; } return $account; } public function getByAccountType(\AccountType $type) { return $this->_user->accounts()->with('accounttype')->orderBy('name', 'ASC') ->where('account_type_id', $type->id)->get(); } /** * @param $ids * * @return array|mixed */ public function getByIds(array $ids) { if (count($ids) > 0) { return $this->_user->accounts()->with('accounttype')->whereIn('id', $ids)->orderBy('name', 'ASC')->get(); } else { return $this->getActiveDefault(); } } // // /** // * @param $name // * // * @return \Account|mixed|null // */ // public function createOrFindBeneficiary($name) // { // if (is_null($name) || strlen($name) == 0) { // return null; // } // $type = \AccountType::where('type', 'Expense account')->first(); // return $this->createOrFind($name, $type); // } // // /** // * @param $name // * @param \AccountType $type // * // * @return \Account|mixed // */ // public function createOrFind($name, \AccountType $type = null) // { // $account = $this->findByName($name, $type); // if (!$account) { // $data = [ // 'name' => $name, // 'account_type' => $type // ]; // // return $this->store($data); // } // // return $account; // } // // /** // * @param $name // * @param \AccountType $type // * // * @return mixed // */ // public function findByName($name, \AccountType $type = null) // { // $type = is_null($type) ? \AccountType::where('type', 'Asset account')->first() : $type; // // return $this->_user->accounts()->where('account_type_id', $type->id) // ->where('name', 'like', '%' . $name . '%') // ->first(); // } /** * @return mixed */ public function getActiveDefault() { return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account'])->where( 'accounts.active', 1 ) ->get(['accounts.*']); } /** * @return mixed */ public function getDefault() { return $this->_user->accounts()->accountTypeIn(['Default account', 'Asset account']) ->orderBy('accounts.name', 'ASC')->get(['accounts.*']); } /** * Gets a list of accounts that have the mentioned type. Will automatically convert * strings in this array to actual (model) account types. * * @param array $types * * @return Collection */ public function getOfTypes(array $types) { $accounts = $this->_user->accounts()->accountTypeIn($types)->get(['accounts.*']); return $accounts; } /** * @param Job $job * @param array $payload * * @return mixed */ public function importAccount(Job $job, array $payload) { /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); /** @var \Importmap $importMap */ $importMap = $repository->findImportmap($payload['mapID']); $user = $importMap->user; $this->overruleUser($user); /* * maybe Account is already imported: */ $importEntry = $repository->findImportEntry($importMap, 'Account', intval($payload['data']['id'])); /* * if so, delete job and return: */ if (!is_null($importEntry)) { \Log::debug('Already imported ' . $payload['data']['name'] . ' of type ' . $payload['account_type']); $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed return; } /* * find the payload's account type: */ $payload['account_type'] = isset($payload['account_type']) ? $payload['account_type'] : 'Expense account'; $type = $this->findAccountType($payload['account_type']); /* * Create data array for store() procedure. */ $data = [ 'account_type' => $type, 'name' => $payload['data']['name'], ]; if (isset($payload['data']['openingbalance'])) { $data['openingbalance'] = floatval($payload['data']['openingbalance']); $data['openingbalancedate'] = $payload['data']['openingbalancedate']; } if (isset($payload['data']['inactive'])) { $data['active'] = intval($payload['data']['inactive']) == 0 ? 1 : 0; } /* * Try to store: */ $account = $this->store($data); /* * Check for failure. */ if (count($account->errors()) > 0) { \Log::error('Account creation error: ' . $account->errors()->first()); $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed return; } \Log::debug('Imported ' . $payload['account_type'] . ': ' . $payload['data']['name']); /* * Save meta data */ $repository->store($importMap, 'Account', intval($payload['data']['id']), $account->id); $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed. return; } // /** // * Used for import // * // * @param $name // * // * @return mixed // */ // public function findByNameAny($name) // { // return $this->_user->accounts() // ->where('name', 'like', '%' . $name . '%') // ->first(); // } /** * @param $data * * @return \Account * @throws \Firefly\Exception\FireflyException */ public function store($data) { /** * If the AccountType has been passed through, use it: */ if (isset($data['account_type']) && is_object($data['account_type']) && get_class($data['account_type']) == 'AccountType' ) { $accountType = $data['account_type']; } else if (isset($data['account_type']) && is_string($data['account_type'])) { // if it isnt but set as string, find it: $accountType = \AccountType::where('type', $data['account_type'])->first(); } else { $accountType = \AccountType::where('type', 'Asset account')->first(); } $active = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval( $data['active'] ) : 1; /** * Create new account: */ $account = new \Account; $account->accountType()->associate($accountType); $account->user()->associate($this->_user); $account->name = $data['name']; $account->active = isset($data['active']) && intval($data['active']) >= 0 && intval($data['active']) <= 1 ? intval( $data['active'] ) : 1; // try to save it: try { if ($account->save()) { // create initial balance, if necessary: if (isset($data['openingbalance']) && isset($data['openingbalancedate'])) { $amount = floatval($data['openingbalance']); $date = new Carbon($data['openingbalancedate']); if ($amount != 0) { $this->_createInitialBalance($account, $amount, $date); } } } } catch (QueryException $e) { // do nothing } // whatever the result, return the account. return $account; } /** * @param \Account $account * @param int $amount * @param Carbon $date * * @return bool * @SuppressWarnings(PHPMD.CamelCaseMethodName) */ protected function _createInitialBalance(\Account $account, $amount = 0, Carbon $date) { /* * The repositories we need: */ /** @var \Firefly\Helper\Controllers\TransactionInterface $transactions */ $transactions = \App::make('Firefly\Helper\Controllers\TransactionInterface'); $transactions->overruleUser($this->_user); /* * get account type: */ $initialBalanceAT = $this->findAccountType('Initial balance account'); /* * create new account */ $initial = new \Account; $initial->accountType()->associate($initialBalanceAT); $initial->user()->associate($this->_user); $initial->name = $account->name . ' initial balance'; $initial->active = 0; if ($initial->validate()) { $initial->save(); /* * create new transaction journal (and transactions): */ $set = [ 'account_from_id' => $initial->id, 'account_to_id' => $account->id, 'description' => 'Initial Balance for ' . $account->name, 'what' => 'Opening balance', 'amount' => $amount, 'category' => '', 'date' => $date->format('Y-m-d') ]; $transactions->store($set); return true; } return false; } /** * Takes a transaction/account component and updates the transaction journal to match. * * @param Job $job * @param array $payload * * @return mixed */ public function importUpdateTransaction(Job $job, array $payload) { /** @var \Firefly\Storage\Import\ImportRepositoryInterface $repository */ $repository = \App::make('Firefly\Storage\Import\ImportRepositoryInterface'); /** @var \Importmap $importMap */ $importMap = $repository->findImportmap($payload['mapID']); $user = $importMap->user; $this->overruleUser($user); if ($job->attempts() > 10) { \Log::error('Never found budget/account combination "' . $payload['data']['transaction_id'] . '"'); $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed. return; } /** @var \Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface $journals */ $journals = \App::make('Firefly\Storage\TransactionJournal\TransactionJournalRepositoryInterface'); $journals->overruleUser($user); /* * Prep some vars from the payload */ $transactionId = intval($payload['data']['transaction_id']); $componentId = intval($payload['data']['component_id']); /* * Find the import map for both: */ $accountMap = $repository->findImportEntry($importMap, 'Account', $componentId); $transactionMap = $repository->findImportEntry($importMap, 'Transaction', $transactionId); /* * Either may be null: */ if (is_null($accountMap) || is_null($transactionMap)) { \Log::notice('No map found in account/transaction mapper. Release.'); if (\Config::get('queue.default') == 'sync') { $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed } else { $job->release(300); // proper release. } return; } /* * Find the account and the transaction: */ $account = $this->find($accountMap->new); /** @var \TransactionJournal $journal */ $journal = $journals->find($transactionMap->new); /* * If either is null, release: */ if (is_null($account) || is_null($journal)) { \Log::notice('Map is incorrect in account/transaction mapper. Release.'); if (\Config::get('queue.default') == 'sync') { $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed } else { $job->release(300); // proper release. } return; } /* * Update one of the journal's transactions to have the right account: */ $importType = $this->findAccountType('Import account'); /** @var \Transaction $transaction */ $updated = false; \Log::debug( 'Connect "' . $account->name . '" (#' . $account->id . ') to "' . $journal->description . '" (#' . $journal->id . ')' ); foreach ($journal->transactions as $index => $transaction) { /* * If it's of the right type, update it! */ \Log::debug( 'Transaction ' . $index . ' (#' . $transaction->id . '): [' . $transaction->account->account_type_id . ' vs. ' . $importType->id . ']' ); if ($transaction->account->account_type_id == $importType->id) { $transaction->account()->associate($account); $transaction->save(); $updated = true; \Log::debug( 'Connected expense account "' . $account->name . '" to journal "' . $journal->description . '"' ); } } if ($updated === false) { \Log::error( 'Did not connect transactions of journal #' . $journal->id . ' to expense account ' . $account->id ); } $journal->save(); $importMap->jobsdone++; $importMap->save(); $job->delete(); // count fixed } /** * @param \User $user * * @return mixed|void */ public function overruleUser(\User $user) { $this->_user = $user; return true; } /** * @param $accountId * * @return mixed */ public function find($accountId) { return $this->_user->accounts()->where('id', $accountId)->first(); } /** * @param \Account $account * @param $data * * @return \Account|mixed */ public function update(\Account $account, $data) { // update account accordingly: $account->name = $data['name']; if ($account->validate()) { $account->save(); } // update initial balance if necessary: if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) { /** @var \Firefly\Helper\Controllers\AccountInterface $interface */ $interface = \App::make('Firefly\Helper\Controllers\AccountInterface'); if ($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account') { $journal = $interface->openingBalanceTransaction($account); if ($journal) { $journal->date = new Carbon($data['openingbalancedate']); $journal->transactions[0]->amount = floatval($data['openingbalance']) * -1; $journal->transactions[1]->amount = floatval($data['openingbalance']); $journal->transactions[0]->save(); $journal->transactions[1]->save(); $journal->save(); } } } return $account; } }