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()
|
private function showBankAccounts()
|
||||||
{
|
{
|
||||||
$account = Auth::user()->account;
|
return View::make('accounts.banks', [
|
||||||
$account->load('bank_accounts');
|
'title' => trans('texts.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')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function showOnlinePayments()
|
private function showOnlinePayments()
|
||||||
|
@ -13,12 +13,14 @@ use stdClass;
|
|||||||
use Crypt;
|
use Crypt;
|
||||||
use URL;
|
use URL;
|
||||||
use Utils;
|
use Utils;
|
||||||
|
use File;
|
||||||
use App\Models\Gateway;
|
use App\Models\Gateway;
|
||||||
use App\Models\Account;
|
use App\Models\Account;
|
||||||
use App\Models\BankAccount;
|
use App\Models\BankAccount;
|
||||||
use App\Ninja\Repositories\BankAccountRepository;
|
use App\Ninja\Repositories\BankAccountRepository;
|
||||||
use App\Services\BankAccountService;
|
use App\Services\BankAccountService;
|
||||||
use App\Http\Requests\CreateBankAccountRequest;
|
use App\Http\Requests\CreateBankAccountRequest;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class BankAccountController extends BaseController
|
class BankAccountController extends BaseController
|
||||||
{
|
{
|
||||||
@ -122,4 +124,28 @@ class BankAccountController extends BaseController
|
|||||||
return $this->bankAccountService->importExpenses($bankId, Input::all());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use Utils;
|
|||||||
class BaseController extends Controller
|
class BaseController extends Controller
|
||||||
{
|
{
|
||||||
use DispatchesJobs, AuthorizesRequests;
|
use DispatchesJobs, AuthorizesRequests;
|
||||||
|
|
||||||
protected $entityType;
|
protected $entityType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,7 +60,7 @@ Route::group(['middleware' => 'auth:client'], function() {
|
|||||||
Route::get('client/documents/js/{documents}/{filename}', 'PublicClientController@getDocumentVFSJS');
|
Route::get('client/documents/js/{documents}/{filename}', 'PublicClientController@getDocumentVFSJS');
|
||||||
Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'PublicClientController@getDocument');
|
Route::get('client/documents/{invitation_key}/{documents}/{filename?}', 'PublicClientController@getDocument');
|
||||||
Route::get('client/documents/{invitation_key}/{filename?}', 'PublicClientController@getInvoiceDocumentsZip');
|
Route::get('client/documents/{invitation_key}/{filename?}', 'PublicClientController@getInvoiceDocumentsZip');
|
||||||
|
|
||||||
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
|
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
|
||||||
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
|
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
|
||||||
Route::get('api/client.recurring_invoices', array('as'=>'api.client.recurring_invoices', 'uses'=>'PublicClientController@recurringInvoiceDatatable'));
|
Route::get('api/client.recurring_invoices', array('as'=>'api.client.recurring_invoices', 'uses'=>'PublicClientController@recurringInvoiceDatatable'));
|
||||||
@ -123,7 +123,7 @@ Route::group(['middleware' => 'auth:user'], function() {
|
|||||||
Route::get('hide_message', 'HomeController@hideMessage');
|
Route::get('hide_message', 'HomeController@hideMessage');
|
||||||
Route::get('force_inline_pdf', 'UserController@forcePDFJS');
|
Route::get('force_inline_pdf', 'UserController@forcePDFJS');
|
||||||
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
|
Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData'));
|
||||||
|
|
||||||
Route::get('settings/user_details', 'AccountController@showUserDetails');
|
Route::get('settings/user_details', 'AccountController@showUserDetails');
|
||||||
Route::post('settings/user_details', 'AccountController@saveUserDetails');
|
Route::post('settings/user_details', 'AccountController@saveUserDetails');
|
||||||
Route::post('users/change_password', 'UserController@changePassword');
|
Route::post('users/change_password', 'UserController@changePassword');
|
||||||
@ -156,7 +156,7 @@ Route::group(['middleware' => 'auth:user'], function() {
|
|||||||
Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS');
|
Route::get('documents/js/{documents}/{filename}', 'DocumentController@getVFSJS');
|
||||||
Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview');
|
Route::get('documents/preview/{documents}/{filename?}', 'DocumentController@getPreview');
|
||||||
Route::post('document', 'DocumentController@postUpload');
|
Route::post('document', 'DocumentController@postUpload');
|
||||||
|
|
||||||
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
Route::get('quotes/create/{client_id?}', 'QuoteController@create');
|
||||||
Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice');
|
Route::get('quotes/{invoices}/clone', 'InvoiceController@cloneInvoice');
|
||||||
Route::get('quotes/{invoices}/edit', 'InvoiceController@edit');
|
Route::get('quotes/{invoices}/edit', 'InvoiceController@edit');
|
||||||
@ -245,6 +245,8 @@ Route::group([
|
|||||||
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
|
Route::get('api/gateways', array('as'=>'api.gateways', 'uses'=>'AccountGatewayController@getDatatable'));
|
||||||
Route::post('account_gateways/bulk', 'AccountGatewayController@bulk');
|
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::resource('bank_accounts', 'BankAccountController');
|
||||||
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
|
Route::get('api/bank_accounts', array('as'=>'api.bank_accounts', 'uses'=>'BankAccountController@getDatatable'));
|
||||||
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
|
Route::post('bank_accounts/bulk', 'BankAccountController@bulk');
|
||||||
@ -487,7 +489,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('INVOICE_STATUS_APPROVED', 4);
|
define('INVOICE_STATUS_APPROVED', 4);
|
||||||
define('INVOICE_STATUS_PARTIAL', 5);
|
define('INVOICE_STATUS_PARTIAL', 5);
|
||||||
define('INVOICE_STATUS_PAID', 6);
|
define('INVOICE_STATUS_PAID', 6);
|
||||||
|
|
||||||
define('PAYMENT_STATUS_PENDING', 1);
|
define('PAYMENT_STATUS_PENDING', 1);
|
||||||
define('PAYMENT_STATUS_VOIDED', 2);
|
define('PAYMENT_STATUS_VOIDED', 2);
|
||||||
define('PAYMENT_STATUS_FAILED', 3);
|
define('PAYMENT_STATUS_FAILED', 3);
|
||||||
@ -706,7 +708,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('AUTO_BILL_OPT_IN', 1);
|
define('AUTO_BILL_OPT_IN', 1);
|
||||||
define('AUTO_BILL_OPT_OUT', 2);
|
define('AUTO_BILL_OPT_OUT', 2);
|
||||||
define('AUTO_BILL_ALWAYS', 3);
|
define('AUTO_BILL_ALWAYS', 3);
|
||||||
|
|
||||||
// These must be lowercase
|
// These must be lowercase
|
||||||
define('PLAN_FREE', 'free');
|
define('PLAN_FREE', 'free');
|
||||||
define('PLAN_PRO', 'pro');
|
define('PLAN_PRO', 'pro');
|
||||||
@ -714,7 +716,7 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('PLAN_WHITE_LABEL', 'white_label');
|
define('PLAN_WHITE_LABEL', 'white_label');
|
||||||
define('PLAN_TERM_MONTHLY', 'month');
|
define('PLAN_TERM_MONTHLY', 'month');
|
||||||
define('PLAN_TERM_YEARLY', 'year');
|
define('PLAN_TERM_YEARLY', 'year');
|
||||||
|
|
||||||
// Pro
|
// Pro
|
||||||
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
define('FEATURE_CUSTOMIZE_INVOICE_DESIGN', 'customize_invoice_design');
|
||||||
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
define('FEATURE_REMOVE_CREATED_BY', 'remove_created_by');
|
||||||
@ -729,23 +731,23 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('FEATURE_API', 'api');
|
define('FEATURE_API', 'api');
|
||||||
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
|
define('FEATURE_CLIENT_PORTAL_PASSWORD', 'client_portal_password');
|
||||||
define('FEATURE_CUSTOM_URL', 'custom_url');
|
define('FEATURE_CUSTOM_URL', 'custom_url');
|
||||||
|
|
||||||
define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed
|
define('FEATURE_MORE_CLIENTS', 'more_clients'); // No trial allowed
|
||||||
|
|
||||||
// Whitelabel
|
// Whitelabel
|
||||||
define('FEATURE_CLIENT_PORTAL_CSS', 'client_portal_css');
|
define('FEATURE_CLIENT_PORTAL_CSS', 'client_portal_css');
|
||||||
define('FEATURE_WHITE_LABEL', 'feature_white_label');
|
define('FEATURE_WHITE_LABEL', 'feature_white_label');
|
||||||
|
|
||||||
// Enterprise
|
// Enterprise
|
||||||
define('FEATURE_DOCUMENTS', 'documents');
|
define('FEATURE_DOCUMENTS', 'documents');
|
||||||
|
|
||||||
// No Trial allowed
|
// No Trial allowed
|
||||||
define('FEATURE_USERS', 'users');// Grandfathered for old Pro users
|
define('FEATURE_USERS', 'users');// Grandfathered for old Pro users
|
||||||
define('FEATURE_USER_PERMISSIONS', 'user_permissions');
|
define('FEATURE_USER_PERMISSIONS', 'user_permissions');
|
||||||
|
|
||||||
// Pro users who started paying on or before this date will be able to manage users
|
// Pro users who started paying on or before this date will be able to manage users
|
||||||
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-05-15');
|
define('PRO_USERS_GRANDFATHER_DEADLINE', '2016-05-15');
|
||||||
|
|
||||||
$creditCards = [
|
$creditCards = [
|
||||||
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
|
1 => ['card' => 'images/credit_cards/Test-Visa-Icon.png', 'text' => 'Visa'],
|
||||||
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
|
2 => ['card' => 'images/credit_cards/Test-MasterCard-Icon.png', 'text' => 'Master Card'],
|
||||||
@ -801,4 +803,4 @@ if (Utils::isNinjaDev())
|
|||||||
//ini_set('memory_limit','1024M');
|
//ini_set('memory_limit','1024M');
|
||||||
//Auth::loginUsingId(1);
|
//Auth::loginUsingId(1);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -27,15 +27,15 @@ class OFX
|
|||||||
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx', 'User-Agent: httpclient'));
|
curl_setopt($c, CURLOPT_HTTPHEADER, array('Content-Type: application/x-ofx', 'User-Agent: httpclient'));
|
||||||
curl_setopt($c, CURLOPT_POSTFIELDS, $this->request);
|
curl_setopt($c, CURLOPT_POSTFIELDS, $this->request);
|
||||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
|
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
|
||||||
$this->response = curl_exec($c);
|
$this->response = curl_exec($c);
|
||||||
|
|
||||||
if (Utils::isNinjaDev()) {
|
if (Utils::isNinjaDev()) {
|
||||||
Log::info(print_r($this->response, true));
|
Log::info(print_r($this->response, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_close($c);
|
curl_close($c);
|
||||||
|
|
||||||
$tmp = explode('<OFX>', $this->response);
|
$tmp = explode('<OFX>', $this->response);
|
||||||
$this->responseHeader = $tmp[0];
|
$this->responseHeader = $tmp[0];
|
||||||
$this->responseBody = '<OFX>'.$tmp[1];
|
$this->responseBody = '<OFX>'.$tmp[1];
|
||||||
@ -48,6 +48,7 @@ class OFX
|
|||||||
|
|
||||||
return $x;
|
return $x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function closeTags($x)
|
public static function closeTags($x)
|
||||||
{
|
{
|
||||||
$x = preg_replace('/\s+/', '', $x);
|
$x = preg_replace('/\s+/', '', $x);
|
||||||
@ -233,4 +234,3 @@ class Account
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,15 +90,24 @@ class Expense extends EntityModel
|
|||||||
{
|
{
|
||||||
return round($this->amount * $this->exchange_rate, 2);
|
return round($this->amount * $this->exchange_rate, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toArray()
|
public function toArray()
|
||||||
{
|
{
|
||||||
$array = parent::toArray();
|
$array = parent::toArray();
|
||||||
|
|
||||||
if(empty($this->visible) || in_array('converted_amount', $this->visible))$array['converted_amount'] = $this->convertedAmount();
|
if(empty($this->visible) || in_array('converted_amount', $this->visible))$array['converted_amount'] = $this->convertedAmount();
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeBankId($query, $bankdId = null)
|
||||||
|
{
|
||||||
|
if ($bankdId) {
|
||||||
|
$query->whereBankId($bankId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expense::creating(function ($expense) {
|
Expense::creating(function ($expense) {
|
||||||
|
@ -34,14 +34,10 @@ class BankAccountService extends BaseService
|
|||||||
return $this->bankAccountRepo;
|
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()
|
$expenses = Expense::scope()
|
||||||
->whereBankId($bankId)
|
->bankId($bankId)
|
||||||
->where('transaction_id', '!=', '')
|
->where('transaction_id', '!=', '')
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->get(['transaction_id'])
|
->get(['transaction_id'])
|
||||||
@ -50,6 +46,16 @@ class BankAccountService extends BaseService
|
|||||||
return $val['transaction_id'];
|
return $val['transaction_id'];
|
||||||
}, $expenses));
|
}, $expenses));
|
||||||
|
|
||||||
|
return $expenses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadBankAccounts($bankId, $username, $password, $includeTransactions = true)
|
||||||
|
{
|
||||||
|
if (! $bankId || ! $username || ! $password) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expenses = $this->getExpenses();
|
||||||
$vendorMap = $this->createVendorMap();
|
$vendorMap = $this->createVendorMap();
|
||||||
$bankAccounts = BankSubaccount::scope()
|
$bankAccounts = BankSubaccount::scope()
|
||||||
->whereHas('bank_account', function ($query) use ($bankId) {
|
->whereHas('bank_account', function ($query) use ($bankId) {
|
||||||
@ -106,44 +112,60 @@ class BankAccountService extends BaseService
|
|||||||
$obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR);
|
$obj->balance = Utils::formatMoney($account->ledgerBalance, CURRENCY_DOLLAR);
|
||||||
|
|
||||||
if ($includeTransactions) {
|
if ($includeTransactions) {
|
||||||
$ofxParser = new \OfxParser\Parser();
|
$obj = $this->parseTransactions($obj, $account->response, $expenses, $vendorMap);
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $obj;
|
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)
|
private function prepareValue($value)
|
||||||
{
|
{
|
||||||
return ucwords(strtolower(trim($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()
|
private function createVendorMap()
|
||||||
{
|
{
|
||||||
$vendorMap = [];
|
$vendorMap = [];
|
||||||
@ -158,7 +180,7 @@ class BankAccountService extends BaseService
|
|||||||
return $vendorMap;
|
return $vendorMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function importExpenses($bankId, $input)
|
public function importExpenses($bankId = 0, $input)
|
||||||
{
|
{
|
||||||
$vendorMap = $this->createVendorMap();
|
$vendorMap = $this->createVendorMap();
|
||||||
$countVendors = 0;
|
$countVendors = 0;
|
||||||
|
@ -1295,7 +1295,9 @@ $LANG = array(
|
|||||||
'no_payment_method_specified' => 'No payment method specified',
|
'no_payment_method_specified' => 'No payment method specified',
|
||||||
'chart_type' => 'Chart Type',
|
'chart_type' => 'Chart Type',
|
||||||
'format' => 'Format',
|
'format' => 'Format',
|
||||||
|
'import_ofx' => 'Import OFX',
|
||||||
|
'ofx_file' => 'OFX File',
|
||||||
|
'ofx_parse_failed' => 'Failed to parse OFX file',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@section('head')
|
@section('head')
|
||||||
@parent
|
@parent
|
||||||
|
|
||||||
@include('money_script')
|
@include('money_script')
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@ -70,7 +70,7 @@
|
|||||||
<td data-bind="text: masked_account_number"></td>
|
<td data-bind="text: masked_account_number"></td>
|
||||||
<td data-bind="text: balance"></td>
|
<td data-bind="text: balance"></td>
|
||||||
<td style="text-align:center">
|
<td style="text-align:center">
|
||||||
<input type="checkbox" value="1"
|
<input type="checkbox" value="1"
|
||||||
data-bind="checked: includeAccount, attr: {name: 'bank_accounts[' + $index() + '][include]'}"/>
|
data-bind="checked: includeAccount, attr: {name: 'bank_accounts[' + $index() + '][include]'}"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -110,19 +110,19 @@
|
|||||||
<tbody data-bind="foreach: filteredTransactions">
|
<tbody data-bind="foreach: filteredTransactions">
|
||||||
<tr>
|
<tr>
|
||||||
<td style="text-align:center">
|
<td style="text-align:center">
|
||||||
<input type="checkbox" value="1"
|
<input type="checkbox" value="1"
|
||||||
data-bind="checked: includeTransaction, attr: {name: 'bank_accounts[' + $index() + '][include]'}"/>
|
data-bind="checked: includeTransaction, attr: {name: 'bank_accounts[' + $index() + '][include]'}"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control"
|
<input type="text" class="form-control"
|
||||||
data-bind="value: vendor.pretty, valueUpdate: 'afterkeydown'"/>
|
data-bind="value: vendor.pretty, valueUpdate: 'afterkeydown'"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control"
|
<input type="text" class="form-control"
|
||||||
data-bind="value: info, valueUpdate: 'afterkeydown'"/>
|
data-bind="value: info, valueUpdate: 'afterkeydown'"/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="text" class="form-control"
|
<input type="text" class="form-control"
|
||||||
data-bind="value: memo, valueUpdate: 'afterkeydown'"/>
|
data-bind="value: memo, valueUpdate: 'afterkeydown'"/>
|
||||||
</td>
|
</td>
|
||||||
<td data-bind="text: date" nowrap></td>
|
<td data-bind="text: date" nowrap></td>
|
||||||
@ -162,7 +162,7 @@
|
|||||||
<p/> <p/>
|
<p/> <p/>
|
||||||
|
|
||||||
{!! Former::actions(
|
{!! Former::actions(
|
||||||
count(Cache::get('banks')) > 0 ?
|
count(Cache::get('banks')) > 0 ?
|
||||||
Button::normal(trans('texts.cancel'))
|
Button::normal(trans('texts.cancel'))
|
||||||
->withAttributes([
|
->withAttributes([
|
||||||
'data-bind' => 'visible: !importResults()',
|
'data-bind' => 'visible: !importResults()',
|
||||||
@ -308,9 +308,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
owner: self
|
owner: self
|
||||||
})
|
})
|
||||||
|
|
||||||
self.amount.pretty = ko.computed({
|
self.amount.pretty = ko.computed({
|
||||||
@ -351,7 +351,7 @@
|
|||||||
|
|
||||||
self.filteredTransactions = ko.computed(function() {
|
self.filteredTransactions = ko.computed(function() {
|
||||||
if (!model.filter()) {
|
if (!model.filter()) {
|
||||||
return self.transactions();
|
return self.transactions();
|
||||||
} else {
|
} else {
|
||||||
return ko.utils.arrayFilter(self.transactions(), function(transaction) {
|
return ko.utils.arrayFilter(self.transactions(), function(transaction) {
|
||||||
return transaction.isMatch(model.filter());
|
return transaction.isMatch(model.filter());
|
||||||
@ -478,11 +478,16 @@
|
|||||||
return self.countExpenses() == 0;
|
return self.countExpenses() == 0;
|
||||||
}, self);
|
}, self);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.model = new ViewModel();
|
window.model = new ViewModel();
|
||||||
ko.applyBindings(model);
|
ko.applyBindings(model);
|
||||||
|
|
||||||
|
@if (!empty($transactions))
|
||||||
|
loadTransactions({!! $transactions !!});
|
||||||
|
model.setPage('import');
|
||||||
|
@endif
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@stop
|
@stop
|
||||||
|
@ -1,31 +1,35 @@
|
|||||||
@extends('header')
|
@extends('header')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
@parent
|
@parent
|
||||||
@include('accounts.nav', ['selected' => ACCOUNT_BANKS])
|
@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'))
|
->asLinkTo(URL::to('/bank_accounts/create'))
|
||||||
->withAttributes(['class' => 'pull-right'])
|
|
||||||
->appendIcon(Icon::create('plus-sign')) !!}
|
->appendIcon(Icon::create('plus-sign')) !!}
|
||||||
|
</div>
|
||||||
|
|
||||||
@include('partials.bulk_form', ['entityType' => ENTITY_BANK_ACCOUNT])
|
@include('partials.bulk_form', ['entityType' => ENTITY_BANK_ACCOUNT])
|
||||||
|
|
||||||
{!! Datatable::table()
|
{!! Datatable::table()
|
||||||
->addColumn(
|
->addColumn(
|
||||||
trans('texts.name'),
|
trans('texts.name'),
|
||||||
trans('texts.integration_type'),
|
trans('texts.integration_type'),
|
||||||
trans('texts.action'))
|
trans('texts.action'))
|
||||||
->setUrl(url('api/bank_accounts/'))
|
->setUrl(url('api/bank_accounts/'))
|
||||||
->setOptions('sPaginationType', 'bootstrap')
|
->setOptions('sPaginationType', 'bootstrap')
|
||||||
->setOptions('bFilter', false)
|
->setOptions('bFilter', false)
|
||||||
->setOptions('bAutoWidth', false)
|
->setOptions('bAutoWidth', false)
|
||||||
->setOptions('aoColumns', [[ "sWidth"=> "50%" ], [ "sWidth"=> "30%" ], ["sWidth"=> "20%"]])
|
->setOptions('aoColumns', [[ "sWidth"=> "50%" ], [ "sWidth"=> "30%" ], ["sWidth"=> "20%"]])
|
||||||
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]])
|
||||||
->render('datatable') !!}
|
->render('datatable') !!}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.onDatatableReady = actionListHandler;
|
window.onDatatableReady = actionListHandler;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@stop
|
@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