mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support manually importing OFX files
This commit is contained in:
parent
52fe1b0dec
commit
b067697b1c
@ -402,17 +402,9 @@ class AccountController extends BaseController
|
||||
|
||||
private function showBankAccounts()
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
$account->load('bank_accounts');
|
||||
$count = count($account->bank_accounts);
|
||||
|
||||
if ($count == 0) {
|
||||
return Redirect::to('bank_accounts/create');
|
||||
} else {
|
||||
return View::make('accounts.banks', [
|
||||
'title' => trans('texts.bank_accounts')
|
||||
]);
|
||||
}
|
||||
return View::make('accounts.banks', [
|
||||
'title' => trans('texts.bank_accounts')
|
||||
]);
|
||||
}
|
||||
|
||||
private function showOnlinePayments()
|
||||
|
@ -13,12 +13,14 @@ use stdClass;
|
||||
use Crypt;
|
||||
use URL;
|
||||
use Utils;
|
||||
use File;
|
||||
use App\Models\Gateway;
|
||||
use App\Models\Account;
|
||||
use App\Models\BankAccount;
|
||||
use App\Ninja\Repositories\BankAccountRepository;
|
||||
use App\Services\BankAccountService;
|
||||
use App\Http\Requests\CreateBankAccountRequest;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class BankAccountController extends BaseController
|
||||
{
|
||||
@ -122,4 +124,28 @@ class BankAccountController extends BaseController
|
||||
return $this->bankAccountService->importExpenses($bankId, Input::all());
|
||||
}
|
||||
|
||||
public function showImportOFX()
|
||||
{
|
||||
return view('accounts.import_ofx');
|
||||
}
|
||||
|
||||
public function doImportOFX(Request $request)
|
||||
{
|
||||
$file = File::get($request->file('ofx_file'));
|
||||
|
||||
try {
|
||||
$data = $this->bankAccountService->parseOFX($file);
|
||||
} catch (\Exception $e) {
|
||||
Session::flash('error', trans('texts.ofx_parse_failed'));
|
||||
return view('accounts.import_ofx');
|
||||
}
|
||||
|
||||
$data = [
|
||||
'banks' => null,
|
||||
'bankAccount' => null,
|
||||
'transactions' => json_encode([$data])
|
||||
];
|
||||
|
||||
return View::make('accounts.bank_account', $data);
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +245,8 @@ Route::group([
|
||||
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
|
||||
Route::post('account_gateways/bulk', 'AccountGatewayController@bulk');
|
||||
|
||||
Route::get('bank_accounts/import_ofx', 'BankAccountController@showImportOFX');
|
||||
Route::post('bank_accounts/import_ofx', 'BankAccountController@doImportOFX');
|
||||
Route::resource('bank_accounts', 'BankAccountController');
|
||||
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
|
||||
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
|
||||
|
@ -48,6 +48,7 @@ class OFX
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
public static function closeTags($x)
|
||||
{
|
||||
$x = preg_replace('/\s+/', '', $x);
|
||||
@ -233,4 +234,3 @@ class Account
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,15 @@ class Expense extends EntityModel
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
public function scopeBankId($query, $bankdId = null)
|
||||
{
|
||||
if ($bankdId) {
|
||||
$query->whereBankId($bankId);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
|
||||
Expense::creating(function ($expense) {
|
||||
|
@ -34,14 +34,10 @@ class BankAccountService extends BaseService
|
||||
return $this->bankAccountRepo;
|
||||
}
|
||||
|
||||
public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true)
|
||||
private function getExpenses($bankId = null)
|
||||
{
|
||||
if (! $bankId || ! $username || ! $password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expenses = Expense::scope()
|
||||
->whereBankId($bankId)
|
||||
->bankId($bankId)
|
||||
->where('transaction_id', '!=', '')
|
||||
->withTrashed()
|
||||
->get(['transaction_id'])
|
||||
@ -50,6 +46,16 @@ class BankAccountService extends BaseService
|
||||
return $val['transaction_id'];
|
||||
}, $expenses));
|
||||
|
||||
return $expenses;
|
||||
}
|
||||
|
||||
public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true)
|
||||
{
|
||||
if (! $bankId || ! $username || ! $password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$expenses = $this->getExpenses();
|
||||
$vendorMap = $this->createVendorMap();
|
||||
$bankAccounts = BankSubaccount::scope()
|
||||
->whereHas('bank_account', function ($query) use ($bankId) {
|
||||
@ -106,44 +112,60 @@ class BankAccountService extends BaseService
|
||||
$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;
|
||||
}
|
||||
|
||||
// if vendor has already been imported use current name
|
||||
$vendorName = trim(substr($transaction->name, 0, 20));
|
||||
$key = strtolower($vendorName);
|
||||
$vendor = isset($vendorMap[$key]) ? $vendorMap[$key] : null;
|
||||
|
||||
$transaction->vendor = $vendor ? $vendor->name : $this->prepareValue($vendorName);
|
||||
$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;
|
||||
}
|
||||
$obj = $this->parseTransactions($obj, $account->response, $expenses, $vendorMap);
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private function parseTransactions($account, $data, $expenses, $vendorMap)
|
||||
{
|
||||
$ofxParser = new \OfxParser\Parser();
|
||||
$ofx = $ofxParser->loadFromString($data);
|
||||
|
||||
$account->start_date = $ofx->BankAccount->Statement->startDate;
|
||||
$account->end_date = $ofx->BankAccount->Statement->endDate;
|
||||
$account->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;
|
||||
}
|
||||
|
||||
// if vendor has already been imported use current name
|
||||
$vendorName = trim(substr($transaction->name, 0, 20));
|
||||
$key = strtolower($vendorName);
|
||||
$vendor = isset($vendorMap[$key]) ? $vendorMap[$key] : null;
|
||||
|
||||
$transaction->vendor = $vendor ? $vendor->name : $this->prepareValue($vendorName);
|
||||
$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;
|
||||
$account->transactions[] = $transaction;
|
||||
}
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
private function prepareValue($value)
|
||||
{
|
||||
return ucwords(strtolower(trim($value)));
|
||||
}
|
||||
|
||||
public function parseOFX($data)
|
||||
{
|
||||
$account = new stdClass;
|
||||
$expenses = $this->getExpenses();
|
||||
$vendorMap = $this->createVendorMap();
|
||||
|
||||
return $this->parseTransactions($account, $data, $expenses, $vendorMap);
|
||||
}
|
||||
|
||||
private function createVendorMap()
|
||||
{
|
||||
$vendorMap = [];
|
||||
@ -158,7 +180,7 @@ class BankAccountService extends BaseService
|
||||
return $vendorMap;
|
||||
}
|
||||
|
||||
public function importExpenses($bankId, $input)
|
||||
public function importExpenses($bankId = 0, $input)
|
||||
{
|
||||
$vendorMap = $this->createVendorMap();
|
||||
$countVendors = 0;
|
||||
|
@ -1295,7 +1295,9 @@ $LANG = array(
|
||||
'no_payment_method_specified' => 'No payment method specified',
|
||||
'chart_type' => 'Chart Type',
|
||||
'format' => 'Format',
|
||||
|
||||
'import_ofx' => 'Import OFX',
|
||||
'ofx_file' => 'OFX File',
|
||||
'ofx_parse_failed' => 'Failed to parse OFX file',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -482,6 +482,11 @@
|
||||
window.model = new ViewModel();
|
||||
ko.applyBindings(model);
|
||||
|
||||
@if (!empty($transactions))
|
||||
loadTransactions({!! $transactions !!});
|
||||
model.setPage('import');
|
||||
@endif
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -1,31 +1,35 @@
|
||||
@extends('header')
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_BANKS])
|
||||
@parent
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_BANKS])
|
||||
|
||||
{!! Button::primary(trans('texts.add_bank_account'))
|
||||
<div class="pull-right">
|
||||
{!! Button::normal(trans('texts.import_ofx'))
|
||||
->asLinkTo(URL::to('/bank_accounts/import_ofx'))
|
||||
->appendIcon(Icon::create('open')) !!}
|
||||
{!! Button::primary(trans('texts.add_bank_account'))
|
||||
->asLinkTo(URL::to('/bank_accounts/create'))
|
||||
->withAttributes(['class' => 'pull-right'])
|
||||
->appendIcon(Icon::create('plus-sign')) !!}
|
||||
</div>
|
||||
|
||||
@include('partials.bulk_form', ['entityType' => ENTITY_BANK_ACCOUNT])
|
||||
@include('partials.bulk_form', ['entityType' => ENTITY_BANK_ACCOUNT])
|
||||
|
||||
{!! Datatable::table()
|
||||
->addColumn(
|
||||
{!! Datatable::table()
|
||||
->addColumn(
|
||||
trans('texts.name'),
|
||||
trans('texts.integration_type'),
|
||||
trans('texts.action'))
|
||||
->setUrl(url('api/bank_accounts/'))
|
||||
->setOptions('sPaginationType', 'bootstrap')
|
||||
->setOptions('bFilter', false)
|
||||
->setOptions('bAutoWidth', false)
|
||||
->setOptions('aoColumns', [[ "sWidth"=> "50%" ], [ "sWidth"=> "30%" ], ["sWidth"=> "20%"]])
|
||||
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
||||
->render('datatable') !!}
|
||||
->setUrl(url('api/bank_accounts/'))
|
||||
->setOptions('sPaginationType', 'bootstrap')
|
||||
->setOptions('bFilter', false)
|
||||
->setOptions('bAutoWidth', false)
|
||||
->setOptions('aoColumns', [[ "sWidth"=> "50%" ], [ "sWidth"=> "30%" ], ["sWidth"=> "20%"]])
|
||||
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
||||
->render('datatable') !!}
|
||||
|
||||
<script>
|
||||
<script>
|
||||
window.onDatatableReady = actionListHandler;
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@stop
|
26
resources/views/accounts/import_ofx.blade.php
Normal file
26
resources/views/accounts/import_ofx.blade.php
Normal file
@ -0,0 +1,26 @@
|
||||
@extends('header')
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_BANKS])
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{{ trans('texts.import_ofx') }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
{!! Former::open_for_files('bank_accounts/import_ofx')
|
||||
->rules(['ofx_file' => 'required'])
|
||||
->addClass('warn-on-exit') !!}
|
||||
|
||||
{!! Former::file("ofx_file") !!}
|
||||
|
||||
{!! Former::actions( Button::info(trans('texts.upload'))->submit()->large()->appendIcon(Icon::create('open'))) !!}
|
||||
{!! Former::close() !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@stop
|
Loading…
x
Reference in New Issue
Block a user