commit 87b4695107ec8050f4492525d08c39c962a0faea
Author: Hans Peter Schlaepfer <hanspeter.schlaepfer@revamp-it.ch>
Date:   Fri Jan 4 05:54:46 2019 +0100

    Neues kivitendo Design Aenderungen in templates/webpages/csv_import/..

diff --git a/templates/webpages/csv_import/_deferred_results.html b/templates/webpages/csv_import/_deferred_results.html
index f2061899a..add73710c 100644
--- a/templates/webpages/csv_import/_deferred_results.html
+++ b/templates/webpages/csv_import/_deferred_results.html
@@ -1,6 +1,6 @@
-[%- USE L %]
-[%- USE T8 %]
-[%- USE HTML %]
+[% USE L %]
+[% USE T8 %]
+[% USE HTML %]
 
 <h2>[% 'Import Status' | $T8 %]</h2>
 
diff --git a/templates/webpages/csv_import/_form_banktransactions.html b/templates/webpages/csv_import/_form_banktransactions.html
index d6da1178d..20851a141 100644
--- a/templates/webpages/csv_import/_form_banktransactions.html
+++ b/templates/webpages/csv_import/_form_banktransactions.html
@@ -1,9 +1,9 @@
 [% USE LxERP %]
 [% USE L %]
 <tr>
- <th align="right">[%- LxERP.t8("Existing bank transactions") %]:</th>
- <td colspan="10">
+ <th>[% LxERP.t8("Existing bank transactions") %]:</th>
+ <td>
   [% opts = [ [ 'skip', LxERP.t8('Skip entry') ] , [ 'insert_new', LxERP.t8('Insert new') ] ] %]
-  [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), style = 'width: 300px') %]
- </td>
+  [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), class='wi-normal') %]
+</td>
 </tr>
diff --git a/templates/webpages/csv_import/_form_customers_vendors.html b/templates/webpages/csv_import/_form_customers_vendors.html
index 449856590..2f8443970 100644
--- a/templates/webpages/csv_import/_form_customers_vendors.html
+++ b/templates/webpages/csv_import/_form_customers_vendors.html
@@ -2,24 +2,24 @@
 [% USE L %]
 
 <tr>
- <th align="right">[%- LxERP.t8('Target table') %]:</th>
- <td colspan="10">
+ <th>[%- LxERP.t8('Target table') %]:</th>
+ <td>
   [% opts = [ [ 'customer', LxERP.t8('Customers') ], [ 'vendor', LxERP.t8('Vendors') ] ] %]
   [% L.select_tag('settings.table', opts, default = SELF.profile.get('table'), style = 'width: 300px') %]
  </td>
 </tr>
 
 <tr>
- <th align="right">[%- LxERP.t8('Existing customers/vendors with same customer/vendor number') %]:</th>
- <td colspan="10">
+ <th>[%- LxERP.t8('Existing customers/vendors with same customer/vendor number') %]:</th>
+ <td>
   [% opts = [ [ 'update_existing', LxERP.t8('Update properties of existing entries') ], [ 'insert_new', LxERP.t8('Insert with new customer/vendor number') ], [ 'skip', LxERP.t8('Skip entry') ] ] %]
   [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), style = 'width: 300px') %]
  </td>
 </tr>
 
 <tr>
- <th align="right" valign="top">[%- LxERP.t8('Default taxzone') %]:</th>
- <td colspan="10" valign="top">
+ <th>[%- LxERP.t8('Default taxzone') %]:</th>
+ <td>
   [% L.select_tag('settings.default_taxzone', SELF.all_taxzones, default = SELF.profile.get('default_taxzone'), title_key = 'description', style => 'width: 300px') %]
  </td>
 </tr>
diff --git a/templates/webpages/csv_import/_mapping_item.html b/templates/webpages/csv_import/_mapping_item.html
index 9e5f74cfb..d575924eb 100644
--- a/templates/webpages/csv_import/_mapping_item.html
+++ b/templates/webpages/csv_import/_mapping_item.html
@@ -1,14 +1,15 @@
-[%- USE HTML %]
-[%- USE LxERP %]
-[%- USE L %]
-[%- USE T8 %]
- <tr class=listrow>
-    <td><a class='remove_line interact cursor-pointer'>✘</a></td>
-[%- IF item.from %]
-  <td>[% L.hidden_tag('mappings[+].from', item.from) %][% item.from | html %]</td>
-[%- ELSE %]
-  <td>[% L.input_tag('mappings[+].from', '') %]</td>
-[%- END %]
+[% USE HTML %]
+[% USE LxERP %]
+[% USE L %]
+[% USE T8 %]
+
+<tr>
+  <td><a class='remove_line interact cursor-pointer'>✘</a></td>
+  [% IF item.from %]
+    <td>[% L.hidden_tag('mappings[+].from', item.from) %][% item.from | html %]</td>
+  [% ELSE %]
+    <td>[% L.input_tag('mappings[+].from', '') %]</td>
+  [% END %]
   <td>[% L.select_tag('mappings[].to', SELF.displayable_columns, value_key='name', title_key='name', default=item.to) %]</td>
- </tr>
+</tr>
 
diff --git a/templates/webpages/csv_import/form.html b/templates/webpages/csv_import/form.html
index 75fae984c..55ca2e614 100644
--- a/templates/webpages/csv_import/form.html
+++ b/templates/webpages/csv_import/form.html
@@ -1,346 +1,356 @@
-[%- USE HTML %]
-[%- USE LxERP %]
-[%- USE L %]
-[%- USE T8 %]
- <h1>[% FORM.title %]</h1>
+[% USE HTML %]
+[% USE LxERP %]
+[% USE L %]
+[% USE T8 %]
 
- [%- INCLUDE 'common/flash.html' %]
+<h1>[% FORM.title %]</h1>
 
- <form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
-  [% L.hidden_tag('form_sent', '1') %]
-  [% L.hidden_tag('action', 'CsvImport/dispatch') %]
-  [% L.hidden_tag('profile.type', SELF.profile.type) %]
-  [% L.hidden_tag('tmp_profile_id', SELF.profile.id) %]
+[% INCLUDE 'common/flash.html' %]
 
- [%- IF SELF.profile.get('dont_edit_profile') %]
+<form method="post" action="controller.pl" enctype="multipart/form-data" id="form">
+[% L.hidden_tag('form_sent', '1') %]
+[% L.hidden_tag('action', 'CsvImport/dispatch') %]
+[% L.hidden_tag('profile.type', SELF.profile.type) %]
+[% L.hidden_tag('tmp_profile_id', SELF.profile.id) %]
+
+[% IF SELF.profile.get('dont_edit_profile') %]
   [% L.hidden_tag('force_profile', 1) %]
   [% L.hidden_tag('profile.id', SELF.profile.id) %]
- [%- ELSE %][%# IF SELF.profile.get('dont_edit_profile') %]
-  <h2>[%- LxERP.t8('Import profiles') %]</h2>
 
-  <table>
-   [%- IF SELF.profile.id %]
+[% ELSE %]
+
+<div class="wrapper">
+
+<table class="tbl-horizontal">
+  <caption>[% LxERP.t8('Import profiles') %]</caption>
+  <colgroup><col class="wi-mediumsmall"><col class="wi-wide"></colgroup>
+  <tbody>
+    [% IF SELF.profile.id %]
     <tr>
-     <th align="right">[%- LxERP.t8('Current profile') %]:</th>
-     <td>[%- HTML.escape(SELF.profile.name) %]</td>
+      <th>[% LxERP.t8('Current profile') %]:</th>
+      <td><h3>[% HTML.escape(SELF.profile.name) %]</h3></td>
     </tr>
-   [%- END %][%# IF SELF.profile.id %]
-
-   [%- IF SELF.all_profiles.size %]
+    [% END %]
+    [% IF SELF.all_profiles.size %]
+    <tr>
+      <th>[% LxERP.t8('Existing profiles') %]:</th>
+      <td>
+        [% L.select_tag('profile.id', SELF.all_profiles, title_key = 'name', default = SELF.profile.id, style = 'width: 300px') %] <br>
+        [% L.submit_tag('action_new', LxERP.t8('Load profile')) %]
+        [% L.submit_tag('action_destroy', LxERP.t8('Delete profile'), confirm => LxERP.t8('Do you really want to delete this object?')) %]
+      </td>
+    </tr>
+    [% END %]
     <tr>
-     <th align="right">[%- LxERP.t8('Existing profiles') %]:</th>
-     <td>
-      [% L.select_tag('profile.id', SELF.all_profiles, title_key = 'name', default = SELF.profile.id, style = 'width: 300px') %]
-     </td>
-     <td>
-      [% L.submit_tag('action_new', LxERP.t8('Load profile')) %]
-      [% L.submit_tag('action_destroy', LxERP.t8('Delete profile'), confirm => LxERP.t8('Do you really want to delete this object?')) %]
-     </td>
+      <th>[% LxERP.t8('Save settings as') %]:</th>
+      <td>
+        [% L.input_tag('profile.name', '', style => 'width: 300px') %]<br>
+        [% L.checkbox_tag('profile.is_default', label => LxERP.t8('Make default profile')) %]<br>
+        <div class="buttons below">[% L.submit_tag('action_save', LxERP.t8('Save profile'), class='below') %]</div>
+      </td>
     </tr>
-   [%- END %][%# IF SELF.all_profiles.size %]
+  </tbody>
+</table>
 
-   <tr>
-    <th align="right" valign="top">[%- LxERP.t8('Save settings as') %]:</th>
-    <td valign="top">
-     [% L.input_tag('profile.name', '', style => 'width: 300px') %]
-     <br>
-     [% L.checkbox_tag('profile.is_default', label => LxERP.t8('Make default profile')) %]
-    </td>
-    <td valign="top">[% L.submit_tag('action_save', LxERP.t8('Save profile')) %]</td>
-   </tr>
-  </table>
+</div><!-- /.wrapper -->
 
-  <hr>
+<div class="wrapper">
 
-  <h2>[%- LxERP.t8('Help on column names') %]</h2>
+<div class="col wi-verywide">
 
-  <div class="help_toggle">
-   <a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Show help text") %]</a>
-  </div>
+<h3 class="caption">[% LxERP.t8('Settings') %][% IF SELF.profile.id %] [% 'for Profile' | $T8 %]«[% HTML.escape(SELF.profile.name) %][% END %]</h3>
 
-  <div class="help_toggle" style="display:none">
-   <p><a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Hide help text") %]</a></p>
-
-   [%- IF SELF.worker.is_multiplexed %]
-     <table>
-       <tr class="listheading">
-         [%- FOREACH p = SELF.worker.profile %]
-           <th>[%- p.row_ident %]</th>
-         [%- END %][%# FOREACH SELF.worker.profile %]
-       </tr>
-       <tr style="vertical-align:top">
-         [%- FOREACH p = SELF.worker.profile %]
-           [% SET ri = p.row_ident %]
-         <td>
-           <table>
-             <tr class="listheading">
-               <th>[%- LxERP.t8('Column name') %]</th>
-               <th>[%- LxERP.t8('Meaning') %]</th>
-             </tr>
-
-             [%- FOREACH row = SELF.displayable_columns.$ri %]
-             <tr class="listrow[% loop.count % 2 %]">
-               <td>[%- HTML.escape(row.name) %]</td>
-               <td>[%- HTML.escape(row.description) %]</td>
-             </tr>
-             [%- END %][%# FOREACH SELF.displayable_columns.$ri %]
-           </table>
-         </td>
-         [%- END %][%# FOREACH SELF.worker.profile %]
-       </tr>
-     </table>
-   [%- ELSE %][%# IF SELF.worker.is_multiplexed %]
-     <table>
-       <tr class="listheading">
-         <th>[%- LxERP.t8('Column name') %]</th>
-         <th>[%- LxERP.t8('Meaning') %]</th>
-       </tr>
-
-       [%- FOREACH row = SELF.displayable_columns %]
-       <tr class="listrow[% loop.count % 2 %]">
-         <td>[%- HTML.escape(row.name) %]</td>
-         <td>[%- HTML.escape(row.description) %]</td>
-       </tr>
-       [%- END %][%# FOREACH SELF.displayable_columns %]
-     </table>
-   [%- END %][%# SELF.worker.is_multiplexed %]
-
-[%- IF SELF.type == 'contacts' %]
-   <p>
-    [%- LxERP.t8("You can update existing contacts by providing the 'cp_id' column with their database IDs. Otherwise: ") %]
-    [%- LxERP.t8('At least one of the columns #1, customer, customernumber, customer_gln, vendor, vendornumber, vendor_gln (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'cp_cv_id') %]
-   </p>
-
-[%- ELSIF SELF.type == 'addresses' %]
-   <p>
-    [%- LxERP.t8('At least one of the columns #1, customer, customernumber, customer_gln, vendor, vendornumber, vendor_gln (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'trans_id') %]
-   </p>
-
-[%- ELSIF SELF.type == 'parts' %]
-   <p>
-    [1]:
-    [% LxERP.t8('The three columns "make_X", "model_X" and "lastcost_X" with the same number "X" are used to import vendor part numbers and vendor prices.') %]
-    [% LxERP.t8('The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).') %]
-    [% LxERP.t8('The items are imported accoring do their number "X" regardless of the column order inside the file.') %]
-    [% LxERP.t8('The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.') %]
-   </p>
+[% BLOCK panel_1 %]
+<table class="tbl-horizontal">
+  <colgroup><col class="wi-mediumsmall"><col class="wi-wide"></colgroup>
+  <tbody>
+    <tr>
+      <th>[% LxERP.t8('Number Format') %]:</th>
+      <td>[% L.select_tag('settings.numberformat', ['1.000,00', '1000,00', '1,000.00', '1000.00', "1'000.00"], default = SELF.profile.get('numberformat'), class='wi-normal') %]</td>
+    </tr>
+    <tr>
+      <th>[% LxERP.t8('Charset') %]:</th>
+      <td>[% L.select_tag('settings.charset', SELF.all_charsets, default = SELF.profile.get('charset'), class='wi-normal') %]</td>
+    </tr>
+    <tr>
+      <th>[% LxERP.t8('Separator') %]:</th>
+      <td>
+      [% SET custom_sep_char = SELF.sep_char %]
+      [% FOREACH entry = SELF.all_sep_chars %]
+        [% IF SELF.sep_char == entry.first %] [% SET custom_sep_char = '' %] [% END %]
+        [% L.radio_button_tag('sep_char', value => entry.first, label => entry.last, checked => SELF.sep_char == entry.first) %]<br>
+      [% END %]
+      [% L.radio_button_tag('sep_char', value => 'custom', checked => custom_sep_char != '') %]
+      [% L.input_tag('custom_sep_char', custom_sep_char, size => 3, maxlength => 1) %]
+      </td>
+    </tr>
+    <tr>
+      <th>[% LxERP.t8('Quote character') %]:</th>
+      <td>
+        [% SET custom_quote_char = SELF.quote_char %]
+        [% FOREACH entry = SELF.all_quote_chars %]
+          [% IF SELF.quote_char == entry.first %] [% SET custom_quote_char = '' %] [% END %]
+          [% L.radio_button_tag('quote_char', value => entry.first, label => entry.last, checked => SELF.quote_char == entry.first) %]<br>
+        [% END %]
+        [% L.radio_button_tag('quote_char', value => 'custom', checked => custom_quote_char != '') %]
+        [% L.input_tag('custom_quote_char', custom_quote_char, size => 3, maxlength => 1) %]
+      </td>
+    </tr>
+    <tr>
+      <th>[% LxERP.t8('Escape character') %]:</th>
+      <td>
+        [% SET custom_escape_char = SELF.escape_char %]
+        [% FOREACH entry = SELF.all_escape_chars %]
+          [% IF SELF.escape_char == entry.first %] [% SET custom_escape_char = '' %] [% END %]
+          [% L.radio_button_tag('escape_char', value => entry.first, label => entry.last, checked => SELF.escape_char == entry.first) %]<br>
+        [% END %]
+        [% L.radio_button_tag('escape_char', value => 'custom', checked => custom_escape_char != '') %]
+        [% L.input_tag('custom_escape_char', custom_escape_char, size => 3, maxlength => 1) %]
+      </td>
+    </tr>
+    [% duplicate_fields = SELF.worker.get_duplicate_check_fields() %]
+    [% IF ( duplicate_fields.size ) %]
+      <tr>
+        <th>[% LxERP.t8('Check for duplicates') %]:</th>
+        <td>
+        [% FOREACH key = duplicate_fields.keys %]
+          [% IF (SELF.profile.get('duplicates_'_ key) || (duplicate_fields.$key.default && !FORM.form_sent)) %] [% SET chckd=' checked' %]
+          [% ELSE %] [% SET chckd='' %]
+          [% END %]
+          <input type="checkbox" name="settings.duplicates_[% key | html %]" id="settings.duplicates_[% key | html %]" value="1"[% chckd %]>
+          <label for="settings.duplicates_[% key | html %]">[% duplicate_fields.$key.label | html %]</label><br>
+        [% END %]
+        </td>
+      </tr>
+      <tr>
+        <th></th>
+        <td>
+          [% opts = [ [ 'no_check',  LxERP.t8('Do not check for duplicates') ],
+                      [ 'check_csv', LxERP.t8('Discard duplicate entries in CSV file') ],
+                      [ 'check_db',  LxERP.t8('Discard entries with duplicates in database or CSV file') ] ] %]
+          [% L.select_tag('settings.duplicates', opts, default = SELF.profile.get('duplicates'), style = 'width: 300px') %]
+        </td>
+      </tr>
+    [% END %]
+    [% IF SELF.type == 'parts' %]
+      [% INCLUDE 'csv_import/_form_parts.html' %]
+    [% ELSIF SELF.type == 'customers_vendors' %]
+      [% INCLUDE 'csv_import/_form_customers_vendors.html' %]
+    [% ELSIF SELF.type == 'contacts' %]
+      [% INCLUDE 'csv_import/_form_contacts.html' %]
+    [% ELSIF SELF.type == 'inventories' %]
+      [% INCLUDE 'csv_import/_form_inventories.html' %]
+    [% ELSIF SELF.type == 'orders' %]
+      [% INCLUDE 'csv_import/_form_orders.html' %]
+    [% ELSIF SELF.type == 'ar_transactions' %]
+      [% INCLUDE 'csv_import/_form_artransactions.html' %]
+    [% ELSIF SELF.type == 'bank_transactions' %]
+      [% INCLUDE 'csv_import/_form_banktransactions.html' %]
+    [% END %]
+    <tr>
+      <th>[% LxERP.t8('Preview Mode') %]:</th>
+      <td>
+        [% L.radio_button_tag('settings.full_preview', value=2, checked=SELF.profile.get('full_preview')==2, label=LxERP.t8('Full Preview')) %]
+        [% L.radio_button_tag('settings.full_preview', value=1, checked=SELF.profile.get('full_preview')==1, label=LxERP.t8('Only Warnings and Errors')) %]
+        [% L.radio_button_tag('settings.full_preview', value=0, checked=!SELF.profile.get('full_preview'),   label=LxERP.t8('First 20 Lines')) %]
+      </td>
+    </tr>
+    <tr>
+      <th>[% LxERP.t8('Import file') %]:</th>
+      <td>[% L.input_tag('file', '', type => 'file', accept => '*') %]</td>
+    </tr>
+    [% IF SELF.file.exists %]
+      <tr>
+        <th>[% LxERP.t8('Existing file on server') %]:</th>
+        <td>[% LxERP.t8('Uploaded on #1, size #2 kB', SELF.file.displayable_mtime, LxERP.format_amount(SELF.file.size / 1024, 2)) %]</td>
+      </tr>
+    [% END %]
+  </tbody>
+</table>
+[% END # panel_1 %]
+
+[%
+  INCLUDE 'common/toggle_panel.html'
+  block_name    = 'panel_1'
+  toggle_class  = 'toggle_panel_1'
+  button_closed = LxERP.t8("Show settings")
+  button_open   = LxERP.t8("Hide settings")
+%]
+
+</div><!-- /.col -->
+<div class="col wi-verywide">
+
+[% UNLESS SELF.worker.is_multiplexed %]
+
+<h3>[% 'Mappings (csv_import)' | $T8 %]</h3>
+
+[% BLOCK panel_2 %]
+<p>[% 'These mappings can be used to map heading from non standard csv files to known columns. These will also be saved in profiles, so you can save profiles for every source of formats.' | $T8 %]</p>
+<table id="csv_import_mappings" class="tbl-list">
+  <thead>
+    <tr>
+      <th>[% 'Action' | $T8 %]</th>
+      <th>[% 'Text in CSV File' | $T8 %]</th>
+      <th>[% 'Known Column' | $T8 %]</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr id="mapping_empty" style="display:none">
+      <td colspan="3">[% 'There is nothing here yet (csv_import)' | $T8 %]</td>
+    </tr>
+    [% FOREACH row = SELF.mappings %]
+      [% PROCESS 'csv_import/_mapping_item.html', item=row IF row.from %]
+    [% END %]
+    [% PROCESS 'csv_import/_mapping_item.html', item={} %]
+  </tbody>
+</table>
+<div class="button">
+  <input type="button" id="add_empty_mapping_line" value="[% 'Add empty line (csv_import)' | $T8 %]">
+  <input type="button" id="add_mapping_from_upload" value="[% 'Add headers from last uploaded file (csv_import)' | $T8 %]">
+</div>
+[% END # panel_2 %]
+
+[%
+  #INCLUDE 'common/toggle_panel.html'
+  block_name    = 'panel_2'
+  toggle_class  = 'toggle_panel_2'
+  button_closed = LxERP.t8("Show mappings (csv_import)")
+  button_open   = LxERP.t8("Hide mappings (csv_import)s")
+%]
+
+[% END # UNLESS SELF.worker.is_multiplexed %]
+
+[% IF SELF.deferred %]
+  <div id="results">
+    [% PROCESS 'csv_import/_deferred_results.html' %]
+  </div>
+[% END %]
+
+
+<h3 class="caption">[% LxERP.t8('Help on column names') %]</h3>
+
+[% BLOCK panel_3 %]
+  [% IF SELF.worker.is_multiplexed %]
+    <table>
+      <tbody>
+        <tr>
+          [% FOREACH p = SELF.worker.profile %]
+            <th>[% p.row_ident %]</th>
+          [% END %]
+        </tr>
+        <tr>
+          [% FOREACH p = SELF.worker.profile %]
+            [% SET ri = p.row_ident %]
+            <td>
+              <table class="tbl-list">
+                <thead>
+                  <tr>
+                    <th>[% LxERP.t8('Column name') %]</th>
+                    <th>[% LxERP.t8('Meaning') %]</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  [% FOREACH row = SELF.displayable_columns.$ri %]
+                    <tr>
+                      <td>[% HTML.escape(row.name) %]</td>
+                      <td>[% HTML.escape(row.description) %]</td>
+                    </tr>
+                  [% END %]
+                </tbody>
+              </table>
+            </td>
+          [% END %]
+        </tr>
+      </tbody>
+    </table>
+  [% ELSE %]
+    <table class="tbl-list">
+      <thead>
+        <tr>
+          <th>[% LxERP.t8('Column name') %]</th>
+          <th>[% LxERP.t8('Meaning') %]</th>
+        </tr>
+      </thead>
+      <tbody>
+        [% FOREACH row = SELF.displayable_columns %]
+        <tr>
+          <td>[% HTML.escape(row.name) %]</td>
+          <td>[% HTML.escape(row.description) %]</td>
+        </tr>
+        [% END %]
+      </tbody>
+    </table>
+  [% END %]
+  [% IF SELF.type == 'contacts' %]
+    <p>
+      [% LxERP.t8("You can update existing contacts by providing the 'cp_id' column with their database IDs. Otherwise: ") %]
+      [% LxERP.t8('At least one of the columns #1, customer, customernumber, customer_gln, vendor, vendornumber, vendor_gln (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'cp_cv_id') %]
+    </p>
+  [% ELSIF SELF.type == 'addresses' %]
+    <p>[% LxERP.t8('At least one of the columns #1, customer, customernumber, customer_gln, vendor, vendornumber, vendor_gln (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'trans_id') %]</p>
+  [% ELSIF SELF.type == 'parts' %]
+    <p>
+      [1]:
+      [% LxERP.t8('The three columns "make_X", "model_X" and "lastcost_X" with the same number "X" are used to import vendor part numbers and vendor prices.') %]
+      [% LxERP.t8('The column triplets can occur multiple times with different numbers "X" each time (e.g. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).') %]
+      [% LxERP.t8('The items are imported accoring do their number "X" regardless of the column order inside the file.') %]
+      [% LxERP.t8('The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.') %]
+    </p>
+    <p>
+      [2]:
+      [% LxERP.t8('Onhand only sets the quantity in master data, not in inventory. This is only a legacy info field and will be overwritten as soon as a inventory transfer happens.') %]
+    </p>
+    <p>
+      [3]:
+      [% LxERP.t8("If the article type is set to 'mixed' then a column called 'part_type' or called 'pclass' must be present.") %]
+      [% LxERP.t8("Type can be either 'part', 'service' or 'assembly'.") %]
+      [% LxERP.t8("If column 'pclass' is present the article type is then irrelevant or used as default ") %]
+      [% LxERP.t8("The 'pclass' column has the same abbreviation like a part export. The first letter is for the type Part,Assembly or Service, the second(and third) for Part Classification") %]
+    </p>
+  [% ELSIF SELF.type == 'inventories' %]
+    <p>[% LxERP.t8('One of the columns "qty" or "target_qty" must be given. If "target_qty" is given, the quantity to transfer for each transfer will be calculate, so that the quantity for this part, warehouse and bin will result in the given "target_qty" after each transfer.') %]</p>
+  [% ELSIF SELF.type == 'orders' OR SELF.type == 'ar_transactions' %]
+    <p>
+      [1]:
+      [% LxERP.t8('The column "datatype" must be present and must be at the same position / column in each data set. The values must be the row names (see settings) for order and item data respectively.') %]
+    </p>
     <p>
-    [2]:
-    [% LxERP.t8('Onhand only sets the quantity in master data, not in inventory. This is only a legacy info field and will be overwritten as soon as a inventory transfer happens.') %]
-   </p>
-   <p>
-    [3]:
-    [% LxERP.t8("If the article type is set to 'mixed' then a column called 'part_type' or called 'pclass' must be present.") %]
-    [% LxERP.t8("Type can be either 'part', 'service' or 'assembly'.") %]
-    [%- LxERP.t8("If column 'pclass' is present the article type is then irrelevant or used as default ") %]
-    [% LxERP.t8("The 'pclass' column has the same abbreviation like a part export. The first letter is for the type Part,Assembly or Service, the second(and third) for Part Classification") %]
-   </p>
-
-[%- ELSIF SELF.type == 'inventories' %]
-   <p>
-    [%- LxERP.t8('One of the columns "qty" or "target_qty" must be given. If "target_qty" is given, the quantity to transfer for each transfer will be calculate, so that the quantity for this part, warehouse and bin will result in the given "target_qty" after each transfer.') %]
-   </p>
-
-[%- ELSIF SELF.type == 'orders' OR SELF.type == 'ar_transactions' %]
-   <p>
-    [1]:
-    [% LxERP.t8('The column "datatype" must be present and must be at the same position / column in each data set. The values must be the row names (see settings) for order and item data respectively.') %]
-   </p>
-   <p>
-    [2]:
-    [%- LxERP.t8('Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.') %]<br>
-    [%- LxERP.t8('If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.') %]<br>
-   </p>
-[%- END %][%# IF SELF.type == … %]
-
-   <p>
-    [%- L.submit_tag('action_download_sample', LxERP.t8('Download sample file')) %]
-   </p>
+      [2]:
+      [% LxERP.t8('Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.') %]<br>
+      [% LxERP.t8('If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.') %]<br>
+    </p>
+  [% END %]
+  <p>[% L.submit_tag('action_download_sample', LxERP.t8('Download sample file')) %]</p>
+[% END # panel_3 %]
 
-  </div>
+[%
+  #INCLUDE 'common/toggle_panel.html'
+  block_name    = 'panel_3'
+  toggle_class  = 'toggle_panel_3'
+  button_closed = LxERP.t8("Show help text")
+  button_open   = LxERP.t8("Hide help text")
+%]
 
-  <hr>
 
-  <h2>[%- LxERP.t8('Settings') %]</h2>
+</div><!-- /.col -->
 
-  <div class="settings_toggle"[% UNLESS SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
-   <a href="#" onClick="javascript:$('.settings_toggle').toggle()">[% LxERP.t8("Show settings") %]</a>
-  </div>
+[% END %]<!-- # /ELSE SELF.profile.get('dont_edit_profile') -->
 
-  <div class="settings_toggle"[% IF SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
-   <p><a href="#" onClick="javascript:$('.settings_toggle').toggle()">[% LxERP.t8("Hide settings") %]</a></p>
-
-  <table>
-   <tr>
-    <th align="right">[%- LxERP.t8('Number Format') %]:</th>
-    <td colspan="10">
-     [% L.select_tag('settings.numberformat', ['1.000,00', '1000,00', '1,000.00', '1000.00', "1'000.00"], default = SELF.profile.get('numberformat'), style = 'width: 300px') %]
-    </td>
-   </tr>
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Charset') %]:</th>
-    <td colspan="10">[% L.select_tag('settings.charset', SELF.all_charsets, default = SELF.profile.get('charset'), style = 'width: 300px') %]</td>
-   </tr>
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Separator') %]:</th>
-    [% SET custom_sep_char = SELF.sep_char %]
-    [% FOREACH entry = SELF.all_sep_chars %]
-     <td>
-      [% IF SELF.sep_char == entry.first %] [% SET custom_sep_char = '' %] [%- END %]
-      [% L.radio_button_tag('sep_char', value => entry.first, label => entry.last, checked => SELF.sep_char == entry.first) %]
-     </td>
-    [%- END %][%# FOREACH SELF.all_sep_chars %]
-
-    <td>
-     [% L.radio_button_tag('sep_char', value => 'custom', checked => custom_sep_char != '') %]
-     [% L.input_tag('custom_sep_char', custom_sep_char, size => 3, maxlength => 1) %]
-    </td>
-   </tr>
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Quote character') %]:</th>
-    [% SET custom_quote_char = SELF.quote_char %]
-    [% FOREACH entry = SELF.all_quote_chars %]
-     <td>
-      [% IF SELF.quote_char == entry.first %] [% SET custom_quote_char = '' %] [%- END %]
-      [% L.radio_button_tag('quote_char', value => entry.first, label => entry.last, checked => SELF.quote_char == entry.first) %]
-     </td>
-    [%- END %][%# FOREACH SELF.all_quote_chars %]
-
-    <td>
-     [% L.radio_button_tag('quote_char', value => 'custom', checked => custom_quote_char != '') %]
-     [% L.input_tag('custom_quote_char', custom_quote_char, size => 3, maxlength => 1) %]
-    </td>
-   </tr>
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Escape character') %]:</th>
-    [% SET custom_escape_char = SELF.escape_char %]
-    [% FOREACH entry = SELF.all_escape_chars %]
-     <td>
-      [% IF SELF.escape_char == entry.first %] [% SET custom_escape_char = '' %] [%- END %]
-      [% L.radio_button_tag('escape_char', value => entry.first, label => entry.last, checked => SELF.escape_char == entry.first) %]
-     </td>
-    [%- END %][%# FOREACH SELF.all_escape_chars %]
-
-    <td>
-     [% L.radio_button_tag('escape_char', value => 'custom', checked => custom_escape_char != '') %]
-     [% L.input_tag('custom_escape_char', custom_escape_char, size => 3, maxlength => 1) %]
-    </td>
-   </tr>
-
-   [% duplicate_fields = SELF.worker.get_duplicate_check_fields() %]
-   [% IF ( duplicate_fields.size ) %]
-     <tr>
-       <th align="right">[%- LxERP.t8('Check for duplicates') %]:</th>
-
-       <td colspan=10>
-         [% FOREACH key = duplicate_fields.keys %]
-           <input type="checkbox" name="settings.duplicates_[% key | html %]" id="settings.duplicates_[% key | html %]" value="1"[% IF ( SELF.profile.get('duplicates_'_ key) || (duplicate_fields.$key.default && !FORM.form_sent ) ) %] checked="checked"[% END %]>
-           <label for="settings.duplicates_[% key | html %]">[% duplicate_fields.$key.label | html %]</label>
-         [% END %][%# FOREACH duplicate_fields.keys %]
-       </td>
-     </tr>
-
-     <tr>
-       <th align="right"></th>
-
-       <td colspan=10>
-         [% opts = [ [ 'no_check',  LxERP.t8('Do not check for duplicates') ],
-                     [ 'check_csv', LxERP.t8('Discard duplicate entries in CSV file') ],
-                     [ 'check_db',  LxERP.t8('Discard entries with duplicates in database or CSV file') ] ] %]
-         [% L.select_tag('settings.duplicates', opts, default = SELF.profile.get('duplicates'), style = 'width: 300px') %]
-       </td>
-     </tr>
-   [% END %][%# IF duplicate_fields.size %]
-
-[%- IF SELF.type == 'parts' %]
- [%- INCLUDE 'csv_import/_form_parts.html' %]
-[%- ELSIF SELF.type == 'customers_vendors' %]
- [%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
-[%- ELSIF SELF.type == 'contacts' %]
- [%- INCLUDE 'csv_import/_form_contacts.html' %]
-[%- ELSIF SELF.type == 'inventories' %]
- [%- INCLUDE 'csv_import/_form_inventories.html' %]
-[%- ELSIF SELF.type == 'orders' %]
- [%- INCLUDE 'csv_import/_form_orders.html' %]
-[%- ELSIF SELF.type == 'ar_transactions' %]
- [%- INCLUDE 'csv_import/_form_artransactions.html' %]
-[%- ELSIF SELF.type == 'bank_transactions' %]
- [%- INCLUDE 'csv_import/_form_banktransactions.html' %]
-[%- END %]
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Preview Mode') %]:</th>
-    <td colspan="10">
-      [% L.radio_button_tag('settings.full_preview', value=2, checked=SELF.profile.get('full_preview')==2, label=LxERP.t8('Full Preview')) %]
-      [% L.radio_button_tag('settings.full_preview', value=1, checked=SELF.profile.get('full_preview')==1, label=LxERP.t8('Only Warnings and Errors')) %]
-      [% L.radio_button_tag('settings.full_preview', value=0, checked=!SELF.profile.get('full_preview'),   label=LxERP.t8('First 20 Lines')) %]
-    </td>
-   </tr>
-
-   <tr>
-    <th align="right">[%- LxERP.t8('Import file') %]:</th>
-    <td colspan="10">[% L.input_tag('file', '', type => 'file', accept => '*') %]</td>
-   </tr>
-
-   [%- IF SELF.file.exists %]
-    <tr>
-     <th align="right">[%- LxERP.t8('Existing file on server') %]:</th>
-     <td colspan="10">[%- LxERP.t8('Uploaded on #1, size #2 kB', SELF.file.displayable_mtime, LxERP.format_amount(SELF.file.size / 1024, 2)) %]</td>
-    </tr>
-   [%- END %][%# IF SELF.file.exists %]
 
-  </table>
 
-  </div>
-  <hr>
+</form>
 
-[%- UNLESS SELF.worker.is_multiplexed %]
-  <h2>[% 'Mappings (csv_import)' | $T8 %]</h2>
+<div class="col wi-wide">
 
-  <div class="mappings_toggle"[% UNLESS SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
-   <a href="#" onClick="javascript:$('.mappings_toggle').toggle()">[% LxERP.t8("Show mappings (csv_import)") %]</a>
-  </div>
-  <div class="mappings_toggle"[% IF SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
-   <p><a href="#" onClick="javascript:$('.mappings_toggle').toggle()">[% LxERP.t8("Hide mappings (csv_import)") %]</a></p>
-
-    <p>[% 'These mappings can be used to map heading from non standard csv files to known columns. These will also be saved in profiles, so you can save profiles for every source of formats.' | $T8 %]</p>
-
-  <table id="csv_import_mappings">
-   <tr class=listheading>
-    <th></th>
-    <th>[% 'Text in CSV File' | $T8 %]</th>
-    <th>[% 'Known Column' | $T8 %]</th>
-   </tr>
-   <tr id='mapping_empty' style='display:none'>
-    <td colspan=3>[% 'There is nothing here yet (csv_import)' | $T8 %]</td>
-   </tr>
-[%- FOREACH row = SELF.mappings %]
-   [% PROCESS 'csv_import/_mapping_item.html', item=row IF row.from %]
-[%- END %][%# FOREACH SELF.mappings %]
-   [% PROCESS 'csv_import/_mapping_item.html', item={} %]
-  </table>
-
-  <input type=button id='add_empty_mapping_line' value='[% 'Add empty line (csv_import)' | $T8 %]'>
-  <input type=button id='add_mapping_from_upload' value='[% 'Add headers from last uploaded file (csv_import)' | $T8 %]'>
 
-  </div>
-  <hr>
-[%- END %][%# UNLESS SELF.worker.is_multiplexed %]
-[%- END %][%# IF SELF.profile.get('dont_edit_profile') %]
- </form>
 
- <div id='results'>
- [%- IF SELF.deferred %]
-   [%- PROCESS 'csv_import/_deferred_results.html' %]
- [%- END %][%# IF SELF.deferred %]
- </div>
+
+</div><!-- /.col -->
+
+</div><!-- /.wrapper -->
 
 
- <script type="text/javascript">
+<script type="text/javascript">
   <!--
     $(document).ready(function() {
       $('#action_save').click(function() {
@@ -358,4 +368,4 @@
       $('#csv_import_mappings').on('click', '.remove_line', function(){ $(this).closest('tr').remove(); if (1==$('#csv_import_mappings tr:visible').length) $('#mapping_empty').show() });
     });
     -->
- </script>
+</script>
diff --git a/templates/webpages/csv_import/report.html b/templates/webpages/csv_import/report.html
index 77e69601e..f188fc05d 100644
--- a/templates/webpages/csv_import/report.html
+++ b/templates/webpages/csv_import/report.html
@@ -2,65 +2,65 @@
 [% USE LxERP %]
 [% USE L %]
  <h2>
- [%- IF SELF.report.test_mode %]
-  [%- LxERP.t8('Import preview') %]
- [%- ELSE %]
-  [%- LxERP.t8('Import result') %]
- [%- END %]
+ [% IF SELF.report.test_mode %]
+  [% LxERP.t8('Import preview') %]
+ [% ELSE %]
+  [% LxERP.t8('Import result') %]
+ [% END %]
 </h2>
 
-[%- IF SELF.report.test_mode %]
+[% IF SELF.report.test_mode %]
  <p>
   [% LxERP.t8("The following is only a preview.") %]
   [% LxERP.t8("No entries have been imported yet.") %]
  </p>
-[%- END %]
+[% END %]
 
-[%- PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
+[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
  <table>
 
-[%- SET max_col = 0 %]
-[%- FOREACH rownum = SELF.display_rows %]
-  [%- SET max_col = max_col > SELF.report_rows.${rownum}.size ? max_col : SELF.report_rows.${rownum}.size  %]
-  [%- LAST IF rownum >= SELF.report_numheaders %]
-[%- END %]
+[% SET max_col = 0 %]
+[% FOREACH rownum = SELF.display_rows %]
+  [% SET max_col = max_col > SELF.report_rows.${rownum}.size ? max_col : SELF.report_rows.${rownum}.size  %]
+  [% LAST IF rownum >= SELF.report_numheaders %]
+[% END %]
 
-[%- FOREACH rownum = SELF.display_rows %]
- [%- SET to_pad = max_col - SELF.report_rows.${rownum}.size %]
- [%- IF rownum < SELF.report_numheaders %]
-  <tr class="listheading">
-  [%- FOREACH value = SELF.report_rows.${rownum} %]
+[% FOREACH rownum = SELF.display_rows %]
+ [% SET to_pad = max_col - SELF.report_rows.${rownum}.size %]
+ [% IF rownum < SELF.report_numheaders %]
+  <tr>
+  [% FOREACH value = SELF.report_rows.${rownum} %]
    <th>[% value | html %]</th>
-  [%- END %]
-   [%- IF to_pad -%]<th style="text-align:center" colspan="[%- to_pad -%]">-</th>[%- END -%]
-   <th>[%- LxERP.t8('Notes') %]</th>
+  [% END %]
+   [% IF to_pad %]<th style="text-align:center" colspan="[% to_pad %]">-</th>[% END %]
+   <th>[% LxERP.t8('Notes') %]</th>
   </tr>
- [%- ELSE %]
+ [% ELSE %]
   [% csv_import_report_errors = SELF.report_status.${rownum}.errors %]
   <tr class="listrow[% IF csv_import_report_errors && csv_import_report_errors.size %]_error[% END %][% (loop.count + SELF.report_numheaders) % 2 %]">
-  [%- FOREACH value = SELF.report_rows.${rownum} %]
-   <td>[%- value | html  %]</td>
-  [%- END %]
-   [%- IF to_pad -%]<td align="center" colspan="[%- to_pad -%]">-</td>[%- END -%]
+  [% FOREACH value = SELF.report_rows.${rownum} %]
+   <td>[% value | html  %]</td>
+  [% END %]
+   [% IF to_pad %]<td colspan="[% to_pad %]">-</td>[% END %]
    <td>
-    [%- FOREACH error = csv_import_report_errors %][%- error | html %][% UNLESS loop.last %]<br>[%- END %][%- END %]
-    [%- FOREACH info  = SELF.report_status.${rownum}.information %][% IF rownum >= SELF.report_numheaders || csv_import_report_errors.size %]<br>[%- END %][%- info | html %][%- END %]
+    [% FOREACH error = csv_import_report_errors %][% error | html %][% UNLESS loop.last %]<br>[% END %][% END %]
+    [% FOREACH info  = SELF.report_status.${rownum}.information %][% IF rownum >= SELF.report_numheaders || csv_import_report_errors.size %]<br>[% END %][% info | html %][% END %]
    </td>
   </tr>
- [%- END %]
-[%- END %]
+ [% END %]
+[% END %]
 
  </table>
 
-[%- PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
+[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
 
 <script type='text/javascript'>
   $(function(){
-    [%- IF SELF.reporterror %]
+    [% IF SELF.reporterror %]
       kivi.clear_flash('info',0);
       kivi.display_flash('error','[% SELF.reporterror %]',0);
-    [%- ELSIF SELF.report.test_mode %]
+    [% ELSIF SELF.report.test_mode %]
       kivi.ActionBar.Action('#action_import').enable();
-    [%- END %]
+    [% END %]
   });
 </script>
