Extend rule set for import.

This commit is contained in:
James Cole 2016-08-11 18:44:11 +02:00
parent 186b704509
commit 0aaf9a6fda
32 changed files with 349 additions and 72 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ _development
.env.local
result.html
test-import.sh
test-import-report.txt

View File

@ -19,6 +19,7 @@ use FireflyIII\Import\Logging\CommandHandler;
use FireflyIII\Models\ImportJob;
use Illuminate\Console\Command;
use Log;
use Monolog\Handler\StreamHandler;
/**
* Class Import
@ -80,6 +81,7 @@ class Import extends Command
// intercept logging by importer.
$monolog = Log::getMonolog();
$handler = new CommandHandler($this);
$monolog->pushHandler($handler);
// create import entries

View File

@ -158,10 +158,12 @@ class AccountCrud implements AccountCrudInterface
/** @var Account $account */
foreach ($accounts as $account) {
if ($account->name === $name) {
Log::debug('Account name is an exact match. ', ['db' => $account->name, 'source' => $name,'id' => $account->id]);
Log::debug('Account name is an exact match. ', ['db' => $account->name, 'source' => $name, 'id' => $account->id]);
return $account;
}
}
Log::warning('Found nothing in findByName()', ['name' => $name, 'types' => $types]);
Log::debug('Found nothing in findByName()', ['name' => $name, 'types' => $types]);
return new Account;
}
@ -233,7 +235,7 @@ class AccountCrud implements AccountCrudInterface
public function store(array $data): Account
{
$newAccount = $this->storeAccount($data);
if (!is_null($newAccount)) {
if (!is_null($newAccount->id)) {
$this->storeMetadata($newAccount, $data);
}
@ -327,8 +329,8 @@ class AccountCrud implements AccountCrudInterface
];
$existingAccount = Account::firstOrNullEncrypted($searchData);
if (!$existingAccount) {
Log::error('Account create error: ' . $newAccount->getErrors()->toJson());
abort(500);
Log::error('Account create error', $newAccount->getErrors()->toArray());
return new Account;
}
$newAccount = $existingAccount;

View File

@ -66,9 +66,17 @@ class AssetAccountIban extends BasicConverter implements ConverterInterface
$account = $repository->store(
['name' => 'Account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0,
'active' => true]
['name' => 'Asset account with IBAN ' . $value, 'iban' => $value, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0,
'active' => true, 'openingBalance' => 0]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::info('Could not store new asset account by IBAN', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
return $account;

View File

@ -68,6 +68,14 @@ class AssetAccountName extends BasicConverter implements ConverterInterface
['name' => $value, 'iban' => null, 'openingBalance' => 0, 'user' => $this->user->id, 'accountType' => 'asset', 'virtualBalance' => 0,
'active' => true]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::info('Could not store new asset account by name', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
Log::debug('Created new asset account ', ['name' => $account->name, 'id' => $account->id]);

View File

@ -32,7 +32,7 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface
public function convert($value)
{
$value = trim($value);
Log::debug('Going to convert using AssetAccountName', ['value' => $value]);
Log::debug('Going to convert using AssetAccountNumber', ['value' => $value]);
if (strlen($value) === 0) {
return new Account;
@ -77,6 +77,14 @@ class AssetAccountNumber extends BasicConverter implements ConverterInterface
'accountType' => 'asset',
'virtualBalance' => 0, 'accountNumber' => $value, 'active' => true]
);
if (is_null($account->id)) {
$this->setCertainty(0);
Log::notice('Could not store new asset account by account number', $account->getErrors()->toArray());
return new Account;
}
$this->setCertainty(100);
return $account;

View File

@ -63,6 +63,7 @@ class BillId extends BasicConverter implements ConverterInterface
}
// should not really happen. If the ID does not match FF, what is FF supposed to do?
Log::info(sprintf('Could not find bill with ID %d. Will return NULL', $value));
$this->setCertainty(0);

View File

@ -76,6 +76,13 @@ class BillName extends BasicConverter implements ConverterInterface
]
);
if (is_null($bill->id)) {
$this->setCertainty(0);
Log::info('Could not store new bill by name', $bill->getErrors()->toArray());
return new Bill;
}
$this->setCertainty(100);
return $bill;

View File

@ -63,6 +63,9 @@ class BudgetId extends BasicConverter implements ConverterInterface
// should not really happen. If the ID does not match FF, what is FF supposed to do?
$this->setCertainty(0);
Log::info(sprintf('Could not find budget with ID %d. Will return NULL', $value));
return new Budget;
}

View File

@ -62,6 +62,9 @@ class CategoryId extends BasicConverter implements ConverterInterface
// should not really happen. If the ID does not match FF, what is FF supposed to do?
$this->setCertainty(0);
Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value));
return new Category;
}

View File

@ -61,6 +61,9 @@ class CurrencyId extends BasicConverter implements ConverterInterface
}
$this->setCertainty(0);
// should not really happen. If the ID does not match FF, what is FF supposed to do?
Log::info(sprintf('Could not find category with ID %d. Will return NULL', $value));
return new TransactionCurrency;
}

View File

@ -37,9 +37,10 @@ class Date extends BasicConverter implements ConverterInterface
try {
$date = Carbon::createFromFormat($this->config['date-format'], $value);
} catch (InvalidArgumentException $e) {
Log::critical($e->getMessage());
Log::critical('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]);
throw new FireflyException(sprintf('Cannot convert "%s" to a valid date using format "%s".', $value, $this->config['date-format']));
Log::notice($e->getMessage());
Log::notice('Cannot convert this string using the given format.', ['value' => $value, 'format' => $this->config['date-format']]);
$this->setCertainty(0);
return new Carbon;
}
Log::debug('Converted date', ['converted' => $date->toAtomString()]);
$this->setCertainty(100);

View File

@ -56,7 +56,7 @@ class OpposingAccountIban extends BasicConverter implements ConverterInterface
$account = $repository->findByIban($value, []);
if (!is_null($account->id)) {
Log::debug('Found account by IBAN', ['id' => $account->id]);
Log::warning(
Log::notice(
'The match between IBAN and account is uncertain because the type of transactions may not have been determined.',
['id' => $account->id, 'iban' => $value]
);

View File

@ -47,6 +47,7 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface
if (!is_null($account->id)) {
Log::debug('Found account by ID', ['id' => $account->id]);
$this->setCertainty(100);
return $account;
}
}
@ -55,11 +56,12 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface
$account = $repository->findByName($value, []);
if (!is_null($account->id)) {
Log::debug('Found opposing account by name', ['id' => $account->id]);
Log::warning(
Log::info(
'The match between name and account is uncertain because the type of transactions may not have been determined.',
['id' => $account->id, 'name' => $value]
);
$this->setCertainty(50);
return $account;
}
@ -68,6 +70,11 @@ class OpposingAccountName extends BasicConverter implements ConverterInterface
'openingBalance' => 0,
]
);
if (is_null($account->id)) {
$this->setCertainty(0);
return new Account;
}
$this->setCertainty(100);
Log::debug('Created new opposing account ', ['name' => $account->name, 'id' => $account->id]);

View File

@ -221,7 +221,7 @@ class ImportEntry
return;
}
Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
}
@ -239,7 +239,7 @@ class ImportEntry
return;
}
Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field]));
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d).', $field, $certainty, $this->certain[$field]));
}
/**
@ -256,7 +256,7 @@ class ImportEntry
return;
}
Log::error(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
Log::info(sprintf('Will not set %s based on certainty %d (current certainty is %d) or NULL id.', $field, $certainty, $this->certain[$field]));
}
}

View File

@ -57,9 +57,9 @@ class ImportStorage
*/
public function store()
{
foreach ($this->entries as $entry) {
Log::debug('--- import store start ---');
$this->storeSingle($entry);
foreach ($this->entries as $index => $entry) {
Log::debug(sprintf('--- import store start for row %d ---', $index));
$this->storeSingle($index, $entry);
}
}
@ -80,19 +80,20 @@ class ImportStorage
}
/**
* @param int $index
* @param ImportEntry $entry
*
* @throws FireflyException
*/
private function storeSingle(ImportEntry $entry)
private function storeSingle(int $index, ImportEntry $entry)
{
if ($entry->valid === false) {
Log::error('Cannot import entry, because valid=false');
Log::error(sprintf('Cannot import row %d, because valid=false', $index));
return;
}
Log::debug('Going to store entry!');
Log::debug(sprintf('Going to store row %d', $index));
$billId = is_null($entry->fields['bill']) ? null : $entry->fields['bill']->id;
$journalData = [
'user_id' => $entry->user->id,
@ -108,7 +109,12 @@ class ImportStorage
];
/** @var TransactionJournal $journal */
$journal = TransactionJournal::create($journalData);
$amount = $this->makePositive($entry->fields['amount']);
foreach ($journal->getErrors()->all() as $err) {
Log::error($err);
}
$amount = $this->makePositive($entry->fields['amount']);
Log::debug('Created journal', ['id' => $journal->id]);
@ -154,13 +160,30 @@ class ImportStorage
$one = Transaction::create($sourceData);
$two = Transaction::create($destinationData);
Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id,'account_name' => $source->name]);
Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id,'account_name' => $destination->name]);
Log::debug('Created transaction 1', ['id' => $one->id, 'account' => $one->account_id, 'account_name' => $source->name]);
Log::debug('Created transaction 2', ['id' => $two->id, 'account' => $two->account_id, 'account_name' => $destination->name]);
$journal->completed = 1;
$journal->save();
// now attach budget and so on.
if (!is_null($entry->fields['budget']) && !is_null($entry->fields['budget']->id)) {
$journal->budgets()->save($entry->fields['budget']);
Log::debug('Attached budget', ['id' => $entry->fields['budget']->id, 'name' => $entry->fields['budget']->name]);
$journal->save();
}
if (!is_null($entry->fields['category']) && !is_null($entry->fields['category']->id)) {
$journal->categories()->save($entry->fields['category']);
Log::debug('Attached category', ['id' => $entry->fields['category']->id, 'name' => $entry->fields['category']->name]);
$journal->save();
}
if (!is_null($entry->fields['bill']) && !is_null($entry->fields['bill']->id)) {
$journal->bill()->associate($entry->fields['bill']);
Log::debug('Attached bill', ['id' => $entry->fields['bill']->id, 'name' => $entry->fields['bill']->name]);
$journal->save();
}
}

View File

@ -52,8 +52,8 @@ class ImportValidator
{
$newCollection = new Collection;
/** @var ImportEntry $entry */
foreach ($this->entries as $entry) {
Log::debug('--- import validator start ---');
foreach ($this->entries as $index => $entry) {
Log::debug(sprintf('--- import validator start for row %d ---', $index));
/*
* X Adds the date (today) if no date is present.
* X Determins the types of accounts involved (asset, expense, revenue).
@ -64,14 +64,12 @@ class ImportValidator
$entry = $this->checkAmount($entry);
$entry = $this->setDate($entry);
$entry = $this->setAssetAccount($entry);
Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type));
$entry = $this->setOpposingAccount($entry);
Log::debug(sprintf('Opposing account is of type %s', $entry->fields['opposing-account']->accountType->type));
$entry = $this->cleanDescription($entry);
$entry = $this->setTransactionType($entry);
$entry = $this->setTransactionCurrency($entry);
$newCollection->push($entry);
$newCollection->put($index, $entry);
}
return $newCollection;
@ -118,6 +116,7 @@ class ImportValidator
*/
private function cleanDescription(ImportEntry $entry): ImportEntry
{
if (!isset($entry->fields['description'])) {
Log::debug('Set empty transaction description because field was not set.');
$entry->fields['description'] = '(empty transaction description)';
@ -130,6 +129,7 @@ class ImportValidator
return $entry;
}
$entry->fields['description'] = trim($entry->fields['description']);
if (strlen($entry->fields['description']) == 0) {
Log::debug('Set empty transaction description because field was empty.');
@ -137,8 +137,7 @@ class ImportValidator
return $entry;
}
Log::debug('Transaction description is OK.');
$entry->fields['description'] = trim($entry->fields['description']);
Log::debug('Transaction description is OK.', ['description' => $entry->fields['description']]);
return $entry;
}
@ -246,7 +245,7 @@ class ImportValidator
*/
private function setDate(ImportEntry $entry): ImportEntry
{
if (is_null($entry->fields['date-transaction'])) {
if (is_null($entry->fields['date-transaction']) || $entry->certain['date-transaction'] == 0) {
// empty date field? find alternative.
$alternatives = ['date-book', 'date-interest', 'date-process'];
foreach ($alternatives as $alternative) {
@ -263,6 +262,9 @@ class ImportValidator
return $entry;
}
// confidence is zero?
Log::debug('Date-transaction is OK');
return $entry;

View File

@ -51,11 +51,10 @@ class CsvImporter implements ImporterInterface
Log::debug('----- import entry build start --');
Log::debug(sprintf('Now going to import row %d.', $index));
$importEntry = $this->importSingleRow($index, $row);
$collection->push($importEntry);
$collection->put($index, $importEntry);
}
}
Log::debug(sprintf('Collection contains %d entries', $collection->count()));
Log::debug('This call should be intercepted somehow.');
Log::debug(sprintf('Import collection contains %d entries', $collection->count()));
return $collection;
}

View File

@ -13,6 +13,7 @@ namespace FireflyIII\Import\Logging;
use Illuminate\Console\Command;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
/**
* Class CommandHandler
@ -32,7 +33,9 @@ class CommandHandler extends AbstractProcessingHandler
*/
public function __construct(Command $command)
{
parent::__construct();
$this->command = $command;
$this->changeLevel(env('LOG_LEVEL', 'debug'));
}
/**
@ -44,6 +47,39 @@ class CommandHandler extends AbstractProcessingHandler
*/
protected function write(array $record)
{
$this->command->line((string) trim($record['formatted']));
$this->command->line((string)trim($record['formatted']));
}
/**
* @param string $level
*/
private function changeLevel(string $level)
{
switch ($level) {
case 'debug':
$this->setLevel(Logger::DEBUG);
break;
case 'info':
$this->setLevel(Logger::INFO);
break;
case 'notice':
$this->setLevel(Logger::NOTICE);
break;
case 'warning':
$this->setLevel(Logger::WARNING);
break;
case 'error':
$this->setLevel(Logger::ERROR);
break;
case 'critical':
$this->setLevel(Logger::CRITICAL);
break;
case 'alert':
$this->setLevel(Logger::ALERT);
break;
case 'emergency':
$this->setLevel(Logger::EMERGENCY);
break;
}
}
}

View File

@ -79,8 +79,9 @@ class Account extends Model
= [
'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id',
'name' => 'required',
'name' => 'required|between:1,200',
'active' => 'required|boolean',
'iban' => 'between:1,50|iban',
];
/** @var bool */
private $joinedAccountTypes;

View File

@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Bill
@ -58,17 +59,20 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereNameEncrypted($value)
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereMatchEncrypted($value)
* @mixin \Eloquent
* @property string $deleted_at
* @property string $deleted_at
* @method static \Illuminate\Database\Query\Builder|\FireflyIII\Models\Bill whereDeletedAt($value)
*/
class Bill extends Model
{
use ValidatingTrait;
protected $dates = ['created_at', 'updated_at', 'date'];
protected $fillable
= ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip',
'automatch', 'active',];
protected $hidden = ['amount_min_encrypted', 'amount_max_encrypted', 'name_encrypted', 'match_encrypted'];
protected $rules = ['name' => 'required|between:1,200',];
/**
* @param Bill $value

View File

@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Budget
@ -51,11 +52,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class Budget extends Model
{
use SoftDeletes;
use SoftDeletes, ValidatingTrait;
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'startdate', 'enddate'];
protected $fillable = ['user_id', 'name', 'active'];
protected $hidden = ['encrypted'];
protected $rules = ['name' => 'required|between:1,200',];
/**
* @param array $fields

View File

@ -17,6 +17,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Category
@ -46,11 +47,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class Category extends Model
{
use SoftDeletes;
use SoftDeletes, ValidatingTrait;
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $fillable = ['user_id', 'name'];
protected $hidden = ['encrypted'];
protected $rules = ['name' => 'required|between:1,200',];
/**
* @param array $fields

View File

@ -15,6 +15,7 @@ use Auth;
use Crypt;
use FireflyIII\Support\Models\TagSupport;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\Tag
@ -52,6 +53,9 @@ class Tag extends TagSupport
{
protected $dates = ['created_at', 'updated_at', 'date'];
protected $fillable = ['user_id', 'tag', 'date', 'description', 'longitude', 'latitude', 'zoomLevel', 'tagMode'];
protected $rules = ['tag' => 'required|between:1,200',];
use ValidatingTrait;
/**
* @param array $fields

View File

@ -15,6 +15,7 @@ use Auth;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Watson\Validating\ValidatingTrait;
/**
* FireflyIII\Models\TransactionCurrency
@ -38,19 +39,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class TransactionCurrency extends Model
{
use SoftDeletes;
use SoftDeletes, ValidatingTrait;
protected $fillable = ['name', 'code', 'symbol'];
protected $dates = ['created_at', 'updated_at', 'deleted_at'];
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactionJournals()
{
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
protected $fillable = ['name', 'code', 'symbol'];
protected $rules = ['name' => 'required|between:1,200', 'code' => 'required|between:3,3', 'symbol' => 'required|between:1,12'];
/**
* @param TransactionCurrency $currency
@ -64,4 +58,12 @@ class TransactionCurrency extends Model
}
throw new NotFoundHttpException;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function transactionJournals()
{
return $this->hasMany('FireflyIII\Models\TransactionJournal');
}
}

View File

@ -444,6 +444,12 @@ class CategoryRepository implements CategoryRepositoryInterface
*/
public function store(array $data): Category
{
// TODO use validation, not this.
if (strlen($data['name']) > 200 || strlen($data['name']) === 0) {
}
$newCategory = Category::firstOrCreateEncrypted(
[
'user_id' => $data['user'],

View File

@ -72,9 +72,9 @@ class TestData
'updated_at' => $this->time,
'user_id' => $account['user_id'],
'account_type_id' => $account['account_type_id'],
'name' => Crypt::encrypt($account['name']),
'name' => $account['name'],
'active' => 1,
'encrypted' => 1,
'encrypted' => 0,
'virtual_balance' => 0,
'iban' => isset($account['iban']) ? Crypt::encrypt($account['iban']) : null,
];
@ -244,6 +244,25 @@ class TestData
DB::table('categories')->insert($insert);
}
/**
*
*/
private function createCurrencies()
{
$insert = [];
foreach ($this->data['currencies'] as $job) {
$insert[] = [
'created_at' => $this->time,
'updated_at' => $this->time,
'deleted_at' => null,
'code' => $job['code'],
'name' => $job['name'],
'symbol' => $job['symbol'],
];
}
DB::table('transaction_currencies')->insert($insert);
}
/**
*
*/
@ -829,6 +848,7 @@ class TestData
$this->createMultiDeposits();
$this->createMultiTransfers();
$this->createImportJobs();
$this->createCurrencies();
}
}

View File

@ -38,10 +38,15 @@ class TestDataSeeder extends Seeder
if ($disk->exists($fileName)) {
Log::debug('Now seeding ' . $fileName);
$file = json_decode($disk->get($fileName), true);
// run the file:
TestData::run($file);
if (is_array($file)) {
// run the file:
TestData::run($file);
return;
}
Log::error('No valid data found (' . $fileName . ') for environment ' . $env);
return;
}
Log::info('No seed file (' . $fileName . ') for environment ' . $env);
}

View File

@ -11,20 +11,78 @@
"role": 1
}
],
"accounts": [],
"account-meta": [],
"bills": [],
"budgets": [],
"accounts": [
{
"user_id": 1,
"account_type_id": 3,
"name": "ExistingAssetAccount",
"iban": "NL62EXFK3945306779"
},
{
"user_id": 1,
"account_type_id": 4,
"name": "ExistingOpposingAccount",
"iban": "NL79BGWN6303364632"
}
],
"account-meta": [
{
"account_id": 1,
"name": "accountNumber",
"data": "\"3945306779\""
},
{
"account_id": 2,
"name": "accountNumber",
"data": "\"6303364632\""
}
],
"bills": [
{
"name": "ExistingBill",
"match": "ExistingBill",
"amount_min": 100,
"amount_max": 200,
"user_id": 1,
"date": "2015-01-01",
"active": 1,
"automatch": 1,
"repeat_freq": "monthly",
"skip": 0
}
],
"budgets": [
{
"name": "ExistingBudget",
"user_id": 1
}
],
"budget-limits": [],
"monthly-limits": [],
"categories": [],
"categories": [
{
"name": "ExistingCategory",
"user_id": 1
}
],
"piggy-banks": [],
"piggy-events": [],
"rule-groups": [],
"rules": [],
"rule-triggers": [],
"rule-actions": [],
"tags": [],
"tags": [
{
"user_id": 1,
"tag": "ExistingTag",
"tagMode": "nothing"
},
{
"user_id": 1,
"tag": "AnotherExistingTag",
"tagMode": "nothing"
}
],
"monthly-deposits": [],
"monthly-transfers": [],
"monthly-withdrawals": [],
@ -43,18 +101,67 @@
"date-format": "Ymd",
"delimiter": ",",
"import-account": 0,
"specifics": [],
"column-count": 7,
"specifics": {
"RabobankDescription": 1,
"AbnAmroDescription": 1
},
"column-count": 30,
"column-roles": [
"account-name",
"opposing-name",
"amount",
"date-transaction",
"category-name",
"account-id",
"account-iban",
"account-name",
"opposing-number",
"bill-id",
"bill-name",
"budget-id",
"budget-name",
"description"
"category-id",
"category-name",
"currency-code",
"currency-id",
"currency-symbol",
"currency-name",
"date-transaction",
"description",
"_ignore",
"ing-debet-credit",
"opposing-iban",
"opposing-id",
"opposing-name",
"opposing-number",
"rabo-debet-credit",
"tags-comma",
"tags-space",
"date-interest",
"date-book",
"date-process",
"external-id"
],
"column-do-mapping": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
@ -64,9 +171,16 @@
false
],
"column-roles-complete": false,
"column-mapping-config": [],
"column-mapping-config": {},
"column-mapping-complete": false
}
}
],
"currencies": [
{
"name": "ExistingCurrency",
"symbol": "#",
"code": "EXI"
}
]
}

View File

@ -1007,5 +1007,6 @@
]
}
],
"import-jobs": []
"import-jobs": [],
"currencies": []
}

View File

@ -298,5 +298,6 @@
]
}
],
"import-jobs": []
"import-jobs": [],
"currencies": []
}

View File

@ -1007,5 +1007,6 @@
]
}
],
"import-jobs": []
"import-jobs": [],
"currencies": []
}