bankAccountRepo = $bankAccountRepo; $this->vendorRepo = $vendorRepo; $this->expenseRepo = $expenseRepo; $this->datatableService = $datatableService; } protected function getRepo() { return $this->bankAccountRepo; } public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true) { if ( ! $bankId || ! $username || ! $password) { return false; } $expenses = Expense::scope() ->whereBankId($bankId) ->where('transaction_id', '!=', '') ->withTrashed() ->get(['transaction_id']) ->toArray(); $expenses = array_flip(array_map(function($val) { return $val['transaction_id']; }, $expenses)); $bankAccounts = BankSubaccount::scope() ->whereHas('bank_account', function($query) use ($bankId) { $query->where('bank_id', '=', $bankId); }) ->get(); $bank = Utils::getFromCache($bankId, 'banks'); $data = []; // load OFX trnansactions try { $finance = new Finance(); $finance->banks[$bankId] = $bank->getOFXBank($finance); $finance->banks[$bankId]->logins[] = new Login($finance->banks[$bankId], $username, $password); foreach ($finance->banks as $bank) { foreach ($bank->logins as $login) { $login->setup(); foreach ($login->accounts as $account) { $account->setup($includeTransactions); if ($account = $this->parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions)) { $data[] = $account; } } } } return $data; } catch (\Exception $e) { return false; } } private function parseBankAccount($account, $bankAccounts, $expenses, $includeTransactions) { $obj = new stdClass; $obj->account_name = ''; // look up bank account name foreach ($bankAccounts as $bankAccount) { if (Hash::check($account->id, $bankAccount->account_number)) { $obj->account_name = $bankAccount->account_name; } } // if we can't find a match skip the account if (count($bankAccounts) && ! $obj->account_name) { return false; } $obj->masked_account_number = Utils::maskAccountNumber($account->id); $obj->hashed_account_number = bcrypt($account->id); $obj->type = $account->type; $obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR); if ($includeTransactions) { $ofxParser = new \OfxParser\Parser; $ofx = $ofxParser->loadFromString($account->response); $obj->start_date = $ofx->BankAccount->Statement->startDate; $obj->end_date = $ofx->BankAccount->Statement->endDate; $obj->transactions = []; foreach ($ofx->BankAccount->Statement->transactions as $transaction) { // ensure transactions aren't imported as expenses twice if (isset($expenses[$transaction->uniqueId])) { continue; } if ($transaction->amount >= 0) { continue; } $transaction->vendor = $this->prepareValue(substr($transaction->name, 0, 20)); $transaction->info = $this->prepareValue(substr($transaction->name, 20)); $transaction->memo = $this->prepareValue($transaction->memo); $transaction->date = \Auth::user()->account->formatDate($transaction->date); $transaction->amount *= -1; $obj->transactions[] = $transaction; } } return $obj; } private function prepareValue($value) { return ucwords(strtolower(trim($value))); } public function importExpenses($bankId, $input) { $countVendors = 0; $countExpenses = 0; // create a vendor map $vendorMap = []; $vendors = Vendor::scope() ->withTrashed() ->get(['id', 'name', 'transaction_name']); foreach ($vendors as $vendor) { $vendorMap[strtolower($vendor->name)] = $vendor; $vendorMap[strtolower($vendor->transaction_name)] = $vendor; } foreach ($input as $transaction) { $vendorName = $transaction['vendor']; $key = strtolower($vendorName); $info = $transaction['info']; // find vendor otherwise create it if (isset($vendorMap[$key])) { $vendor = $vendorMap[$key]; } else { $field = $this->determineInfoField($info); $vendor = $this->vendorRepo->save([ $field => $info, 'name' => $vendorName, 'transaction_name' => $transaction['vendor_orig'], 'vendorcontact' => [] ]); $vendorMap[$key] = $vendor; $vendorMap[$transaction['vendor_orig']] = $vendor; $countVendors++; } // create the expense record $this->expenseRepo->save([ 'vendor_id' => $vendor->id, 'amount' => $transaction['amount'], 'public_notes' => $transaction['memo'], 'expense_date' => $transaction['date'], 'transaction_id' => $transaction['id'], 'bank_id' => $bankId, 'should_be_invoiced' => true, ]); $countExpenses++; } return trans('texts.imported_expenses', [ 'count_vendors' => $countVendors, 'count_expenses' => $countExpenses ]); } private function determineInfoField($value) { if (preg_match("/^[0-9\-\(\)\.]+$/", $value)) { return 'work_phone'; } elseif (strpos($value, '.') !== false) { return 'private_notes'; } else { return 'city'; } } public function getDatatable($accountId) { $query = $this->bankAccountRepo->find($accountId); return $this->createDatatable(ENTITY_BANK_ACCOUNT, $query, false); } protected function getDatatableColumns($entityType, $hideClient) { return [ [ 'bank_name', function ($model) { return link_to("bank_accounts/{$model->public_id}/edit", $model->bank_name); } ], [ 'bank_library_id', function ($model) { return 'OFX'; } ], ]; } protected function getDatatableActions($entityType) { return [ [ uctrans('texts.edit_bank_account'), function ($model) { return URL::to("bank_accounts/{$model->public_id}/edit"); } ] ]; } }