No known key found for this signature in database
GPG Key ID: BDE6667570EADBD5
50 changed files with 47957 additions and 628 deletions
-
12app/Console/AutoImports.php
-
4app/Console/Commands/AutoImport.php
-
4app/Console/StartImport.php
-
6app/Http/Controllers/AutoImportController.php
-
6app/Http/Controllers/AutoUploadController.php
-
37app/Http/Controllers/Import/CSV/ConvertController.php
-
4app/Http/Controllers/Import/CSV/MapController.php
-
48app/Http/Controllers/Import/CSV/RoleController.php
-
15app/Http/Controllers/Import/ConfigurationController.php
-
4app/Http/Controllers/Import/UploadController.php
-
2app/Http/Controllers/NavController.php
-
2app/Http/Middleware/ConfigComplete.php
-
2app/Http/Middleware/MappingComplete.php
-
2app/Http/Middleware/RolesComplete.php
-
48app/Http/Request/ConfigurationPostRequest.php
-
2app/Http/Request/RolesPostRequest.php
-
8app/Services/CSV/Configuration/ConfigFileProcessor.php
-
4app/Services/CSV/Mapper/AssetAccountIbans.php
-
4app/Services/CSV/Mapper/AssetAccounts.php
-
6app/Services/CSV/Mapper/Bills.php
-
6app/Services/CSV/Mapper/Budgets.php
-
6app/Services/CSV/Mapper/Categories.php
-
18app/Services/CSV/Mapper/GetAccounts.php
-
8app/Services/CSV/Mapper/MapperService.php
-
4app/Services/CSV/Mapper/OpposingAccountIbans.php
-
4app/Services/CSV/Mapper/OpposingAccounts.php
-
6app/Services/CSV/Mapper/TransactionCurrencies.php
-
4app/Services/Import/ImportRoutineManager.php
-
10app/Services/Import/Routine/CSVFileProcessor.php
-
10app/Services/Import/Routine/LineProcessor.php
-
14app/Services/Import/Routine/PseudoTransactionProcessor.php
-
20app/Services/Import/Task/Accounts.php
-
14app/Services/Session/Constants.php
-
14app/Support/Token.php
-
10package.json
-
47251public/js/app.js
-
8resources/js/app.js
-
2resources/js/components/import/ConversionMessages.vue
-
177resources/js/components/import/ConversionStatus.vue
-
168resources/js/components/import/ImportStatus.vue
-
12resources/views/import/003-upload/index.twig
-
198resources/views/import/004-configure/index.twig
-
154resources/views/import/005-roles/index.twig
-
0resources/views/import/006-mapping/index.twig
-
50resources/views/import/007-convert/index.twig
-
119resources/views/import/roles/index.twig
-
37resources/views/import/run/index.twig
-
2resources/views/index.twig
-
38routes/web.php
-
1webpack.mix.js
47251
public/js/app.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,177 @@ |
|||
<!-- |
|||
- ImportStatus.vue |
|||
- Copyright (c) 2021 james@firefly-iii.org |
|||
- |
|||
- This file is part of the Firefly III Data Importer |
|||
- (https://github.com/firefly-iii/data-importer). |
|||
- |
|||
- This program is free software: you can redistribute it and/or modify |
|||
- it under the terms of the GNU Affero General Public License as |
|||
- published by the Free Software Foundation, either version 3 of the |
|||
- License, or (at your option) any later version. |
|||
- |
|||
- This program is distributed in the hope that it will be useful, |
|||
- but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
- GNU Affero General Public License for more details. |
|||
- |
|||
- You should have received a copy of the GNU Affero General Public License |
|||
- along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<template> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header">Data conversion</div> |
|||
<div class="card-body" v-if="'waiting_to_start' === this.status && false === this.triedToStart"> |
|||
<p> |
|||
Your CSV file will be converted so it can be imported. Press "start job" to start. |
|||
</p> |
|||
<p> |
|||
<button class="btn btn-success float-end" v-on:click="callStart" type="button">Start job |
|||
→ |
|||
</button> |
|||
</p> |
|||
</div> |
|||
<div class="card-body" v-if="'waiting_to_start' === this.status && true === this.triedToStart"> |
|||
<p>Waiting for the job to start..</p> |
|||
</div> |
|||
<div class="card-body" v-if="'job_running' === this.status"> |
|||
<p> |
|||
Job is running, please wait. |
|||
</p> |
|||
<div class="progress"> |
|||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" |
|||
aria-valuenow="100" aria-valuemin="0" |
|||
aria-valuemax="100" style="width: 100%"></div> |
|||
</div> |
|||
<conversion-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></conversion-messages> |
|||
</div> |
|||
<div class="card-body" v-if="'job_done' === this.status "> |
|||
<p> |
|||
The import routine has finished 🎉. You can <a :href="this.flushUrl" |
|||
class="btn btn-success btn-sm">start a new |
|||
import</a>, |
|||
<a class="btn btn-info btn-sm" :href="this.downloadUrl" title="Download configuration file.">download |
|||
the import configuration</a> |
|||
or inspect the results of the import further below: |
|||
</p> |
|||
<conversion-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></conversion-messages> |
|||
<p> |
|||
Thank you for using this tool. <a rel="noopener noreferrer" |
|||
href="https://github.com/firefly-iii/firefly-iii/issues" |
|||
target="_blank">Please share any feedback you may have</a>. |
|||
</p> |
|||
</div> |
|||
<div class="card-body" v-if="'job_errored' === this.status"> |
|||
<p class="text-danger"> |
|||
The job could not be started, or failed due to an error. Please check the log files. Sorry about |
|||
this :(. |
|||
</p> |
|||
<conversion-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></conversion-messages> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "ConversionStatus", |
|||
/* |
|||
* The component's data. |
|||
*/ |
|||
data() { |
|||
return { |
|||
triedToStart: false, |
|||
status: '', |
|||
messages: [], |
|||
warnings: [], |
|||
errors: [], |
|||
downloadUrl: window.configDownloadUrl, |
|||
jobBackUrl: window.jobBackUrl, |
|||
flushUrl: window.flushUrl |
|||
}; |
|||
}, |
|||
props: [], |
|||
mounted() { |
|||
console.log(`Mounted, check job at ${jobStatusUrl}.`); |
|||
this.getJobStatus(); |
|||
}, |
|||
methods: { |
|||
getJobStatus: function () { |
|||
console.log('getJobStatus'); |
|||
axios.get(jobStatusUrl).then((response) => { |
|||
|
|||
// first try post result: |
|||
if (true === this.triedToStart && 'job_errored' === this.status) { |
|||
console.error('Job failed!!!'); |
|||
return; |
|||
} |
|||
|
|||
// handle success |
|||
this.errors = response.data.errors; |
|||
this.warnings = response.data.warnings; |
|||
this.messages = response.data.messages; |
|||
console.log(`Job status is ${this.status}.`); |
|||
if (false === this.triedToStart && 'waiting_to_start' === response.data.status) { |
|||
// call to job start. |
|||
console.log('Job hasn\'t started yet. Show user some info'); |
|||
this.status = response.data.status; |
|||
return; |
|||
} |
|||
if (true === this.triedToStart && 'waiting_to_start' === response.data.status) { |
|||
console.log('Job hasn\'t started yet, but its been tried.'); |
|||
} |
|||
if (true === this.triedToStart && 'job_errored' === response.data.status) { |
|||
console.error('Job failed'); |
|||
this.status = response.data.status; |
|||
return; |
|||
} |
|||
if ('job_done' === response.data.status) { |
|||
console.log('Job is done!'); |
|||
this.status = response.data.status; |
|||
return; |
|||
} |
|||
if ('job_errored' === response.data.status) { |
|||
console.error('Job is kill.'); |
|||
console.error(response.data); |
|||
return; |
|||
} |
|||
|
|||
setTimeout(function () { |
|||
console.log('Fired on setTimeout'); |
|||
this.getJobStatus(); |
|||
}.bind(this), 1000); |
|||
}); |
|||
}, |
|||
callStart: function () { |
|||
console.log('Call job start URL: ' + jobStartUrl); |
|||
axios.post(jobStartUrl).then((response) => { |
|||
console.log('POST was OK'); |
|||
this.getJobStatus(); |
|||
}).catch((error) => { |
|||
console.error('JOB HAS FAILED :('); |
|||
this.triedToStart = true; |
|||
this.status = 'job_errored'; |
|||
}); |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
}, |
|||
}, |
|||
watch: {} |
|||
} |
|||
</script> |
@ -1,168 +0,0 @@ |
|||
<!-- |
|||
- ImportStatus.vue |
|||
- Copyright (c) 2021 james@firefly-iii.org |
|||
- |
|||
- This file is part of the Firefly III Data Importer |
|||
- (https://github.com/firefly-iii/data-importer). |
|||
- |
|||
- This program is free software: you can redistribute it and/or modify |
|||
- it under the terms of the GNU Affero General Public License as |
|||
- published by the Free Software Foundation, either version 3 of the |
|||
- License, or (at your option) any later version. |
|||
- |
|||
- This program is distributed in the hope that it will be useful, |
|||
- but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
- GNU Affero General Public License for more details. |
|||
- |
|||
- You should have received a copy of the GNU Affero General Public License |
|||
- along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
--> |
|||
|
|||
<template> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<div class="card"> |
|||
<div class="card-header">Import status</div> |
|||
<div class="card-body" v-if="'waiting_to_start' === this.status && false === this.triedToStart"> |
|||
<p> |
|||
The tool is ready to import your data. Press "start job" to start. |
|||
<a :href="this.downloadUrl" title="Download configuration file."> |
|||
You can download a configuration file of your import</a>, so you can make a quick start the next time you import. |
|||
</p> |
|||
<div class="row"> |
|||
<div class="col-lg-6"> |
|||
<!-- go back to upload --> |
|||
<a :href="this.jobBackUrl" class="btn btn-secondary">← Go back</a> |
|||
</div> |
|||
<div class="col-lg-6"> |
|||
<button |
|||
class="btn btn-success float-right" |
|||
v-on:click="callStart" type="button">Start job → |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<p> |
|||
|
|||
</p> |
|||
</div> |
|||
<div class="card-body" v-if="'waiting_to_start' === this.status && true === this.triedToStart"> |
|||
<p>Waiting for the job to start.. |
|||
</p> |
|||
</div> |
|||
<div class="card-body" v-if="'job_running' === this.status"> |
|||
<p> |
|||
Job is running, please wait. |
|||
</p> |
|||
<div class="progress"> |
|||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100" aria-valuemin="0" |
|||
aria-valuemax="100" style="width: 100%"></div> |
|||
</div> |
|||
<import-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></import-messages> |
|||
</div> |
|||
<div class="card-body" v-if="'job_done' === this.status "> |
|||
<p> |
|||
The import routine has finished 🎉. You can <a :href="this.flushUrl" class="btn btn-success btn-sm">start a new import</a>, |
|||
<a class="btn btn-info btn-sm" :href="this.downloadUrl" title="Download configuration file.">download the import configuration</a> |
|||
or inspect the results of the import further below: |
|||
</p> |
|||
<import-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></import-messages> |
|||
<p> |
|||
Thank you for using this tool. <a rel="noopener noreferrer" href="https://github.com/firefly-iii/firefly-iii/issues" target="_blank">Please share any feedback you may have</a>. |
|||
</p> |
|||
</div> |
|||
<div class="card-body" v-if="'job_errored' === this.status"> |
|||
<p class="text-danger"> |
|||
The job could not be started, or failed due to an error. Please check the log files. Sorry about this :(. |
|||
</p> |
|||
<import-messages |
|||
:messages="this.messages" |
|||
:warnings="this.warnings" |
|||
:errors="this.errors" |
|||
></import-messages> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "ImportStatus", |
|||
/* |
|||
* The component's data. |
|||
*/ |
|||
data() { |
|||
return { |
|||
triedToStart: false, |
|||
status: '', |
|||
messages: [], |
|||
warnings: [], |
|||
errors: [], |
|||
downloadUrl: window.configDownloadUrl, |
|||
jobBackUrl: window.jobBackUrl, |
|||
flushUrl: window.flushUrl |
|||
}; |
|||
}, |
|||
props: [], |
|||
mounted() { |
|||
console.log(`Mounted, check job at ${jobStatusUrl}.`); |
|||
this.getJobStatus(); |
|||
}, |
|||
methods: { |
|||
getJobStatus: function () { |
|||
console.log('getJobStatus'); |
|||
axios.get(jobStatusUrl).then((response) => { |
|||
// handle success |
|||
this.status = response.data.status; |
|||
this.errors = response.data.errors; |
|||
this.warnings = response.data.warnings; |
|||
this.messages = response.data.messages; |
|||
console.log(`Job status is ${this.status}.`); |
|||
if (false === this.triedToStart && 'waiting_to_start' === this.status) { |
|||
// call to job start. |
|||
console.log('Job hasn\'t started yet. Show user some info'); |
|||
return; |
|||
} |
|||
if (true === this.triedToStart && 'waiting_to_start' === this.status) { |
|||
console.log('Job hasn\'t started yet.'); |
|||
} |
|||
if ('job_done' === this.status) { |
|||
console.log('Job is done!'); |
|||
return; |
|||
} |
|||
if('job_errored' === this.status) { |
|||
console.error('Job is kill.'); |
|||
console.error(response.data); |
|||
return; |
|||
} |
|||
|
|||
setTimeout(function () { |
|||
console.log('Fired on setTimeout'); |
|||
this.getJobStatus(); |
|||
}.bind(this), 1000); |
|||
}); |
|||
}, |
|||
callStart: function () { |
|||
console.log('Call job start URL: ' + jobStartUrl); |
|||
axios.post(jobStartUrl).then((response) => { |
|||
this.getJobStatus(); |
|||
}).catch((error) => { |
|||
this.triedToStart = true; |
|||
this.status = 'job_errored'; |
|||
}); |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
}, |
|||
}, |
|||
watch: {} |
|||
} |
|||
</script> |
@ -0,0 +1,154 @@ |
|||
{% extends "./layout/default" %} |
|||
{% block content %} |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
{{ subTitle }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<p> |
|||
Set up the the meaning of each column in your file. |
|||
</p> |
|||
<p> |
|||
Each column in your TODO file has a role, it contains a specific type of content. |
|||
By configuring these roles here, you tell the importer how to approach and treat |
|||
the data in each column. <a target="_blank" |
|||
href="https://docs.firefly-iii.org/csv/usage/roles/">TODO Read |
|||
the documentation</a> to learn more |
|||
about this process. |
|||
|
|||
TODO explain account / opposing account |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% if not errors.isEmpty %} |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Errors :( |
|||
</div> |
|||
<div class="card-body"> |
|||
<p class="text-danger">Some error(s) occurred:</p> |
|||
<ul> |
|||
{% for error in errors.all %} |
|||
<li class="text-danger">{{ error }}</li> |
|||
{% endfor %} |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endif %} |
|||
|
|||
<div class="row mt-3"> |
|||
<div class="col-lg-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Role configuration |
|||
</div> |
|||
<div class="card-body"> |
|||
<form method="post" action="{{ route('005-roles.post') }}" accept-charset="UTF-8"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
<table class="table"> |
|||
<tr> |
|||
<th>Column</th> |
|||
<th>Example data</th> |
|||
<th>Role</th> |
|||
<th>Map data?</th> |
|||
</tr> |
|||
{% for index, column in columns %} |
|||
<tr> |
|||
<td>{{ column }} |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
<br/> |
|||
<span class="text-muted small">This is the unique column</span> |
|||
{% endif %}</td> |
|||
<td> |
|||
{% if examples[index]|length > 0 %} |
|||
{% for example in examples[index] %} |
|||
<pre style="color:#e83e8c;margin-bottom:0;">{{ example }}</pre> |
|||
{% endfor %} |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
{# |
|||
User cannot select a role because its the unique column so it MUST be this role. |
|||
#} |
|||
<p class="form-text"> |
|||
<span class="text-muted small"> |
|||
This column is your unique identifier, so it will be fixed to |
|||
</span> |
|||
<code class="small">{{ configuration.getUniqueColumnType }}</code> |
|||
</p> |
|||
{# smart users can overrule this of course. #} |
|||
<input type="hidden" name="roles[{{ index }}]" |
|||
value="{{ configuration.getUniqueColumnType }}"/> |
|||
{% else %} |
|||
<select name="roles[{{ index }}]" id="roles_{{ index }}" |
|||
class="form-control"> |
|||
{% for key, role in roles %} |
|||
<option value="{{ key }}" |
|||
{% if configuredRoles[index] == key %}selected{% endif %} |
|||
label="{{ trans('import.column_'~key) }}">{{ trans('import.column_'~key) }}</option> |
|||
{% endfor %} |
|||
</select> |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
<label for="do_mapping_{{ index }}"> |
|||
{# reverse if statement is pretty sloppy but OK. #} |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
|
|||
{% else %} |
|||
<input type="checkbox" |
|||
{% if configuredDoMapping[index] %}checked{% endif %} |
|||
name="do_mapping[{{ index }}]" id="do_mapping_{{ index }}" |
|||
value="1"/> |
|||
{% endif %} |
|||
</label> |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
<button type="submit" class="float-end btn btn-primary">Submit →</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ route('back.config') }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> Go back to configuration</a> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm"><span class="fas fa-redo-alt"></span> Start over</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- |
|||
<div class="row"> |
|||
<div class="col-lg-6"> |
|||
|
|||
</div> |
|||
<div class="col-lg-6"> |
|||
|
|||
</div> |
|||
</div> |
|||
--> |
|||
{% endblock %} |
|||
|
@ -0,0 +1,50 @@ |
|||
{% extends "./layout/default" %} |
|||
{% block content %} |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<h1>{{ mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div id="app"> |
|||
<conversion-status></conversion-status> |
|||
</div> |
|||
|
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a class="btn btn-danger btn-sm" href="{{ route('flush') }}" data-bs-toggle="tooltip" data-bs-placement="top" title="If the importer seems stuck, you can reset it.">Start over</a> |
|||
<a class="btn btn-info text-white btn-sm" href="{{ route('004-configure.download') }}" data-bs-toggle="tooltip" data-bs-placement="top" title="You can download a configuration file of your import, so you can make a quick start the next time you import."> |
|||
Download configuration file |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
{% endblock %} |
|||
{% block scripts %} |
|||
<script type="text/javascript"> |
|||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) |
|||
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { |
|||
return new bootstrap.Tooltip(tooltipTriggerEl) |
|||
}) |
|||
|
|||
var jobStatusUrl = '{{ route('007-convert.status') }}?identifier={{ identifier }}'; |
|||
var jobStartUrl = '{{ route('007-convert.start') }}?identifier={{ identifier }}'; |
|||
var jobBackUrl = '{{ jobBackUrl }}'; |
|||
var configDownloadUrl = '{{ route('004-configure.download') }}?identifier={{ identifier }}'; |
|||
var flushUrl = '{{ route('flush') }}'; |
|||
</script> |
|||
<script src="{{ asset('js/app.js') }}?version={{ version }}"></script> |
|||
{% endblock %} |
@ -1,119 +0,0 @@ |
|||
{% extends "./layout/default" %} |
|||
{% block content %} |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<h1>{{ mainTitle }}</h1> |
|||
<h2>{{ subTitle }}</h2> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<p class="lead">Set up the the meaning of each column in your file.</p> |
|||
<p> |
|||
Each column in your CSV file has a role, it contains a specific type of content. |
|||
By configuring these roles here, you tell the importer how to approach and treat |
|||
the data in each column. <a target="_blank" href="https://docs.firefly-iii.org/csv/usage/roles/">Read the documentation</a> to learn more |
|||
about this process. |
|||
</p> |
|||
{% if not errors.isEmpty %} |
|||
<p class="text-danger">Some error(s) occurred:</p> |
|||
<ul> |
|||
{% for error in errors.all %} |
|||
<li class="text-danger">{{ error }}</li> |
|||
{% endfor %} |
|||
</ul> |
|||
{% endif %} |
|||
<hr/> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<form method="post" action="{{ route('import.roles.post') }}" accept-charset="UTF-8"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
<input type="hidden" name="mapping" value="{{ mapping }}"/> |
|||
<table class="table"> |
|||
<tr> |
|||
<th>Column</th> |
|||
<th>Example data</th> |
|||
<th>Role</th> |
|||
<th>Map data?</th> |
|||
</tr> |
|||
{% for index, column in columns %} |
|||
<tr> |
|||
<td>{{ column }} |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
<br /> |
|||
<span class="text-muted small">This is the unique column</span> |
|||
{% endif %}</td> |
|||
<td> |
|||
{% if examples[index]|length > 0 %} |
|||
{% for example in examples[index] %} |
|||
<pre style="color:#e83e8c;margin-bottom:0;">{{ example }}</pre> |
|||
{% endfor %} |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
{# |
|||
User cannot select a role because its the unique column so it MUST be this role. |
|||
#} |
|||
<p class="form-text"> |
|||
<span class="text-muted small"> |
|||
This column is your unique identifier, so it will be fixed to |
|||
</span> |
|||
<code class="small">{{ configuration.getUniqueColumnType }}</code> |
|||
</p> |
|||
{# smart users can overrule this of course. #} |
|||
<input type="hidden" name="roles[{{ index }}]" value="{{ configuration.getUniqueColumnType }}" /> |
|||
{% else %} |
|||
<select name="roles[{{ index }}]" id="roles_{{ index }}" class="form-control"> |
|||
{% for key, role in roles %} |
|||
<option value="{{ key }}" {% if configuredRoles[index] == key %}selected{% endif %} |
|||
label="{{ trans('import.column_'~key) }}">{{ trans('import.column_'~key) }}</option> |
|||
{% endfor %} |
|||
</select> |
|||
{% endif %} |
|||
</td> |
|||
<td> |
|||
<label for="do_mapping_{{ index }}"> |
|||
{# reverse if statement is pretty sloppy but OK. #} |
|||
{% if index == configuration.getUniqueColumnIndex and 'cell' == configuration.getDuplicateDetectionMethod %} |
|||
|
|||
{% else %} |
|||
<input type="checkbox" {% if configuredDoMapping[index] %}checked{% endif %} name="do_mapping[{{ index }}]" id="do_mapping_{{ index }}" value="1"/> |
|||
{% endif %} |
|||
</label> |
|||
</td> |
|||
</tr> |
|||
{% endfor %} |
|||
|
|||
</table> |
|||
<p> |
|||
|
|||
|
|||
</p> |
|||
<div class="row"> |
|||
<div class="col-lg-6"> |
|||
<!-- go back to config --> |
|||
<a href="{{ route('back.config') }}" class="btn btn-secondary">← Go back to configuration</a> |
|||
<br> |
|||
<small class="text-muted">Changes on this page will not be saved.</small> |
|||
<br /><br /> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger btn-sm">Start over</a> |
|||
</div> |
|||
<div class="col-lg-6"> |
|||
<button type="submit" class="float-end btn btn-primary">Submit →</button> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
|
@ -1,37 +0,0 @@ |
|||
{% extends "./layout/default" %} |
|||
{% block content %} |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<h1>{{ mainTitle }}</h1> |
|||
<h2>{{ subTitle }}</h2> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-12"> |
|||
<p> |
|||
<small>If the importer seems stuck, |
|||
<a href="{{ route('flush') }}" class="text-danger">you can reset it</a>.</small> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
<div id="app"> |
|||
<import-status></import-status> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
{% block scripts %} |
|||
<script type="text/javascript"> |
|||
var jobStatusUrl = '{{ route('import.job.status') }}?identifier={{ identifier }}'; |
|||
var jobStartUrl = '{{ route('import.job.start') }}?identifier={{ identifier }}'; |
|||
var jobBackUrl = '{{ jobBackUrl }}'; |
|||
var configDownloadUrl = '{{ route('import.job.configuration.download') }}?identifier={{ identifier }}'; |
|||
var flushUrl = '{{ route('flush') }}'; |
|||
</script> |
|||
<script src="{{ asset('js/app.js') }}?version={{ version }}"></script> |
|||
{% endblock %} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue