Merge branch 'feature/new-csv-import' into develop

# Conflicts:
#	app/Helpers/Csv/Importer.php
This commit is contained in:
James Cole 2016-06-23 12:15:19 +02:00
commit 9ffc0936ee
75 changed files with 693 additions and 4065 deletions

View File

@ -1,35 +0,0 @@
<?php
/**
* AccountId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class AccountId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AccountId extends BasicConverter implements ConverterInterface
{
/**
* @return Account
*/
public function convert(): Account
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
$var = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$account = $crud->find($var);
return $account;
}
}

View File

@ -1,32 +0,0 @@
<?php
/**
* Amount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Amount extends BasicConverter implements ConverterInterface
{
/**
* @return string
*/
public function convert(): string
{
if (is_numeric($this->value)) {
return strval($this->value);
}
return '0';
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* AmountComma.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class AmountComma
*
* Parses the input as the amount with a comma as decimal separator
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AmountComma extends BasicConverter implements ConverterInterface
{
/**
* @return float|int
*/
public function convert(): string
{
$value = str_replace(',', '.', strval($this->value));
if (is_numeric($value)) {
return strval($value);
}
return '0';
}
}

View File

@ -1,89 +0,0 @@
<?php
/**
* AssetAccountIban.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use Carbon\Carbon;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccountIban
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AssetAccountIban extends BasicConverter implements ConverterInterface
{
/**
* @return Account
*/
public function convert(): Account
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$account = $crud->find(intval($this->mapped[$this->index][$this->value]));
return $account;
}
if (strlen($this->value) > 0) {
$account = $this->searchOrCreate($crud);
return $account;
}
return new Account;
}
/**
* @param AccountCrudInterface $crud
*
* @return Account
*/
private function searchOrCreate(AccountCrudInterface $crud)
{
// find or create new account:
$set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->iban == $this->value) {
return $entry;
}
}
// create it if doesn't exist.
$accountData = [
'name' => $this->value,
'accountType' => 'asset',
'virtualBalance' => 0,
'virtualBalanceCurrency' => 1, // hard coded.
'active' => true,
'user' => Auth::user()->id,
'iban' => $this->value,
'accountNumber' => $this->value,
'accountRole' => null,
'openingBalance' => 0,
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => 1, // hard coded.
];
$account = $crud->store($accountData);
return $account;
}
}

View File

@ -1,65 +0,0 @@
<?php
/**
* AssetAccountName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccountName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AssetAccountName extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert(): Account
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
if (isset($this->mapped[$this->index][$this->value])) {
$account = $crud->find(intval($this->mapped[$this->index][$this->value]));
return $account;
}
$set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
/** @var Account $entry */
foreach ($set as $entry) {
if ($entry->name == $this->value) {
return $entry;
}
}
$accountData = [
'name' => $this->value,
'accountType' => 'asset',
'virtualBalance' => 0,
'virtualBalanceCurrency' => 1, // hard coded.
'active' => true,
'user' => Auth::user()->id,
'iban' => null,
'accountNumber' => $this->value,
'accountRole' => null,
'openingBalance' => 0,
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => 1, // hard coded.
];
$account = $crud->store($accountData);
return $account;
}
}

View File

@ -1,77 +0,0 @@
<?php
/**
* AssetAccountNumber.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
/**
* Class AssetAccountNumber
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class AssetAccountNumber extends BasicConverter implements ConverterInterface
{
/**
* @return Account|null
*/
public function convert(): Account
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$account = $crud->find(intval($this->mapped[$this->index][$this->value]));
return $account;
}
// if not, search for it (or create it):
$value = $this->value ?? '';
if (strlen($value) > 0) {
// find or create new account:
$set = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
/** @var Account $entry */
foreach ($set as $entry) {
$accountNumber = $entry->getMeta('accountNumber');
if ($accountNumber == $this->value) {
return $entry;
}
}
$accountData = [
'name' => $this->value,
'accountType' => 'asset',
'virtualBalance' => 0,
'virtualBalanceCurrency' => 1, // hard coded.
'active' => true,
'user' => Auth::user()->id,
'iban' => null,
'accountNumber' => $this->value,
'accountRole' => null,
'openingBalance' => 0,
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => 1, // hard coded.
];
$account = $crud->store($accountData);
return $account;
}
return null; // is this accepted?
}
}

View File

@ -1,114 +0,0 @@
<?php
/**
* BasicConverter.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class BasicConverter
*
*
* @SuppressWarnings(PHPMD.NumberOfChildren)
* @package FireflyIII\Helpers\Csv\Converter
*/
class BasicConverter
{
/** @var array */
protected $data;
/** @var string */
protected $field;
/** @var int */
protected $index;
/** @var array */
protected $mapped;
/** @var string */
protected $value;
/**
* @return array
*/
public function getData(): array
{
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return string
*/
public function getField(): string
{
return $this->field;
}
/**
* @param string $field
*/
public function setField(string $field)
{
$this->field = $field;
}
/**
* @return int
*/
public function getIndex(): int
{
return $this->index;
}
/**
* @param int $index
*/
public function setIndex(int $index)
{
$this->index = $index;
}
/**
* @return array
*/
public function getMapped(): array
{
return $this->mapped;
}
/**
* @param array $mapped
*/
public function setMapped(array $mapped)
{
$this->mapped = $mapped;
}
/**
* @return string
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $value
*/
public function setValue(string $value)
{
$this->value = $value;
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* BillId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
/**
* Class BillId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BillId extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert(): Bill
{
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
$value = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$bill = $repository->find($value);
return $bill;
}
}

View File

@ -1,47 +0,0 @@
<?php
/**
* BillName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
/**
* Class BillName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BillName extends BasicConverter implements ConverterInterface
{
/**
* @return Bill
*/
public function convert(): Bill
{
/** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class);
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
return $repository->find($this->mapped[$this->index][$this->value]);
}
$bills = $repository->getBills();
/** @var Bill $bill */
foreach ($bills as $bill) {
if ($bill->name == $this->value) {
return $bill;
}
}
return new Bill;
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* BudgetId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
/**
* Class BudgetId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BudgetId extends BasicConverter implements ConverterInterface
{
/**
* @return Budget
*/
public function convert(): Budget
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
$value = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$budget = $repository->find($value);
return $budget;
}
}

View File

@ -1,44 +0,0 @@
<?php
/**
* BudgetName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Budget;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
/**
* Class BudgetName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class BudgetName extends BasicConverter implements ConverterInterface
{
/**
* @return Budget
*/
public function convert(): Budget
{
/** @var BudgetRepositoryInterface $repository */
$repository = app(BudgetRepositoryInterface::class);
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$budget = $repository->find($this->mapped[$this->index][$this->value]);
return $budget;
}
$budget = $repository->store(['name' => $this->value, 'user' => Auth::user()->id]);
return $budget;
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* CategoryId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
/**
* Class CategoryId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CategoryId extends BasicConverter implements ConverterInterface
{
/**
* @return Category
*/
public function convert(): Category
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
$value = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$category = $repository->find($value);
return $category;
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* CategoryName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Auth;
use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
/**
* Class CategoryName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CategoryName extends BasicConverter implements ConverterInterface
{
/**
* @return Category
*/
public function convert(): Category
{
/** @var CategoryRepositoryInterface $repository */
$repository = app(CategoryRepositoryInterface::class);
// is mapped? Then it's easy!
if (isset($this->mapped[$this->index][$this->value])) {
$category = $repository->find($this->mapped[$this->index][$this->value]);
return $category;
}
$data = [
'name' => $this->value,
'user' => Auth::user()->id,
];
$category = $repository->store($data);
return $category;
}
}

View File

@ -1,52 +0,0 @@
<?php
/**
* ConverterInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Interface ConverterInterface
*
* @package FireflyIII\Helpers\Csv\Converter
*/
interface ConverterInterface
{
/**
* @return mixed
*/
public function convert();
/**
* @param array $data
*/
public function setData(array $data);
/**
* @param string $field
*
*/
public function setField(string $field);
/**
* @param int $index
*/
public function setIndex(int $index);
/**
* @param array $mapped
*/
public function setMapped(array $mapped);
/**
* @param string $value
*/
public function setValue(string $value);
}

View File

@ -1,43 +0,0 @@
<?php
/**
* CurrencyCode.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
/**
* Class CurrencyCode
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyCode extends BasicConverter implements ConverterInterface
{
/**
* @return TransactionCurrency
*/
public function convert(): TransactionCurrency
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
if (isset($this->mapped[$this->index][$this->value])) {
$currency = $repository->find(intval($this->mapped[$this->index][$this->value]));
return $currency;
}
$currency = $repository->findByCode($this->value);
return $currency;
}
}

View File

@ -1,36 +0,0 @@
<?php
/**
* CurrencyId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
/**
* Class CurrencyId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyId extends BasicConverter implements ConverterInterface
{
/**
* @return TransactionCurrency
*/
public function convert(): TransactionCurrency
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$value = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$currency = $repository->find($value);
return $currency;
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* CurrencyName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
/**
* Class CurrencyName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencyName extends BasicConverter implements ConverterInterface
{
/**
* @return TransactionCurrency
*/
public function convert(): TransactionCurrency
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
if (isset($this->mapped[$this->index][$this->value])) {
$currency = $repository->find($this->mapped[$this->index][$this->value]);
return $currency;
}
$currency = $repository->findByName($this->value);
return $currency;
}
}

View File

@ -1,43 +0,0 @@
<?php
/**
* CurrencySymbol.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
/**
* Class CurrencySymbol
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class CurrencySymbol extends BasicConverter implements ConverterInterface
{
/**
* @return TransactionCurrency
*/
public function convert(): TransactionCurrency
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
if (isset($this->mapped[$this->index][$this->value])) {
$currency = $repository->find($this->mapped[$this->index][$this->value]);
return $currency;
}
$currency = $repository->findBySymbol($this->value);
return $currency;
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* Date.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use InvalidArgumentException;
use Log;
/**
* Class Date
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Date extends BasicConverter implements ConverterInterface
{
/**
* @return Carbon
* @throws FireflyException
*/
public function convert(): Carbon
{
$format = session('csv-date-format');
try {
$date = Carbon::createFromFormat($format, $this->value);
} catch (InvalidArgumentException $e) {
Log::error('Date conversion error: ' . $e->getMessage() . '. Value was "' . $this->value . '", format was "' . $format . '".');
$message = trans('firefly.csv_date_parse_error', ['format' => $format, 'value' => $this->value]);
throw new FireflyException($message);
}
return $date;
}
}

View File

@ -1,31 +0,0 @@
<?php
/**
* Description.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class Description
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Description extends BasicConverter implements ConverterInterface
{
/**
* @return string
*/
public function convert(): string
{
$description = $this->data['description'] ?? '';
return trim($description . ' ' . $this->value);
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* INGDebetCredit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class INGDebetCredit
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class INGDebetCredit extends BasicConverter implements ConverterInterface
{
/**
* @return int
*/
public function convert(): int
{
if ($this->value === 'Af') {
return -1;
}
return 1;
}
}

View File

@ -1,28 +0,0 @@
<?php
/**
* Ignore.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class Ignore extends BasicConverter implements ConverterInterface
{
/**
* @return null
*/
public function convert()
{
return null;
}
}

View File

@ -1,64 +0,0 @@
<?php
/**
* OpposingAccountIban.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Models\Account;
/**
* Class OpposingAccountIban
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountIban extends BasicConverter implements ConverterInterface
{
/**
* If mapped, return account. Otherwise, only return the name itself.
*
* @return Account|string
*/
public function convert()
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
if (isset($this->mapped[$this->index][$this->value])) {
$account = $crud->find($this->mapped[$this->index][$this->value]);
return $account;
}
return $this->findAccount($crud);
}
/**
* @param AccountCrudInterface $crud
*
* @return Account|string
*/
private function findAccount(AccountCrudInterface $crud)
{
if (strlen($this->value) > 0) {
$set = $crud->getAccountsByType([]);
/** @var Account $account */
foreach ($set as $account) {
if ($account->iban == $this->value) {
return $account;
}
}
}
return $this->value;
}
}

View File

@ -1,35 +0,0 @@
<?php
/**
* OpposingAccountId.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class OpposingAccountId
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountId extends BasicConverter implements ConverterInterface
{
/**
* @return Account
*/
public function convert(): Account
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
$value = isset($this->mapped[$this->index][$this->value]) ? $this->mapped[$this->index][$this->value] : $this->value;
$account = $crud->find($value);
return $account;
}
}

View File

@ -1,41 +0,0 @@
<?php
/**
* OpposingAccountName.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Models\Account;
/**
* Class OpposingAccountName
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class OpposingAccountName extends BasicConverter implements ConverterInterface
{
/**
* If mapped, return account. Otherwise, only return the name itself.
*
* @return Account|string
*/
public function convert()
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
if (isset($this->mapped[$this->index][$this->value])) {
$account = $crud->find($this->mapped[$this->index][$this->value]);
return $account;
}
return $this->value;
}
}

View File

@ -1,34 +0,0 @@
<?php
/**
* RabobankDebetCredit.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
/**
* Class RabobankDebetCredit
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class RabobankDebetCredit extends BasicConverter implements ConverterInterface
{
/**
* @return int
*/
public function convert(): int
{
if ($this->value == 'D') {
return -1;
}
return 1;
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* TagsComma.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class TagsComma
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class TagsComma extends BasicConverter implements ConverterInterface
{
/**
* @return Collection
*/
public function convert(): Collection
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tags = new Collection;
$strings = explode(',', $this->value);
foreach ($strings as $string) {
$data = [
'tag' => $string,
'date' => null,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
if (strlen($string) > 0) {
$tag = $repository->store($data); // should validate first?
$tags->push($tag);
}
}
$tags = $tags->merge($this->data['tags']);
return $tags;
}
}

View File

@ -1,54 +0,0 @@
<?php
/**
* TagsSpace.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Converter;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Support\Collection;
/**
* Class TagsSpace
*
* @package FireflyIII\Helpers\Csv\Converter
*/
class TagsSpace extends BasicConverter implements ConverterInterface
{
/**
* @return Collection
*/
public function convert(): Collection
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$tags = new Collection;
$strings = explode(' ', $this->value);
foreach ($strings as $string) {
$data = [
'tag' => $string,
'date' => null,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
if (strlen($string) > 0) {
$tag = $repository->store($data); // should validate first?
$tags->push($tag);
}
}
$tags = $tags->merge($this->data['tags']);
return $tags;
}
}

View File

@ -1,336 +0,0 @@
<?php
/**
* Data.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv;
use Crypt;
use League\Csv\Reader;
use Session;
use Storage;
/**
* Class Data
*
* @package FireflyIII\Helpers\Csv
*/
class Data
{
/** @var string */
protected $csvFileContent = '';
/** @var string */
protected $csvFileLocation = '';
/** @var string */
protected $dateFormat = '';
/** @var string */
protected $delimiter = '';
/** @var bool */
protected $hasHeaders;
/** @var int */
protected $importAccount = 0;
/** @var array */
protected $map = [];
/** @var array */
protected $mapped = [];
/** @var Reader */
protected $reader;
/** @var array */
protected $roles = [];
/** @var array */
protected $specifix = [];
/**
*/
public function __construct()
{
$this->sessionHasHeaders();
$this->sessionDateFormat();
$this->sessionCsvFileLocation();
$this->sessionMap();
$this->sessionRoles();
$this->sessionMapped();
$this->sessionSpecifix();
$this->sessionImportAccount();
$this->sessionDelimiter();
}
/**
*
* @return string
*/
public function getCsvFileContent(): string
{
return $this->csvFileContent ?? '';
}
/**
*
* @param string $csvFileContent
*/
public function setCsvFileContent(string $csvFileContent)
{
$this->csvFileContent = $csvFileContent;
}
/**
* FIXxME may return null
*
* @return string
*/
public function getCsvFileLocation(): string
{
return $this->csvFileLocation;
}
/**
*
* @param string $csvFileLocation
*/
public function setCsvFileLocation(string $csvFileLocation)
{
Session::put('csv-file', $csvFileLocation);
$this->csvFileLocation = $csvFileLocation;
}
/**
* FIXxME may return null
*
* @return string
*/
public function getDateFormat(): string
{
return $this->dateFormat;
}
/**
*
* @param string $dateFormat
*/
public function setDateFormat(string $dateFormat)
{
Session::put('csv-date-format', $dateFormat);
$this->dateFormat = $dateFormat;
}
/**
* FIXxME may return null
*
* @return string
*/
public function getDelimiter(): string
{
return $this->delimiter;
}
/**
*
* @param string $delimiter
*/
public function setDelimiter(string $delimiter)
{
Session::put('csv-delimiter', $delimiter);
$this->delimiter = $delimiter;
}
/**
*
* @return array
*/
public function getMap(): array
{
return $this->map;
}
/**
*
* @param array $map
*/
public function setMap(array $map)
{
Session::put('csv-map', $map);
$this->map = $map;
}
/**
*
* @return array
*/
public function getMapped(): array
{
return $this->mapped;
}
/**
*
* @param array $mapped
*/
public function setMapped(array $mapped)
{
Session::put('csv-mapped', $mapped);
$this->mapped = $mapped;
}
/**
*
* @return Reader
*/
public function getReader(): Reader
{
if (!is_null($this->csvFileContent) && strlen($this->csvFileContent) === 0) {
$this->loadCsvFile();
}
if (is_null($this->reader)) {
$this->reader = Reader::createFromString($this->getCsvFileContent());
$this->reader->setDelimiter($this->delimiter);
}
return $this->reader;
}
/**
*
* @return array
*/
public function getRoles(): array
{
return $this->roles;
}
/**
*
* @param array $roles
*/
public function setRoles(array $roles)
{
Session::put('csv-roles', $roles);
$this->roles = $roles;
}
/**
*
* @return array
*/
public function getSpecifix(): array
{
return is_array($this->specifix) ? $this->specifix : [];
}
/**
*
* @param array $specifix
*/
public function setSpecifix(array $specifix)
{
Session::put('csv-specifix', $specifix);
$this->specifix = $specifix;
}
/**
*
* @return bool
*/
public function hasHeaders(): bool
{
return $this->hasHeaders;
}
/**
*
* @param bool $hasHeaders
*/
public function setHasHeaders(bool $hasHeaders)
{
Session::put('csv-has-headers', $hasHeaders);
$this->hasHeaders = $hasHeaders;
}
/**
*
* @param int $importAccount
*/
public function setImportAccount(int $importAccount)
{
Session::put('csv-import-account', $importAccount);
$this->importAccount = $importAccount;
}
protected function loadCsvFile()
{
$file = $this->getCsvFileLocation();
$disk = Storage::disk('upload');
$content = $disk->get($file);
$contentDecrypted = Crypt::decrypt($content);
$this->setCsvFileContent($contentDecrypted);
}
protected function sessionCsvFileLocation()
{
if (Session::has('csv-file')) {
$this->csvFileLocation = (string)session('csv-file');
}
}
protected function sessionDateFormat()
{
if (Session::has('csv-date-format')) {
$this->dateFormat = (string)session('csv-date-format');
}
}
protected function sessionDelimiter()
{
if (Session::has('csv-delimiter')) {
$this->delimiter = session('csv-delimiter');
}
}
protected function sessionHasHeaders()
{
if (Session::has('csv-has-headers')) {
$this->hasHeaders = (bool)session('csv-has-headers');
}
}
protected function sessionImportAccount()
{
if (Session::has('csv-import-account')) {
$this->importAccount = intval(session('csv-import-account'));
}
}
protected function sessionMap()
{
if (Session::has('csv-map')) {
$this->map = (array)session('csv-map');
}
}
protected function sessionMapped()
{
if (Session::has('csv-mapped')) {
$this->mapped = (array)session('csv-mapped');
}
}
protected function sessionRoles()
{
if (Session::has('csv-roles')) {
$this->roles = (array)session('csv-roles');
}
}
protected function sessionSpecifix()
{
if (Session::has('csv-specifix')) {
$this->specifix = (array)session('csv-specifix');
}
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* AnyAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Account;
/**
* Class AnyAccount
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class AnyAccount implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->accounts()->with('accountType')->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$list = [];
/** @var Account $account */
foreach ($result as $account) {
$list[$account->id] = $account->name . ' (' . $account->accountType->type . ')';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,54 +0,0 @@
<?php
/**
* AssetAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Account;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* Class AssetAccount
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class AssetAccount implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->accounts()->with(
['accountmeta' => function (HasMany $query) {
$query->where('name', 'accountRole');
}]
)->accountTypeIn(['Default account', 'Asset account'])->orderBy('accounts.name', 'ASC')->get(['accounts.*']);
$list = [];
/** @var Account $account */
foreach ($result as $account) {
$name = $account->name;
$iban = $account->iban ?? '';
if (strlen($iban) > 0) {
$name .= ' (' . $account->iban . ')';
}
$list[$account->id] = $name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* Bill.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Bill as BillModel;
/**
* Class Bill
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Bill implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->bills()->get(['bills.*']);
$list = [];
/** @var BillModel $bill */
foreach ($result as $bill) {
$list[$bill->id] = $bill->name . ' [' . $bill->match . ']';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* Budget.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Budget as BudgetModel;
/**
* Class Budget
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Budget implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->budgets()->get(['budgets.*']);
$list = [];
/** @var BudgetModel $budget */
foreach ($result as $budget) {
$list[$budget->id] = $budget->name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,42 +0,0 @@
<?php
/**
* Category.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Category as CategoryModel;
/**
* Class Category
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Category implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->categories()->get(['categories.*']);
$list = [];
/** @var CategoryModel $category */
foreach ($result as $category) {
$list[$category->id] = $category->name;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,24 +0,0 @@
<?php
/**
* MapperInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
/**
* Interface MapperInterface
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
interface MapperInterface
{
/**
* @return array
*/
public function getMap(): array;
}

View File

@ -1,42 +0,0 @@
<?php
/**
* Tag.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use Auth;
use FireflyIII\Models\Tag as TagModel;
/**
* Class Tag
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class Tag implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$result = Auth::user()->budgets()->get(['tags.*']);
$list = [];
/** @var TagModel $tag */
foreach ($result as $tag) {
$list[$tag->id] = $tag->tag;
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,40 +0,0 @@
<?php
/**
* TransactionCurrency.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Mapper;
use FireflyIII\Models\TransactionCurrency as TC;
/**
* Class TransactionCurrency
*
* @package FireflyIII\Helpers\Csv\Mapper
*/
class TransactionCurrency implements MapperInterface
{
/**
* @return array
*/
public function getMap(): array
{
$currencies = TC::get();
$list = [];
foreach ($currencies as $currency) {
$list[$currency->id] = $currency->name . ' (' . $currency->code . ')';
}
asort($list);
$list = [0 => trans('firefly.csv_do_not_map')] + $list;
return $list;
}
}

View File

@ -1,44 +0,0 @@
<?php
/**
* Amount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Amount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Amount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
$amount = $this->data['amount'] ?? '0';
$modifier = strval($this->data['amount-modifier']);
$this->data['amount'] = bcmul($amount, $modifier);
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@ -1,274 +0,0 @@
<?php
/**
* AssetAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
use Auth;
use Carbon\Carbon;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Validator;
/**
* Class AssetAccount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class AssetAccount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
$result = $this->checkIdNameObject(); // has object in ID or Name?
if (!is_null($result)) {
return $result;
}
// no object? maybe asset-account-iban is a string and we can find the matching account.
$result = $this->checkIbanString();
if (!is_null($result)) {
return $result;
}
// no object still? maybe we can find the account by name.
$result = $this->checkNameString();
if (!is_null($result)) {
return $result;
}
// still nothing? Perhaps the account number can lead us to an account:
$result = $this->checkAccountNumberString();
if (!is_null($result)) {
return $result;
}
return null;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return array|null
*/
protected function checkAccountNumberString()
{
$accountNumber = $this->data['asset-account-number'] ?? null;
if ($accountNumber instanceof Account) { // fourth: try to find account based on name, if any.
$this->data['asset-account-object'] = $accountNumber;
return $this->data;
}
if (is_string($accountNumber)) { // it's an actual account number
$this->data['asset-account-object'] = $this->parseAccountNumberString();
return $this->data;
}
return null;
}
/**
* @return array|null
*/
protected function checkIbanString()
{
$iban = $this->data['asset-account-iban'] ?? '';
$rules = ['iban' => 'iban'];
$check = ['iban' => $iban];
$validator = Validator::make($check, $rules);
if (!$validator->fails()) {
$this->data['asset-account-object'] = $this->parseIbanString();
return $this->data;
}
return null;
}
/**
* @return array
*/
protected function checkIdNameObject()
{
$accountId = $this->data['asset-account-id'] ?? null;
$accountIban = $this->data['asset-account-iban'] ?? null;
$accountNumber = $this->data['asset-account-number'] ?? null;
if ($accountId instanceof Account) { // first priority. try to find the account based on ID, if any
$this->data['asset-account-object'] = $accountId;
return $this->data;
}
if ($accountIban instanceof Account) { // second: try to find the account based on IBAN, if any.
$this->data['asset-account-object'] = $accountIban;
return $this->data;
}
if ($accountNumber instanceof Account) { // second: try to find the account based on account number, if any.
$this->data['asset-account-object'] = $accountNumber;
return $this->data;
}
return null;
}
/**
* @return array|null
*/
protected function checkNameString()
{
$accountName = $this->data['asset-account-name'] ?? null;
if ($accountName instanceof Account) { // third: try to find account based on name, if any.
$this->data['asset-account-object'] = $accountName;
return $this->data;
}
if (is_string($accountName)) {
$this->data['asset-account-object'] = $this->parseNameString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function createAccount()
{
$accountType = $this->getAccountType();
$name = $this->data['asset-account-name'] ?? '';
$iban = $this->data['asset-account-iban'] ?? '';
// create if not exists: // See issue #180
$name = strlen($name) > 0 ? $name : $iban;
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $name,
'iban' => $iban,
'active' => true,
]
);
return $account;
}
/**
*
* @return AccountType
*/
protected function getAccountType()
{
return AccountType::where('type', 'Asset account')->first();
}
/**
* @return Account|null
*/
protected function parseIbanString()
{
// create by name and/or iban.
$iban = $this->data['asset-account-iban'] ?? '';
$accounts = Auth::user()->accounts()->get();
foreach ($accounts as $entry) {
if ($iban !== '' && $entry->iban === $iban) {
return $entry;
}
}
$account = $this->createAccount();
return $account;
}
/**
* @return Account|null
*/
protected function parseNameString()
{
$accountType = $this->getAccountType();
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
foreach ($accounts as $entry) {
if ($entry->name == $this->data['asset-account-name']) {
return $entry;
}
}
// create if not exists:
// See issue #180
$account = Account::firstOrCreateEncrypted(
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $this->data['asset-account-name'],
'iban' => '',
'active' => true,
]
);
return $account;
}
/**
* @return Account|null
*/
private function parseAccountNumberString()
{
/** @var AccountCrudInterface $crud */
$crud = app(AccountCrudInterface::class);
$accountNumber = $this->data['asset-account-number'] ?? '';
$accountType = $this->getAccountType();
$accounts = Auth::user()->accounts()->with(['accountmeta'])->where('account_type_id', $accountType->id)->get();
/** @var Account $entry */
foreach ($accounts as $entry) {
$metaFieldValue = $entry->getMeta('accountNumber');
if ($metaFieldValue === $accountNumber && $metaFieldValue !== '') {
return $entry;
}
}
// create new if not exists and return that one:
$accountData = [
'name' => $accountNumber,
'accountType' => 'asset',
'virtualBalance' => 0,
'virtualBalanceCurrency' => 1, // hard coded.
'active' => true,
'user' => Auth::user()->id,
'iban' => null,
'accountNumber' => $accountNumber,
'accountRole' => null,
'openingBalance' => 0,
'openingBalanceDate' => new Carbon,
'openingBalanceCurrency' => 1, // hard coded.
];
$account = $crud->store($accountData);
return $account;
}
}

View File

@ -1,45 +0,0 @@
<?php
/**
* Bill.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Bill
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Bill implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
// get bill id.
if (!is_null($this->data['bill']) && !is_null($this->data['bill']->id)) {
$this->data['bill-id'] = $this->data['bill']->id;
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* Currency.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
use FireflyIII\Models\TransactionCurrency;
use Preferences;
/**
* Class Currency
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Currency implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
// fix currency
if (is_null($this->data['currency'])) {
$currencyPreference = Preferences::get('currencyPreference', env('DEFAULT_CURRENCY', 'EUR'));
$this->data['currency'] = TransactionCurrency::whereCode($currencyPreference->data)->first();
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@ -1,47 +0,0 @@
<?php
/**
* Description.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Class Description
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class Description implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
$description = $this->data['description'] ?? '';
$this->data['description'] = trim($description);
if (strlen($this->data['description']) == 0) {
$this->data['description'] = trans('firefly.csv_empty_description');
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
}

View File

@ -1,210 +0,0 @@
<?php
/**
* OpposingAccount.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
use Auth;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use Validator;
/**
* Class OpposingAccount
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
class OpposingAccount implements PostProcessorInterface
{
/** @var array */
protected $data;
/**
* @return array
*/
public function process(): array
{
// three values:
// opposing-account-id, opposing-account-iban, opposing-account-name
$result = $this->checkIdNameObject();
if (!is_null($result)) {
return $result;
}
$result = $this->checkIbanString();
if (!is_null($result)) {
return $result;
}
$result = $this->checkNameString();
if (!is_null($result)) {
return $result;
}
return null;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @return array|null
*/
protected function checkIbanString()
{
$rules = ['iban' => 'iban'];
$iban = $this->data['opposing-account-iban'];
$check = ['iban' => $iban];
$validator = Validator::make($check, $rules);
if (is_string($iban) && strlen($iban) > 0 && !$validator->fails()) {
$this->data['opposing-account-object'] = $this->parseIbanString();
return $this->data;
}
return null;
}
/**
* @return array
*/
protected function checkIdNameObject()
{
if ($this->data['opposing-account-id'] instanceof Account) { // first priority. try to find the account based on ID, if any
$this->data['opposing-account-object'] = $this->data['opposing-account-id'];
return $this->data;
}
if ($this->data['opposing-account-iban'] instanceof Account) { // second: try to find the account based on IBAN, if any.
$this->data['opposing-account-object'] = $this->data['opposing-account-iban'];
return $this->data;
}
return null;
}
/**
* @return array|null
*/
protected function checkNameString()
{
if ($this->data['opposing-account-name'] instanceof Account) { // third: try to find account based on name, if any.
$this->data['opposing-account-object'] = $this->data['opposing-account-name'];
return $this->data;
}
if (is_string($this->data['opposing-account-name'])) {
$this->data['opposing-account-object'] = $this->parseNameString();
return $this->data;
}
return null;
}
/**
* @return Account|null
*/
protected function createAccount()
{
$accountType = $this->getAccountType();
// create if not exists:
$name = is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) > 0 ? $this->data['opposing-account-name']
: $this->data['opposing-account-iban'];
$account = Account::firstOrCreateEncrypted( // See issue #180
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $name,
'iban' => $this->data['opposing-account-iban'],
'active' => true,
]
);
return $account;
}
/**
*
* @return AccountType
*/
protected function getAccountType()
{
// opposing account type:
if ($this->data['amount'] < 0) {
// create expense account:
return AccountType::where('type', 'Expense account')->first();
}
// create revenue account:
return AccountType::where('type', 'Revenue account')->first();
}
/**
* @return Account|null
*/
protected function parseIbanString()
{
// create by name and/or iban.
$accounts = Auth::user()->accounts()->get();
foreach ($accounts as $entry) {
if ($entry->iban == $this->data['opposing-account-iban']) {
return $entry;
}
}
$account = $this->createAccount();
return $account;
}
/**
* @return Account|null
*/
protected function parseNameString()
{
$accountType = $this->getAccountType();
$accounts = Auth::user()->accounts()->where('account_type_id', $accountType->id)->get();
foreach ($accounts as $entry) {
if ($entry->name == $this->data['opposing-account-name']) {
return $entry;
}
}
// create if not exists:
$account = Account::firstOrCreateEncrypted( // See issue #180
[
'user_id' => Auth::user()->id,
'account_type_id' => $accountType->id,
'name' => $this->data['opposing-account-name'],
'iban' => '',
'active' => true,
]
);
return $account;
}
}

View File

@ -1,31 +0,0 @@
<?php
/**
* PostProcessorInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\PostProcessing;
/**
* Interface PostProcessorInterface
*
* @package FireflyIII\Helpers\Csv\PostProcessing
*/
interface PostProcessorInterface
{
/**
* @return array
*/
public function process(): array;
/**
* @param array $data
*/
public function setData(array $data);
}

View File

@ -1,226 +0,0 @@
<?php
/**
* AbnAmroDescription.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Parses the description from txt files for ABN AMRO bank accounts.
*
* Based on the logic as described in the following Gist:
* https://gist.github.com/vDorst/68d555a6a90f62fec004
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class AbnAmroDescription extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* AbnAmroDescription constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array
*/
public function fix(): array
{
// Try to parse the description in known formats.
$parsed = $this->parseSepaDescription() || $this->parseTRTPDescription() || $this->parseGEABEADescription() || $this->parseABNAMRODescription();
// If the description could not be parsed, specify an unknown opposing
// account, as an opposing account is required
if (!$parsed) {
$this->data['opposing-account-name'] = trans('firefly.unknown');
}
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow(array $row)
{
$this->row = $row;
}
/**
* Parses the current description with costs from ABN AMRO itself
*
* @return bool true if the description is GEA/BEA-format, false otherwise
*/
protected function parseABNAMRODescription()
{
// See if the current description is formatted in ABN AMRO format
if (preg_match('/ABN AMRO.{24} (.*)/', $this->data['description'], $matches)) {
$this->data['opposing-account-name'] = 'ABN AMRO';
$this->data['description'] = $matches[1];
return true;
}
return false;
}
/**
* Parses the current description in GEA/BEA format
*
* @return bool true if the description is GEA/BEAformat, false otherwise
*/
protected function parseGEABEADescription()
{
// See if the current description is formatted in GEA/BEA format
if (preg_match('/([BG]EA) +(NR:[a-zA-Z:0-9]+) +([0-9.\/]+) +([^,]*)/', $this->data['description'], $matches)) {
// description and opposing account will be the same.
$this->data['opposing-account-name'] = $matches[4];
$this->data['description'] = $matches[4];
if ($matches[1] == 'GEA') {
$this->data['description'] = 'GEA ' . $matches[4];
}
return true;
}
return false;
}
/**
* Parses the current description in SEPA format
*
* @return bool true if the description is SEPA format, false otherwise
*/
protected function parseSepaDescription()
{
// See if the current description is formatted as a SEPA plain description
if (preg_match('/^SEPA(.{28})/', $this->data['description'], $matches)) {
$type = $matches[1];
$reference = '';
$name = '';
$newDescription = '';
// SEPA plain descriptions contain several key-value pairs, split by a colon
preg_match_all('/([A-Za-z]+(?=:\s)):\s([A-Za-z 0-9._#-]+(?=\s|$))/', $this->data['description'], $matches, PREG_SET_ORDER);
if (is_array($matches)) {
foreach ($matches as $match) {
$key = $match[1];
$value = trim($match[2]);
switch (strtoupper($key)) {
case 'OMSCHRIJVING':
$newDescription = $value;
break;
case 'NAAM':
$this->data['opposing-account-name'] = $value;
$name = $value;
break;
case 'KENMERK':
$reference = $value;
break;
case 'IBAN':
$this->data['opposing-account-iban'] = $value;
break;
default:
// Ignore the rest
}
}
}
// Set a new description for the current transaction. If none was given
// set the description to type, name and reference
$this->data['description'] = $newDescription;
if (strlen($newDescription) === 0) {
$this->data['description'] = sprintf('%s - %s (%s)', $type, $name, $reference);
}
return true;
}
return false;
}
/**
* Parses the current description in TRTP format
*
* @return bool true if the description is TRTP format, false otherwise
*/
protected function parseTRTPDescription()
{
// See if the current description is formatted in TRTP format
if (preg_match_all('!\/([A-Z]{3,4})\/([^/]*)!', $this->data['description'], $matches, PREG_SET_ORDER)) {
$type = '';
$name = '';
$reference = '';
$newDescription = '';
// Search for properties specified in the TRTP format. If no description
// is provided, use the type, name and reference as new description
if (is_array($matches)) {
foreach ($matches as $match) {
$key = $match[1];
$value = trim($match[2]);
switch (strtoupper($key)) {
case 'NAME':
$this->data['opposing-account-name'] = $name = $value;
break;
case 'REMI':
$newDescription = $value;
break;
case 'IBAN':
$this->data['opposing-account-iban'] = $value;
break;
case 'EREF':
$reference = $value;
break;
case 'TRTP':
$type = $value;
break;
default:
// Ignore the rest
}
}
// Set a new description for the current transaction. If none was given
// set the description to type, name and reference
$this->data['description'] = $newDescription;
if (strlen($newDescription) === 0) {
$this->data['description'] = sprintf('%s - %s (%s)', $type, $name, $reference);
}
}
return true;
}
return false;
}
}

View File

@ -1,60 +0,0 @@
<?php
/**
* Dummy.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Class Dummy
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class Dummy extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* Dummy constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array
*/
public function fix(): array
{
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow(array $row)
{
$this->row = $row;
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* RabobankDescription.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Class RabobankDescription
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class RabobankDescription extends Specifix implements SpecifixInterface
{
/** @var array */
protected $data;
/** @var array */
protected $row;
/**
* RabobankDescription constructor.
*/
public function __construct()
{
$this->setProcessorType(self::POST_PROCESSOR);
}
/**
* @return array
*/
public function fix(): array
{
$this->rabobankFixEmptyOpposing();
return $this->data;
}
/**
* @param array $data
*/
public function setData(array $data)
{
$this->data = $data;
}
/**
* @param array $row
*/
public function setRow(array $row)
{
$this->row = $row;
}
/**
* Fixes Rabobank specific thing.
*/
protected function rabobankFixEmptyOpposing()
{
if (is_string($this->data['opposing-account-name']) && strlen($this->data['opposing-account-name']) == 0) {
$this->data['opposing-account-name'] = $this->row[10];
$this->data['description'] = trim(str_replace($this->row[10], '', $this->data['description']));
}
}
}

View File

@ -1,46 +0,0 @@
<?php
/**
* Specifix.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Class Specifix
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
class Specifix
{
/** @var int */
protected $processorType;
/**
* @return int
*/
public function getProcessorType(): int
{
return $this->processorType;
}
/**
* @param int $processorType
*
* @return $this
*/
public function setProcessorType(int $processorType)
{
$this->processorType = $processorType;
return $this;
}
}

View File

@ -1,49 +0,0 @@
<?php
/**
* SpecifixInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv\Specifix;
/**
* Interface SpecifixInterface
*
* @package FireflyIII\Helpers\Csv\Specifix
*/
interface SpecifixInterface
{
const PRE_PROCESSOR = 1;
const POST_PROCESSOR = 2;
/**
* Implement bank and locale related fixes.
*/
public function fix();
/**
* @return int
*/
public function getProcessorType(): int;
/**
* @param array $data
*/
public function setData(array $data);
/**
* @param int $processorType
*
* @return $this
*/
public function setProcessorType(int $processorType);
/**
* @param array $row
*/
public function setRow(array $row);
}

View File

@ -1,202 +0,0 @@
<?php
/**
* Wizard.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv;
use Auth;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Mapper\MapperInterface;
use League\Csv\Reader;
use Log;
use ReflectionException;
use Session;
use SplFileObject;
use Storage;
/**
* Class Wizard
*
* @package FireflyIII\Helpers\Csv
*/
class Wizard implements WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues(Reader $reader, array $map, bool $hasHeaders): array
{
$values = [];
/*
* Loop over the CSV and collect mappable data:
*/
$keys = array_keys($map);
foreach ($reader as $index => $row) {
if ($this->useRow($hasHeaders, $index)) {
// collect all map values
foreach ($keys as $column) {
$values[$column][] = $row[$column];
}
}
}
/*
* Make each one unique.
*/
$values = $this->uniqueRecursive($values);
return $values;
}
/**
* @param array $roles
* @param array $map
*
* @return array
*/
public function processSelectedMapping(array $roles, array $map): array
{
$configRoles = config('csv.roles');
$maps = [];
$keys = array_keys($map);
foreach ($keys as $index) {
if (isset($roles[$index])) {
$name = $roles[$index];
if ($configRoles[$name]['mappable']) {
$maps[$index] = $name;
}
}
}
return $maps;
}
/**
* @param array $input
*
* @return array
*/
public function processSelectedRoles(array $input): array
{
$roles = [];
/*
* Store all rows for each column:
*/
if (is_array($input)) {
foreach ($input as $index => $role) {
if ($role != '_ignore') {
$roles[$index] = $role;
}
}
}
return $roles;
}
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields): bool
{
foreach ($fields as $field) {
if (!Session::has($field)) {
Log::error('Session is missing field: ' . $field);
return false;
}
}
return true;
}
/**
* @param array $map
*
* @return array
* @throws FireflyException
*/
public function showOptions(array $map): array
{
$options = [];
foreach ($map as $index => $columnRole) {
$mapper = config('csv.roles.' . $columnRole . '.mapper');
if (is_null($mapper)) {
throw new FireflyException('Cannot map field of type "' . $columnRole . '".');
}
$class = 'FireflyIII\Helpers\Csv\Mapper\\' . $mapper;
try {
/** @var MapperInterface $mapObject */
$mapObject = app($class);
} catch (ReflectionException $e) {
throw new FireflyException('Column "' . $columnRole . '" cannot be mapped because mapper class ' . $mapper . ' does not exist.');
}
$set = $mapObject->getMap();
$options[$index] = $set;
}
return $options;
}
/**
* @param string $path
*
* @return string
*/
public function storeCsvFile(string $path): string
{
$time = str_replace(' ', '-', microtime());
$fileName = 'csv-upload-' . Auth::user()->id . '-' . $time . '.csv.encrypted';
$disk = Storage::disk('upload');
$file = new SplFileObject($path, 'r');
$content = $file->fread($file->getSize());
$contentEncrypted = Crypt::encrypt($content);
$disk->put($fileName, $contentEncrypted);
return $fileName;
}
/**
* @param array $array
*
* @return array
*/
protected function uniqueRecursive(array $array)
{
foreach ($array as $column => $found) {
$array[$column] = array_unique($found);
}
return $array;
}
/**
* @param bool $hasHeaders
* @param int $index
*
* @return bool
*/
protected function useRow(bool $hasHeaders, int $index)
{
return ($hasHeaders && $index > 1) || !$hasHeaders;
}
}

View File

@ -1,68 +0,0 @@
<?php
/**
* WizardInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Helpers\Csv;
use League\Csv\Reader;
/**
* Interface WizardInterface
*
* @package FireflyIII\Helpers\Csv
*/
interface WizardInterface
{
/**
* @param Reader $reader
* @param array $map
* @param bool $hasHeaders
*
* @return array
*/
public function getMappableValues(Reader $reader, array $map, bool $hasHeaders): array;
/**
* @param array $roles
* @param array $map
*
* @return array
*/
public function processSelectedMapping(array $roles, array $map): array;
/**
* @param array $input
*
* @return array
*/
public function processSelectedRoles(array $input): array;
/**
* @param array $fields
*
* @return bool
*/
public function sessionHasValues(array $fields): bool;
/**
* @param array $map
*
* @return array
*/
public function showOptions(array $map): array;
/**
* @param string $path
*
* @return string
*/
public function storeCsvFile(string $path): string;
}

View File

@ -1,444 +0,0 @@
<?php
/**
* CsvController.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use ExpandedForm;
use FireflyIII\Crud\Account\AccountCrudInterface;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Csv\Data;
use FireflyIII\Helpers\Csv\Importer;
use FireflyIII\Helpers\Csv\WizardInterface;
use FireflyIII\Models\AccountType;
use Illuminate\Http\Request;
use Input;
use Log;
use Preferences;
use Session;
use View;
/**
* Class CsvController
*
* @package FireflyIII\Http\Controllers
*/
class CsvController extends Controller
{
/** @var Data */
protected $data;
/** @var WizardInterface */
protected $wizard;
/**
*
*/
public function __construct()
{
parent::__construct();
View::share('title', trans('firefly.csv'));
View::share('mainTitleIcon', 'fa-file-text-o');
if (config('firefly.csv_import_enabled') === false) {
throw new FireflyException('CSV Import is not enabled.');
}
$this->wizard = app(WizardInterface::class);
$this->data = app(Data::class);
}
/**
* Define column roles and mapping.
*
* STEP THREE
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function columnRoles()
{
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-import-account', 'csv-specifix', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Log::error('Could not recover upload.');
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
$subTitle = trans('firefly.csv_define_column_roles');
$firstRow = $this->data->getReader()->fetchOne();
$count = count($firstRow);
$headers = [];
$example = $this->data->getReader()->fetchOne(1);
$availableRoles = [];
$roles = $this->data->getRoles();
$map = $this->data->getMap();
for ($i = 1; $i <= $count; $i++) {
$headers[] = trans('firefly.csv_column') . ' #' . $i;
}
if ($this->data->hasHeaders()) {
$headers = $firstRow;
}
$keys = array_keys(config('csv.roles'));
foreach ($keys as $name) {
$availableRoles[$name] = trans('firefly.csv_column_' . $name);
}
asort($availableRoles);
return view('csv.column-roles', compact('availableRoles', 'map', 'roles', 'headers', 'example', 'subTitle'));
}
/**
* Optional download of mapping.
*
* STEP FOUR THREE-A
*
* @return \Illuminate\Http\RedirectResponse|string
*/
public function downloadConfig()
{
$fields = ['csv-date-format', 'csv-has-headers', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
$data = [
'date-format' => session('csv-date-format'),
'has-headers' => session('csv-has-headers'),
];
if (Session::has('csv-map')) {
$data['map'] = session('csv-map');
}
if (Session::has('csv-roles')) {
$data['roles'] = session('csv-roles');
}
if (Session::has('csv-mapped')) {
$data['mapped'] = session('csv-mapped');
}
if (Session::has('csv-specifix')) {
$data['specifix'] = session('csv-specifix');
}
$result = json_encode($data, JSON_PRETTY_PRINT);
$name = sprintf('"%s"', addcslashes('csv-configuration-' . date('Y-m-d') . '.json', '"\\'));
return response($result, 200)
->header('Content-disposition', 'attachment; filename=' . $name)
->header('Content-Type', 'application/json')
->header('Content-Description', 'File Transfer')
->header('Connection', 'Keep-Alive')
->header('Expires', '0')
->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->header('Pragma', 'public')
->header('Content-Length', strlen($result));
}
/**
* @return \Illuminate\View\View
*/
public function downloadConfigPage()
{
$fields = ['csv-date-format', 'csv-has-headers', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
$subTitle = trans('firefly.csv_download_config_title');
return view('csv.download-config', compact('subTitle'));
}
/**
* This method shows the initial upload form.
*
* STEP ONE
*
* @param AccountCrudInterface $crud
*
* @return \Illuminate\View\View
*/
public function index(AccountCrudInterface $crud)
{
$subTitle = trans('firefly.csv_import');
Session::forget('csv-date-format');
Session::forget('csv-has-headers');
Session::forget('csv-file');
Session::forget('csv-import-account');
Session::forget('csv-map');
Session::forget('csv-roles');
Session::forget('csv-mapped');
Session::forget('csv-specifix');
Session::forget('csv-delimiter');
// get list of supported specifix
$specifix = [];
foreach (config('csv.specifix') as $entry) {
$specifix[$entry] = trans('firefly.csv_specifix_' . $entry);
}
// get a list of delimiters:
$delimiters = [
',' => trans('form.csv_comma'),
';' => trans('form.csv_semicolon'),
'tab' => trans('form.csv_tab'),
];
// get a list of asset accounts:
$accounts = ExpandedForm::makeSelectList($crud->getAccountsByType([AccountType::ASSET, AccountType::DEFAULT]));
// can actually upload?
$uploadPossible = is_writable(storage_path('upload'));
$path = storage_path('upload');
return view('csv.index', compact('subTitle', 'uploadPossible', 'path', 'specifix', 'accounts', 'delimiters'));
}
/**
* Parse the file.
*
* STEP FOUR
*
* @return \Illuminate\Http\RedirectResponse
*/
public function initialParse()
{
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
// process given roles and mapping:
$inputMap = Input::get('map') ?? [];
$inputRoles = Input::get('role') ?? [];
$roles = $this->wizard->processSelectedRoles($inputRoles);
$maps = $this->wizard->processSelectedMapping($roles, $inputMap);
Session::put('csv-map', $maps);
Session::put('csv-roles', $roles);
// Go back when no roles defined:
if (count($roles) === 0) {
Session::flash('warning', strval(trans('firefly.must_select_roles')));
return redirect(route('csv.column-roles'));
}
/*
* Continue with map specification when necessary.
*/
if (count($maps) > 0) {
return redirect(route('csv.map'));
}
/*
* Or simply start processing.
*/
// proceed to download config
return redirect(route('csv.download-config-page'));
}
/**
*
* Map first if necessary,
*
* STEP FIVE.
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
* @throws FireflyException
*/
public function map()
{
// Make sure all fields we need are accounted for.
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
/*
* The "options" array contains all options the user has
* per column, where the key represents the column.
*
* For each key there is an array which in turn represents
* all the options available: grouped by ID.
*
* options[column index] = [
* field id => field identifier.
* ]
*/
$options = $this->wizard->showOptions($this->data->getMap());
// After these values are prepped, read the actual CSV file
$reader = $this->data->getReader();
$map = $this->data->getMap();
$hasHeaders = $this->data->hasHeaders();
$values = $this->wizard->getMappableValues($reader, $map, $hasHeaders);
$map = $this->data->getMap();
$mapped = $this->data->getMapped();
$subTitle = trans('firefly.csv_map_values');
return view('csv.map', compact('map', 'options', 'values', 'mapped', 'subTitle'));
}
/**
*
* Finally actually process the CSV file.
*
* STEP SEVEN
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function process()
{
/*
* Make sure all fields we need are accounted for.
*/
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-mapped', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
/** @var Importer $importer */
$importer = app(Importer::class);
$importer->setData($this->data);
$importer->run();
$rows = $importer->getRows();
$errors = $importer->getErrors();
$imported = $importer->getImported();
$journals = $importer->getJournals();
Preferences::mark();
$subTitle = trans('firefly.csv_process_title');
return view('csv.process', compact('rows', 'errors', 'imported', 'subTitle', 'journals'));
}
/**
* Store the mapping the user has made. This is
*
* STEP SIX
*
*
* @return \Illuminate\Http\RedirectResponse
*/
public function saveMapping()
{
/*
* Make sure all fields we need are accounted for.
*/
$fields = ['csv-file', 'csv-date-format', 'csv-has-headers', 'csv-map', 'csv-roles', 'csv-delimiter'];
if (!$this->wizard->sessionHasValues($fields)) {
Session::flash('warning', strval(trans('firefly.could_not_recover')));
return redirect(route('csv.index'));
}
// save mapping to session.
$mapped = [];
if (!is_array(Input::get('mapping'))) {
Session::flash('warning', strval(trans('firefly.invalid_mapping')));
return redirect(route('csv.map'));
}
foreach (Input::get('mapping') as $index => $data) {
$mapped[$index] = [];
foreach ($data as $value => $mapping) {
if (intval($mapping) !== 0) {
$mapped[$index][$value] = $mapping;
}
}
}
Session::put('csv-mapped', $mapped);
// proceed to process.
return redirect(route('csv.download-config-page'));
}
/**
*
* This method processes the file, puts it away somewhere safe
* and sends you onwards.
*
* STEP TWO
*
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse
*/
public function upload(Request $request)
{
if (!$request->hasFile('csv')) {
Session::flash('warning', strval(trans('firefly.no_file_uploaded')));
return redirect(route('csv.index'));
}
$path = $this->wizard->storeCsvFile($request->file('csv')->getRealPath());
$settings = [];
$settings['date-format'] = Input::get('date_format');
$settings['has-headers'] = intval(Input::get('has_headers')) === 1;
$settings['specifix'] = is_array(Input::get('specifix')) ? Input::get('specifix') : [];
$settings['import-account'] = intval(Input::get('csv_import_account'));
$settings['delimiter'] = Input::get('csv_delimiter', ',');
// A tab character cannot be used itself as option value in HTML
// See http://stackoverflow.com/questions/6064135/valid-characters-in-option-value
if ($settings['delimiter'] == 'tab') {
$settings['delimiter'] = "\t";
}
$settings['map'] = [];
$settings['mapped'] = [];
$settings['roles'] = [];
if ($request->hasFile('csv_config')) { // Process config file if present.
$size = $request->file('csv_config')->getSize();
$data = $request->file('csv_config')->openFile()->fread($size);
$json = json_decode($data, true);
if (is_array($json)) {
$settings = array_merge($settings, $json);
}
}
$this->data->setCsvFileLocation($path);
$this->data->setDateFormat($settings['date-format']);
$this->data->setHasHeaders($settings['has-headers']);
$this->data->setMap($settings['map']);
$this->data->setMapped($settings['mapped']);
$this->data->setRoles($settings['roles']);
$this->data->setSpecifix($settings['specifix']);
$this->data->setImportAccount($settings['import-account']);
$this->data->setDelimiter($settings['delimiter']);
return redirect(route('csv.column-roles'));
}
}

View File

@ -2,7 +2,14 @@
namespace FireflyIII\Http\Controllers;
use Crypt;
use FireflyIII\Http\Requests;
use FireflyIII\Http\Requests\ImportUploadRequest;
use FireflyIII\Import\Importer\ImporterInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use SplFileObject;
use Storage;
use View;
/**
@ -22,6 +29,26 @@ class ImportController extends Controller
View::share('title', trans('firefly.import_data'));
}
/**
* @param ImportJob $job
*
* @return View
*/
public function configure(ImportJob $job)
{
// create proper importer (depends on job)
$type = $job->file_type;
/** @var ImporterInterface $importer */
$importer = app('FireflyIII\Import\Importer\\' . ucfirst($type) . 'Importer');
$importer->setJob($job);
$importer->configure();
$data = $importer->getConfigurationData();
return view('import.' . $type . '.configure', compact('data', 'job'));
}
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
@ -38,4 +65,27 @@ class ImportController extends Controller
return view('import.index', compact('subTitle', 'subTitleIcon', 'importFileTypes', 'defaultImportType'));
}
/**
* @param ImportUploadRequest $request
* @param ImportJobRepositoryInterface $repository
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function upload(ImportUploadRequest $request, ImportJobRepositoryInterface $repository)
{
// create import job:
$type = $request->get('import_file_type');
$job = $repository->create($type);
$upload = $request->files->get('import_file');
$newName = $job->key . '.upload';
$uploaded = new SplFileObject($upload->getRealPath());
$content = $uploaded->fread($uploaded->getSize());
$contentEncrypted = Crypt::encrypt($content);
$disk = Storage::disk('upload');
$disk->put($newName, $contentEncrypted);
return redirect(route('import.configure', [$job->key]));
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* ImportUploadRequest.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Http\Requests;
use Auth;
/**
* Class ImportUploadRequest
*
*
* @package FireflyIII\Http\Requests
*/
class ImportUploadRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$types = array_keys(config('firefly.import_formats'));
return [
'import_file' => 'required|file',
'import_file_type' => 'required|in:' . join(',', $types),
];
}
}

View File

@ -269,44 +269,6 @@ Breadcrumbs::register(
}
);
/**
* CSV CONTROLLER
*/
Breadcrumbs::register(
'csv.index', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('home');
$breadcrumbs->push(trans('firefly.csv_index_title'), route('csv.index'));
}
);
Breadcrumbs::register(
'csv.column-roles', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_define_column_roles'), route('csv.column-roles'));
}
);
Breadcrumbs::register(
'csv.map', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_map_values'), route('csv.map'));
}
);
Breadcrumbs::register(
'csv.download-config-page', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_download_config'), route('csv.download-config-page'));
}
);
Breadcrumbs::register(
'csv.process', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('csv.index');
$breadcrumbs->push(trans('firefly.csv_process_title'), route('csv.process'));
}
);
/**
* CURRENCIES
*/

View File

@ -157,21 +157,6 @@ Route::group(
Route::post('/categories/update/{category}', ['uses' => 'CategoryController@update', 'as' => 'categories.update']);
Route::post('/categories/destroy/{category}', ['uses' => 'CategoryController@destroy', 'as' => 'categories.destroy']);
/**
* CSV controller
*/
Route::get('/csv', ['uses' => 'CsvController@index', 'as' => 'csv.index']);
Route::post('/csv/upload', ['uses' => 'CsvController@upload', 'as' => 'csv.upload']);
Route::get('/csv/column_roles', ['uses' => 'CsvController@columnRoles', 'as' => 'csv.column-roles']);
Route::post('/csv/initial_parse', ['uses' => 'CsvController@initialParse', 'as' => 'csv.initial_parse']);
Route::get('/csv/map', ['uses' => 'CsvController@map', 'as' => 'csv.map']);
Route::get('/csv/download-config', ['uses' => 'CsvController@downloadConfig', 'as' => 'csv.download-config']);
Route::get('/csv/download', ['uses' => 'CsvController@downloadConfigPage', 'as' => 'csv.download-config-page']);
Route::post('/csv/save_mapping', ['uses' => 'CsvController@saveMapping', 'as' => 'csv.save_mapping']);
Route::get('/csv/process', ['uses' => 'CsvController@process', 'as' => 'csv.process']);
/**
* Currency Controller
*/
@ -238,8 +223,9 @@ Route::group(
/**
* IMPORT CONTROLLER
*/
Route::get('/import', ['uses' => 'ImportController@index', 'as' => 'import.index']);
Route::post('/import/upload', ['uses' => 'ImportController@upload', 'as' => 'import.upload']);
Route::get('/import', ['uses' => 'ImportController@index','as' => 'import.index']);
Route::post('/import/upload', ['uses' => 'ImportController@upload','as' => 'import.upload']);
Route::get('/import/configure/{importJob}', ['uses' => 'ImportController@configure','as' => 'import.configure']);
/**
* Help Controller

View File

@ -0,0 +1,83 @@
<?php
/**
* CsvImporter.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Importer;
use ExpandedForm;
use FireflyIII\Import\Role\Map;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\ImportJob;
/**
* Class CsvImporter
*
* @package FireflyIII\Import\Importer
*/
class CsvImporter implements ImporterInterface
{
/** @var ImportJob */
public $job;
/**
* @return bool
*/
public function configure(): bool
{
// need to do nothing, for now.
return true;
}
/**
* @return array
*/
public function getConfigurationData(): array
{
$crud = app('FireflyIII\Crud\Account\AccountCrudInterface');
$accounts = $crud->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET]);
$delimiters = [
',' => trans('form.csv_comma'),
';' => trans('form.csv_semicolon'),
'tab' => trans('form.csv_tab'),
];
$data = [
'accounts' => ExpandedForm::makeSelectList($accounts),
'specifix' => [],
'delimiters' => $delimiters,
'upload_path' => storage_path('upload'),
'is_upload_possible' => is_writable(storage_path('upload')),
];
return $data;
}
/**
* Returns a Map thing used to allow the user to
* define roles for each entry.
*
* @return Map
*/
public function prepareRoles(): Map
{
return 'do not work';
exit;
}
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job)
{
$this->job = $job;
}
}

View File

@ -0,0 +1,52 @@
<?php
/**
* ImporterInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Importer;
use FireflyIII\Import\Role\Map;
use FireflyIII\Models\ImportJob;
/**
* Interface ImporterInterface
*
* @package FireflyIII\Import\Importer
*/
interface ImporterInterface
{
/**
* After uploading, and after setJob(), prepare anything that is
* necessary for the configure() line.
*
* @return bool
*/
public function configure(): bool;
/**
* Returns any data necessary to do the configuration.
*
* @return array
*/
public function getConfigurationData(): array;
/**
* Returns a Map thing used to allow the user to
* define roles for each entry.
*
* @return Map
*/
public function prepareRoles(): Map;
/**
* @param ImportJob $job
*
*/
public function setJob(ImportJob $job);
}

18
app/Import/Role/Map.php Normal file
View File

@ -0,0 +1,18 @@
<?php
/**
* Map.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Role;
class Map
{
}

18
app/Import/Role/Role.php Normal file
View File

@ -0,0 +1,18 @@
<?php
/**
* Role.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Import\Role;
class Role
{
}

75
app/Models/ImportJob.php Normal file
View File

@ -0,0 +1,75 @@
<?php
/**
* ImportJob.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Models;
use Auth;
use Illuminate\Database\Eloquent\Model;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ImportJob
*
* @package FireflyIII\Models
* @property integer $id
* @property \Carbon\Carbon $created_at
* @property \Carbon\Carbon $updated_at
* @property integer $user_id
* @property string $key
* @property string $file_type
* @property string $status
* @property-read \FireflyIII\User $user
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereCreatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUpdatedAt($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereUserId($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereKey($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereFileType($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\ImportJob whereStatus($value)
* @mixin \Eloquent
*/
class ImportJob extends Model
{
/**
* @param $value
*
* @return mixed
* @throws NotFoundHttpException
*/
public static function routeBinder($value)
{
if (Auth::check()) {
$model = self::where('key', $value)->where('user_id', Auth::user()->id)->first();
if (!is_null($model)) {
return $model;
}
}
throw new NotFoundHttpException;
}
/**
* @param $status
*/
public function change($status)
{
$this->status = $status;
$this->save();
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo('FireflyIII\User');
}
}

View File

@ -31,6 +31,27 @@ class ExportJobServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->exportJob();
$this->importJob();
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
//
}
/**
*
*/
private function exportJob()
{
$this->app->bind(
'FireflyIII\Repositories\ExportJob\ExportJobRepositoryInterface',
function (Application $app, array $arguments) {
@ -46,13 +67,20 @@ class ExportJobServiceProvider extends ServiceProvider
);
}
/**
* Register the application services.
*
* @return void
*/
public function register()
private function importJob()
{
//
$this->app->bind(
'FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface',
function (Application $app, array $arguments) {
if (!isset($arguments[0]) && $app->auth->check()) {
return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', [$app->auth->user()]);
}
if (!isset($arguments[0]) && !$app->auth->check()) {
throw new FireflyException('There is no user present.');
}
return app('FireflyIII\Repositories\ImportJob\ImportJobRepository', $arguments);
}
);
}
}

View File

@ -87,7 +87,6 @@ class FireflyServiceProvider extends ServiceProvider
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
$this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository');
$this->app->bind('FireflyIII\Helpers\Csv\WizardInterface', 'FireflyIII\Helpers\Csv\Wizard');
$this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper');
$this->app->bind(
'FireflyIII\Generator\Chart\Account\AccountChartGeneratorInterface', 'FireflyIII\Generator\Chart\Account\ChartJsAccountChartGenerator'

View File

@ -0,0 +1,81 @@
<?php
/**
* ImportJobRepository.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Repositories\ImportJob;
use FireflyIII\Models\ImportJob;
use FireflyIII\User;
use Illuminate\Support\Str;
/**
* Class ImportJobRepository
*
* @package FireflyIII\Repositories\ImportJob
*/
class ImportJobRepository implements ImportJobRepositoryInterface
{
/** @var User */
private $user;
/**
* ExportJobRepository constructor.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* @param string $fileType
*
* @return ImportJob
*/
public function create(string $fileType): ImportJob
{
$count = 0;
while ($count < 30) {
$key = Str::random(12);
$existing = $this->findByKey($key);
if (is_null($existing->id)) {
$importJob = new ImportJob;
$importJob->user()->associate($this->user);
$importJob->file_type = $fileType;
$importJob->key = Str::random(12);
$importJob->status = 'import_status_never_started';
$importJob->save();
// breaks the loop:
return $importJob;
}
$count++;
}
return new ImportJob;
}
/**
* @param string $key
*
* @return ImportJob
*/
public function findByKey(string $key): ImportJob
{
$result = $this->user->importJobs()->where('key', $key)->first();
if (is_null($result)) {
return new ImportJob;
}
return $result;
}
}

View File

@ -0,0 +1,36 @@
<?php
/**
* ImportJobRepositoryInterface.php
* Copyright (C) 2016 thegrumpydictator@gmail.com
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
declare(strict_types = 1);
namespace FireflyIII\Repositories\ImportJob;
use FireflyIII\Models\ImportJob;
/**
* Interface ImportJobRepositoryInterface
*
* @package FireflyIII\Repositories\ImportJob
*/
interface ImportJobRepositoryInterface
{
/**
* @param string $fileType
*
* @return ImportJob
*/
public function create(string $fileType): ImportJob;
/**
* @param string $key
*
* @return ImportJob
*/
public function findByKey(string $key): ImportJob;
}

View File

@ -54,6 +54,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlocked($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\User whereBlockedCode($value)
* @mixin \Eloquent
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\ImportJob[] $importjobs
*/
class User extends Authenticatable
{
@ -145,6 +146,14 @@ class User extends Authenticatable
return $this->hasMany('FireflyIII\Models\ExportJob');
}
/**
* @return HasMany
*/
public function importjobs(): HasMany
{
return $this->hasMany('FireflyIII\Models\ImportJob');
}
/**
* Checks if the user has a role by its name.
*

View File

@ -180,8 +180,8 @@ return [
// own stuff:
// Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
// Barryvdh\Debugbar\ServiceProvider::class,
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
Barryvdh\Debugbar\ServiceProvider::class,
'DaveJamesMiller\Breadcrumbs\ServiceProvider',
'TwigBridge\ServiceProvider',
'PragmaRX\Google2FA\Vendor\Laravel\ServiceProvider',

View File

@ -126,6 +126,7 @@ return [
'rule' => 'FireflyIII\Models\Rule',
'ruleGroup' => 'FireflyIII\Models\RuleGroup',
'jobKey' => 'FireflyIII\Models\ExportJob',
'importJob' => 'FireflyIII\Models\ImportJob',
// lists
'accountList' => 'FireflyIII\Support\Binder\AccountList',
'budgetList' => 'FireflyIII\Support\Binder\BudgetList',

View File

@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV391
*/
class ChangesForV391 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('import_jobs');
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// new table "import_jobs"
Schema::create(
'import_jobs', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('user_id')->unsigned();
$table->string('key', 12)->unique();
$table->string('file_type', 12);
$table->string('status', 45);
// connect rule groups to users
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
}
);
}
}

View File

@ -326,96 +326,11 @@ return [
'title_transfer' => 'Transfers',
'title_transfers' => 'Transfers',
// csv import:
'csv_import' => 'Import CSV file',
'csv' => 'CSV',
'csv_index_title' => 'Upload and import a CSV file',
'csv_define_column_roles' => 'Define column roles',
'csv_map_values' => 'Map found values to existing values',
'csv_download_config' => 'Download CSV configuration file.',
'csv_index_text' => 'This form allows you to import a CSV file with transactions into Firefly. It is based on the excellent CSV importer made by the folks at <a href="https://www.atlassian.com/">Atlassian</a>. Simply upload your CSV file and follow the instructions. If you would like to learn more, please click on the <i class="fa fa-question-circle"></i> button at the top of this page.',
'csv_index_beta_warning' => 'This tool is very much in beta. Please proceed with caution',
'csv_header_help' => 'Check this box when your CSV file\'s first row consists of column names, not actual data',
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> indicates. The default value will parse dates that look like this: :dateExample.',
'csv_csv_file_help' => 'Select the CSV file here. You can only upload one file at a time',
'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
'csv_upload_button' => 'Start importing CSV',
'csv_column_roles_title' => 'Define column roles',
'csv_column_roles_text' => 'Firefly does not know what each column means. You need to indicate what every column is. Please check out the example data if you\'re not sure yourself. Click on the question mark (top right of the page) to learn what each column means. If you want to map imported data onto existing data in Firefly, use the checkbox. The next step will show you what this button does.',
'csv_column_roles_table' => 'Column roles',
'csv_column' => 'CSV column',
'csv_column_name' => 'CSV column name',
'csv_column_example' => 'Column example data',
'csv_column_role' => 'Column contains?',
'csv_do_map_value' => 'Map value?',
'csv_continue' => 'Continue to the next step',
'csv_go_back' => 'Go back to the previous step',
'csv_map_title' => 'Map found values to existing values',
'csv_map_text' => 'This page allows you to map the values from the CSV file to existing entries in your database. This ensures that accounts and other things won\'t be created twice.',
'csv_field_value' => 'Field value from CSV',
'csv_field_mapped_to' => 'Must be mapped to...',
'csv_do_not_map' => 'Do not map this value',
'csv_download_config_title' => 'Download CSV configuration',
'csv_download_config_text' => 'Everything you\'ve just set up can be downloaded as a configuration file. Click the button to do so.',
'csv_more_information_text' => 'If the import fails, you can use this configuration file so you don\'t have to start all over again. But, if the import succeeds, it will be easier to upload similar CSV files.',
'csv_do_download_config' => 'Download configuration file.',
'csv_empty_description' => '(empty description)',
'csv_upload_form' => 'CSV upload form',
'csv_index_unsupported_warning' => 'The CSV importer is yet incapable of doing the following:',
'csv_unsupported_map' => 'The importer cannot map the column ":columnRole" to existing values in the database.',
'csv_unsupported_value' => 'The importer does not know how to handle values in columns marked as ":columnRole".',
'csv_cannot_store_value' => 'The importer has not reserved space for columns marked ":columnRole" and will be incapable of processing them.',
'csv_process_title' => 'CSV import finished!',
'csv_process_text' => 'The CSV importer has finished and has processed :rows rows',
'csv_row' => 'Row',
'csv_import_with_errors' => 'There was one error.|There were :errors errors.',
'csv_error_see_logs' => 'Check the log files to see details.',
'csv_process_new_entries' => 'Firefly has created :imported new transaction(s).',
'csv_start_over' => 'Import again',
'csv_to_index' => 'Back home',
'csv_upload_not_writeable' => 'Cannot write to the path mentioned here. Cannot upload',
'csv_column__ignore' => '(ignore this column)',
'csv_column_account-iban' => 'Asset account (IBAN)',
'csv_column_account-id' => 'Asset account ID (matching Firefly)',
'csv_column_account-name' => 'Asset account (name)',
'csv_column_amount' => 'Amount',
'csv_column_amount-comma-separated' => 'Amount (comma as decimal separator)',
'csv_column_bill-id' => 'Bill ID (matching Firefly)',
'csv_column_bill-name' => 'Bill name',
'csv_column_budget-id' => 'Budget ID (matching Firefly)',
'csv_column_budget-name' => 'Budget name',
'csv_column_category-id' => 'Category ID (matching Firefly)',
'csv_column_category-name' => 'Category name',
'csv_column_currency-code' => 'Currency code (ISO 4217)',
'csv_column_currency-id' => 'Currency ID (matching Firefly)',
'csv_column_currency-name' => 'Currency name (matching Firefly)',
'csv_column_currency-symbol' => 'Currency symbol (matching Firefly)',
'csv_column_date-rent' => 'Rent calculation date',
'csv_column_date-transaction' => 'Date',
'csv_column_description' => 'Description',
'csv_column_opposing-iban' => 'Opposing account (IBAN)',
'csv_column_opposing-id' => 'Opposing account ID (matching Firefly)',
'csv_column_opposing-name' => 'Opposing account (name)',
'csv_column_rabo-debet-credit' => 'Rabobank specific debet/credit indicator',
'csv_column_ing-debet-credit' => 'ING specific debet/credit indicator',
'csv_column_sepa-ct-id' => 'SEPA Credit Transfer end-to-end ID',
'csv_column_sepa-ct-op' => 'SEPA Credit Transfer opposing account',
'csv_column_sepa-db' => 'SEPA Direct Debet',
'csv_column_tags-comma' => 'Tags (comma separated)',
'csv_column_tags-space' => 'Tags (space separated)',
'csv_column_account-number' => 'Asset account (account number)',
'csv_column_opposing-number' => 'Opposing account (account number)',
'csv_specifix_RabobankDescription' => 'Select this when you\'re importing Rabobank CSV export files.',
'csv_specifix_AbnAmroDescription' => 'Select this when you\'re importing ABN AMRO CSV export files.',
'csv_specifix_Dummy' => 'Checking this has no effect whatsoever.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.',
'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.',
'csv_date_parse_error' => 'Could not parse a valid date from ":value", using the format ":format". Are you sure your CSV is correct?',
'could_not_recover' => 'Could not continue from the previous step. Your progress has been lost :(. The log files will tell you what happened.',
'must_select_roles' => 'You must select some roles for your file content, or the process cannot continue.',
'invalid_mapping' => 'You have submitted an invalid mapping. The process cannot continue.',
'no_file_uploaded' => 'It seems you did not upload a file.',
// import routine
'import_data' => 'Import data',
'import' => 'Import',
'import_intro_text' => 'Some intro here',
'import_file_help' => 'Select your file',
// create new stuff:
@ -813,29 +728,39 @@ return [
'split_table_intro_withdrawal' => 'Split your withdrawal in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_withdrawal' => 'Store splitted withdrawal',
'update_splitted_withdrawal' => 'Update splitted withdrawal',
'split_title_deposit' => 'Split your new deposit',
'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.',
'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.',
'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.',
'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_deposit' => 'Store splitted deposit',
'split_title_transfer' => 'Split your new transfer',
'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.',
'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.',
'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.',
'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_transfer' => 'Store splitted transfer',
'add_another_split' => 'Add another split',
'split-transactions' => 'Split transactions',
'split-new-transaction' => 'Split a new transaction',
'do_split' => 'Do a split',
'split_this_withdrawal' => 'Split this withdrawal',
'split_this_deposit' => 'Split this deposit',
'split_this_transfer' => 'Split this transfer',
'split_title_deposit' => 'Split your new deposit',
'split_intro_one_deposit' => 'Firefly supports the "splitting" of a deposit.',
'split_intro_two_deposit' => 'It means that the amount of money you\'ve earned is divided between several source revenue accounts or categories.',
'split_intro_three_deposit' => 'For example: you could split your :total salary so you get :split_one as your base salary and :split_two as a reimbursment for expenses made.',
'split_table_intro_deposit' => 'Split your deposit in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_deposit' => 'Store splitted deposit',
'split_title_transfer' => 'Split your new transfer',
'split_intro_one_transfer' => 'Firefly supports the "splitting" of a transfer.',
'split_intro_two_transfer' => 'It means that the amount of money you\'re moving is divided between several categories or piggy banks.',
'split_intro_three_transfer' => 'For example: you could split your :total move so you get :split_one in one piggy bank and :split_two in another.',
'split_table_intro_transfer' => 'Split your transfer in as many things as you want. By default the transaction will not split, there is just one entry. Add as many splits as you want to, below. Remember that you should not deviate from your total amount. If you do, Firefly will warn you but not correct you.',
'store_splitted_transfer' => 'Store splitted transfer',
'add_another_split' => 'Add another split',
'split-transactions' => 'Split transactions',
'split-new-transaction' => 'Split a new transaction',
'do_split' => 'Do a split',
'split_this_withdrawal' => 'Split this withdrawal',
'split_this_deposit' => 'Split this deposit',
'split_this_transfer' => 'Split this transfer',
// import
'import_file_type_csv' => 'CSV (comma separated values)',
'import_file_type_help' => 'Select the type of file you will upload',
'import_start' => 'Start the import',
'import_csv_configure_title' => 'Configure your import',
'import_csv_configure_intro' => 'There are some options for your CSV import.',
'import_csv_configure_form' => 'Form',
'csv_header_help' => 'Check this if the first row of your CSV file are the column titles',
'csv_date_help' => 'Date time format in your CSV. Follow the format like <a href="https://secure.php.net/manual/en/datetime.createfromformat.php#refsect1-datetime.createfromformat-parameters">this page</a> indicates. The default value will parse dates that look like this: :dateExample.',
'csv_delimiter_help' => 'Choose the field delimiter that is used in your input file. If not sure, comma is the safest option.',
'csv_csv_config_file_help' => 'Select your CSV import configuration here. If you do not know what this is, ignore it. It will be explained later.',
'csv_import_account_help' => 'If your CSV file does NOT contain information about your asset account(s), use this dropdown to select to which account the transactions in the CSV belong to.',
'csv_upload_not_writeable' => 'The grey box contains a file path. It should be writeable. Please make sure it is.',
];

View File

@ -71,13 +71,9 @@ return [
'code' => 'Code',
'iban' => 'IBAN',
'accountNumber' => 'Account number',
'csv' => 'CSV file',
'has_headers' => 'Headers',
'date_format' => 'Date format',
'csv_config' => 'CSV import configuration',
'specifix' => 'Bank- or file specific fixes',
'csv_import_account' => 'Default import account',
'csv_delimiter' => 'CSV field delimiter',
'attachments[]' => 'Attachments',
'store_new_withdrawal' => 'Store new withdrawal',
'store_new_deposit' => 'Store new deposit',
@ -102,9 +98,6 @@ return [
'include_config' => 'Include configuration file',
'include_old_uploads' => 'Include imported data',
'accounts' => 'Export transactions from these accounts',
'csv_comma' => 'A comma (,)',
'csv_semicolon' => 'A semicolon (;)',
'csv_tab' => 'A tab (invisible)',
'delete_account' => 'Delete account ":name"',
'delete_bill' => 'Delete bill ":name"',
'delete_budget' => 'Delete budget ":name"',
@ -137,4 +130,17 @@ return [
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will spared deletion.',
// import
'import_file' => 'Import file',
'import_file_type' => 'Import file type',
'csv_comma' => 'A comma (,)',
'csv_semicolon' => 'A semicolon (;)',
'csv_tab' => 'A tab (invisible)',
'csv_delimiter' => 'CSV field delimiter',
'csv_import_account' => 'Default import account',
'csv_config' => 'CSV import configuration',
];

View File

@ -55,11 +55,6 @@ return [
'categories-show' => 'categories.show',
'categories-show-date' => 'categories.show.date',
'categories-noCategory' => 'categories.noCategory',
'csv-index' => 'csv.index',
'csv-column-roles' => 'csv.column-roles',
'csv-map' => 'csv.map',
'csv-download-config-page' => 'csv.download-config-page',
'csv-process' => 'csv.process',
'currency-index' => 'currency.index',
'currency-create' => 'currency.create',
'currency-edit' => 'currency.edit',

View File

@ -0,0 +1,85 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, job) }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'import_csv_configure_title'|_ }}</h3>
</div>
<div class="box-body">
<p>
{{ 'import_csv_configure_intro'|_ }}
</p>
</div>
</div>
</div>
</div>
<form class="form-horizontal" action="#" method="post" enctype="multipart/form-data">
<input type="hidden" name="_token" value="{{ csrf_token() }}"/>
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'import_csv_configure_form'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.checkbox('has_headers',1,null,{helpText: 'csv_header_help'|_}) }}
{{ ExpandedForm.text('date_format','Ymd',{helpText: trans('firefly.csv_date_help', {dateExample: phpdate('Ymd')}) }) }}
{{ ExpandedForm.select('csv_delimiter', data.delimiters, 0, {helpText: 'csv_delimiter_help'|_} ) }}
{{ ExpandedForm.file('csv_config',{helpText: 'csv_csv_config_file_help'|_}) }}
{{ ExpandedForm.select('csv_import_account', data.accounts, 0, {helpText: 'csv_import_account_help'|_} ) }}
{{ ExpandedForm.multiCheckbox('specifix', data.specifix) }}
{% if not data.is_upload_possible %}
<div class="form-group" id="csv_holder">
<div class="col-sm-4">
&nbsp;
</div>
<div class="col-sm-8">
<pre>{{ data.upload_path }}</pre>
<p class="text-danger">
{{ 'csv_upload_not_writeable'|_ }}
</p>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% if data.is_upload_possible %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-body">
<button type="submit" class="pull-right btn btn-success">
{{ 'csv_upload_button'|_ }}
</button>
</div>
</div>
</div>
</div>
{% endif %}
</form>
{% endblock %}

View File

@ -25,7 +25,7 @@
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="col-lg-6 col-md-8 col-sm-12 col-xs-12">
{{ ExpandedForm.file('import_file',{helpText: 'import_file_help'|_}) }}
{{ ExpandedForm.file('import_file', {helpText: 'import_file_help'|_}) }}
{{ ExpandedForm.select('import_file_type', importFileTypes, defaultImportType, {'helpText' : 'import_file_type_help'|_}) }}

View File

@ -110,8 +110,7 @@
</li>
<!-- import and export -->
<li class="{{ activeRoutePartial('export') }} {{ activeRoutePartial('csv') }} treeview">
<li class="{{ activeRoutePartial('export') }} {{ activeRoutePartial('import') }} treeview">
<a href="#">
<i class="fa fa-arrows-alt fa-fw"></i>
<span>
@ -120,12 +119,9 @@
<i class="fa fa-angle-left pull-right"></i>
</a>
<ul class="treeview-menu">
{% if Config.get('firefly.csv_import_enabled') %}
<li class="{{ activeRoutePartial('csv') }}">
<a href="{{ route('csv.index') }}"><i class="fa fa-file-text-o fa-fw"></i> {{ 'csv_import'|_ }}</a>
</li>
{% endif %}
<li class="{{ activeRoutePartial('import') }}">
<a href="{{ route('import.index') }}"><i class="fa fa-archive fa-fw"></i> {{ 'import_data'|_ }}</a>
</li>
<li class="{{ activeRoutePartial('export') }}">
<a href="{{ route('export.index') }}"><i class="fa fa-file-archive-o fa-fw"></i> {{ 'export_data'|_ }}</a>
</li>