CSV-Import: Test-Button unnötig bei direktem Import
[kivitendo-erp.git] / templates / webpages / csv_import / form.html
1 [%- USE HTML %]
2 [%- USE LxERP %]
3 [%- USE L %]
4 [%- USE T8 %]
5  <h1>[% FORM.title %]</h1>
6
7  [%- INCLUDE 'common/flash.html' %]
8
9  <form method="post" action="controller.pl" enctype="multipart/form-data">
10   [% L.hidden_tag('form_sent', '1') %]
11   [% L.hidden_tag('action', 'CsvImport/dispatch') %]
12   [% L.hidden_tag('profile.type', SELF.profile.type) %]
13
14  [%- IF SELF.profile.get('dont_edit_profile') %]
15   [% L.hidden_tag('force_profile', 1) %]
16   [% L.hidden_tag('profile.id', SELF.profile.id) %]
17  [%- ELSE %]
18   <h2>[%- LxERP.t8('Import profiles') %]</h2>
19
20   <table>
21    [%- IF SELF.profile.id %]
22     <tr>
23      <th align="right">[%- LxERP.t8('Current profile') %]:</th>
24      <td>[%- HTML.escape(SELF.profile.name) %]</td>
25     </tr>
26    [%- END %]
27
28    [%- IF SELF.all_profiles.size %]
29     <tr>
30      <th align="right">[%- LxERP.t8('Existing profiles') %]:</th>
31      <td>
32       [% L.select_tag('profile.id', SELF.all_profiles, title_key = 'name', default = SELF.profile.id, style = 'width: 300px') %]
33      </td>
34      <td>
35       [% L.submit_tag('action_new', LxERP.t8('Load profile')) %]
36       [% L.submit_tag('action_destroy', LxERP.t8('Delete profile'), confirm => LxERP.t8('Do you really want to delete this object?')) %]
37      </td>
38     </tr>
39    [%- END %]
40
41    <tr>
42     <th align="right" valign="top">[%- LxERP.t8('Save settings as') %]:</th>
43     <td valign="top">
44      [% L.input_tag('profile.name', '', style => 'width: 300px') %]
45      <br>
46      [% L.checkbox_tag('profile.is_default', label => LxERP.t8('Make default profile')) %]
47     </td>
48     <td valign="top">[% L.submit_tag('action_save', LxERP.t8('Save profile')) %]</td>
49    </tr>
50   </table>
51
52   <hr>
53
54   <h2>[%- LxERP.t8('Help on column names') %]</h2>
55
56   <div class="help_toggle">
57    <a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Show help text") %]</a>
58   </div>
59
60   <div class="help_toggle" style="display:none">
61    <p><a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Hide help text") %]</a></p>
62
63    [%- IF SELF.worker.is_multiplexed %]
64      <table>
65        <tr class="listheading">
66          [%- FOREACH p = SELF.worker.profile %]
67            <th>[%- p.row_ident %]</th>
68          [%- END %]
69        </tr>
70        <tr class="listrow[% loop.count % 2 %]">
71          [%- FOREACH p = SELF.worker.profile %]
72            [% SET ri = p.row_ident %]
73          <td>
74            <table>
75              <tr class="listheading">
76                <th>[%- LxERP.t8('Column name') %]</th>
77                <th>[%- LxERP.t8('Meaning') %]</th>
78              </tr>
79
80              [%- FOREACH row = SELF.displayable_columns.$ri %]
81              <tr class="listrow[% loop.count % 2 %]">
82                <td>[%- HTML.escape(row.name) %]</td>
83                <td>[%- HTML.escape(row.description) %]</td>
84              </tr>
85              [%- END %]
86            </table>
87          </td>
88          [%- END %]
89        </tr>
90      </table>
91    [%- ELSE %]
92      <table>
93        <tr class="listheading">
94          <th>[%- LxERP.t8('Column name') %]</th>
95          <th>[%- LxERP.t8('Meaning') %]</th>
96        </tr>
97
98        [%- FOREACH row = SELF.displayable_columns %]
99        <tr class="listrow[% loop.count % 2 %]">
100          <td>[%- HTML.escape(row.name) %]</td>
101          <td>[%- HTML.escape(row.description) %]</td>
102        </tr>
103        [%- END %]
104      </table>
105    [%- END %]
106
107 [%- IF SELF.type == 'contacts' %]
108    <p>
109     [%- LxERP.t8("You can update existing contacts by providing the 'cp_id' column with their database IDs. Otherwise: ") %]
110     [%- LxERP.t8('At least one of the columns #1, customer, customernumber, vendor, vendornumber (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'cp_cv_id') %]
111    </p>
112
113 [%- ELSIF SELF.type == 'addresses' %]
114    <p>
115     [%- LxERP.t8('At least one of the columns #1, customer, customernumber, vendor, vendornumber (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'trans_id') %]
116    </p>
117
118 [%- ELSIF SELF.type == 'parts' %]
119    <p>
120     [1]:
121     [% 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.') %]
122     [% 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).') %]
123     [% LxERP.t8('The items are imported accoring do their number "X" regardless of the column order inside the file.') %]
124     [% LxERP.t8('The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.') %]
125    </p>
126     <p>
127     [2]:
128     [% 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.') %]
129    </p>
130    <p>
131     [3]:
132     [% LxERP.t8("If the article type is set to 'mixed' then a column called 'type' must be present.") %]
133     [% LxERP.t8("Type can be either 'part', 'service' or 'assembly'.") %]
134     [% LxERP.t8("Assemblies can not be imported (yet). But the type column is used for sanity checks on price updates in order to prevent that articles with the wrong type will be updated.") %]
135    </p>
136
137 [%- ELSIF SELF.type == 'inventories' %]
138    <p>
139     [%- 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.') %]
140    </p>
141
142 [%- ELSIF SELF.type == 'orders' OR SELF.type == 'ar_transactions' %]
143    <p>
144     [1]:
145     [% 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.') %]
146    </p>
147    <p>
148     [2]:
149     [%- LxERP.t8('Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.') %]<br>
150     [%- LxERP.t8('If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.') %]<br>
151    </p>
152 [%- END %]
153
154    <p>
155     [%- L.submit_tag('action_download_sample', LxERP.t8('Download sample file')) %]
156    </p>
157
158   </div>
159
160   <hr>
161
162   <h2>[%- LxERP.t8('Settings') %]</h2>
163
164   <div class="settings_toggle"[% UNLESS SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
165    <a href="#" onClick="javascript:$('.settings_toggle').toggle()">[% LxERP.t8("Show settings") %]</a>
166   </div>
167
168   <div class="settings_toggle"[% IF SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
169    <p><a href="#" onClick="javascript:$('.settings_toggle').toggle()">[% LxERP.t8("Hide settings") %]</a></p>
170
171   <table>
172    <tr>
173     <th align="right">[%- LxERP.t8('Number Format') %]:</th>
174     <td colspan="10">
175      [% 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') %]
176     </td>
177    </tr>
178
179    <tr>
180     <th align="right">[%- LxERP.t8('Charset') %]:</th>
181     <td colspan="10">[% L.select_tag('settings.charset', SELF.all_charsets, default = SELF.profile.get('charset'), style = 'width: 300px') %]</td>
182    </tr>
183
184    <tr>
185     <th align="right">[%- LxERP.t8('Separator') %]:</th>
186     [% SET custom_sep_char = SELF.sep_char %]
187     [% FOREACH entry = SELF.all_sep_chars %]
188      <td>
189       [% IF SELF.sep_char == entry.first %] [% SET custom_sep_char = '' %] [%- END %]
190       [% L.radio_button_tag('sep_char', value => entry.first, label => entry.last, checked => SELF.sep_char == entry.first) %]
191      </td>
192     [%- END %]
193
194     <td>
195      [% L.radio_button_tag('sep_char', value => 'custom', checked => custom_sep_char != '') %]
196      [% L.input_tag('custom_sep_char', custom_sep_char, size => 3, maxlength => 1) %]
197     </td>
198    </tr>
199
200    <tr>
201     <th align="right">[%- LxERP.t8('Quote character') %]:</th>
202     [% SET custom_quote_char = SELF.quote_char %]
203     [% FOREACH entry = SELF.all_quote_chars %]
204      <td>
205       [% IF SELF.quote_char == entry.first %] [% SET custom_quote_char = '' %] [%- END %]
206       [% L.radio_button_tag('quote_char', value => entry.first, label => entry.last, checked => SELF.quote_char == entry.first) %]
207      </td>
208     [%- END %]
209
210     <td>
211      [% L.radio_button_tag('quote_char', value => 'custom', checked => custom_quote_char != '') %]
212      [% L.input_tag('custom_quote_char', custom_quote_char, size => 3, maxlength => 1) %]
213     </td>
214    </tr>
215
216    <tr>
217     <th align="right">[%- LxERP.t8('Escape character') %]:</th>
218     [% SET custom_escape_char = SELF.escape_char %]
219     [% FOREACH entry = SELF.all_escape_chars %]
220      <td>
221       [% IF SELF.escape_char == entry.first %] [% SET custom_escape_char = '' %] [%- END %]
222       [% L.radio_button_tag('escape_char', value => entry.first, label => entry.last, checked => SELF.escape_char == entry.first) %]
223      </td>
224     [%- END %]
225
226     <td>
227      [% L.radio_button_tag('escape_char', value => 'custom', checked => custom_escape_char != '') %]
228      [% L.input_tag('custom_escape_char', custom_escape_char, size => 3, maxlength => 1) %]
229     </td>
230    </tr>
231
232    [% duplicate_fields = SELF.worker.get_duplicate_check_fields() %]
233    [% IF ( duplicate_fields.size ) %]
234      <tr>
235        <th align="right">[%- LxERP.t8('Check for duplicates') %]:</th>
236
237        <td colspan=10>
238          [% FOREACH key = duplicate_fields.keys %]
239            <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 %]>
240            <label for="settings.duplicates_[% key | html %]">[% duplicate_fields.$key.label | html %]</label>
241          [% END %]
242        </td>
243      </tr>
244
245      <tr>
246        <th align="right"></th>
247
248        <td colspan=10>
249          [% opts = [ [ 'no_check',  LxERP.t8('Do not check for duplicates') ],
250                      [ 'check_csv', LxERP.t8('Discard duplicate entries in CSV file') ],
251                      [ 'check_db',  LxERP.t8('Discard entries with duplicates in database or CSV file') ] ] %]
252          [% L.select_tag('settings.duplicates', opts, default = SELF.profile.get('duplicates'), style = 'width: 300px') %]
253        </td>
254      </tr>
255    [% END %]
256
257 [%- IF SELF.type == 'parts' %]
258  [%- INCLUDE 'csv_import/_form_parts.html' %]
259 [%- ELSIF SELF.type == 'customers_vendors' %]
260  [%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
261 [%- ELSIF SELF.type == 'contacts' %]
262  [%- INCLUDE 'csv_import/_form_contacts.html' %]
263 [%- ELSIF SELF.type == 'inventories' %]
264  [%- INCLUDE 'csv_import/_form_inventories.html' %]
265 [%- ELSIF SELF.type == 'orders' %]
266  [%- INCLUDE 'csv_import/_form_orders.html' %]
267 [%- ELSIF SELF.type == 'ar_transactions' %]
268  [%- INCLUDE 'csv_import/_form_artransactions.html' %]
269 [%- ELSIF SELF.type == 'bank_transactions' %]
270  [%- INCLUDE 'csv_import/_form_banktransactions.html' %]
271 [%- END %]
272
273    <tr>
274     <th align="right">[%- LxERP.t8('Preview Mode') %]:</th>
275     <td colspan="10">
276       [% L.radio_button_tag('settings.full_preview', value=2, checked=SELF.profile.get('full_preview')==2, label=LxERP.t8('Full Preview')) %]
277       [% L.radio_button_tag('settings.full_preview', value=1, checked=SELF.profile.get('full_preview')==1, label=LxERP.t8('Only Warnings and Errors')) %]
278       [% L.radio_button_tag('settings.full_preview', value=0, checked=!SELF.profile.get('full_preview'),   label=LxERP.t8('First 20 Lines')) %]
279     </td>
280    </tr>
281
282    <tr>
283     <th align="right">[%- LxERP.t8('Import file') %]:</th>
284     <td colspan="10">[% L.input_tag('file', '', type => 'file', accept => '*') %]</td>
285    </tr>
286
287    [%- IF SELF.file.exists %]
288     <tr>
289      <th align="right">[%- LxERP.t8('Existing file on server') %]:</th>
290      <td colspan="10">[%- LxERP.t8('Uploaded on #1, size #2 kB', SELF.file.displayable_mtime, LxERP.format_amount(SELF.file.size / 1024, 2)) %]</td>
291     </tr>
292    [%- END %]
293
294   </table>
295
296   </div>
297   <hr>
298
299 [%- UNLESS SELF.worker.is_multiplexed %]
300   <h2>[% 'Mappings (csv_import)' | $T8 %]</h2>
301
302   <div class="mappings_toggle"[% UNLESS SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
303    <a href="#" onClick="javascript:$('.mappings_toggle').toggle()">[% LxERP.t8("Show mappings (csv_import)") %]</a>
304   </div>
305   <div class="mappings_toggle"[% IF SELF.deferred || SELF.import_status %] style="display:none"[% END %]>
306    <p><a href="#" onClick="javascript:$('.mappings_toggle').toggle()">[% LxERP.t8("Hide mappings (csv_import)") %]</a></p>
307
308     <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>
309
310   <table id="csv_import_mappings">
311    <tr class=listheading>
312     <th></th>
313     <th>[% 'Text in CSV File' | $T8 %]</th>
314     <th>[% 'Known Column' | $T8 %]</th>
315    </tr>
316    <tr id='mapping_empty' style='display:none'>
317     <td colspan=3>[% 'There is nothing here yet (csv_import)' | $T8 %]</td>
318    </tr>
319 [%- FOREACH row = SELF.mappings %]
320    [% PROCESS 'csv_import/_mapping_item.html', item=row IF row.from %]
321 [%- END %]
322    [% PROCESS 'csv_import/_mapping_item.html', item={} %]
323   </table>
324
325   <input type=button id='add_empty_mapping_line' value='[% 'Add empty line (csv_import)' | $T8 %]'>
326   <input type=button id='add_mapping_from_upload' value='[% 'Add headers from last uploaded file (csv_import)' | $T8 %]'>
327
328   </div>
329   <hr>
330 [%- END %]
331   [% L.submit_tag('action_test', LxERP.t8('Test and preview')) %]
332 [%- END %]
333   [% L.submit_tag('action_import', LxERP.t8('Import'), style='display:none') %]
334
335  </form>
336
337  <div id='results'>
338  [%- IF SELF.deferred %]
339    [%- PROCESS 'csv_import/_deferred_results.html' %]
340  [%- ELSIF SELF.import_status %]
341    [%- PROCESS 'csv_import/_results.html' %]
342  [%- END %]
343  </div>
344
345
346  <script type="text/javascript">
347   <!--
348     $(document).ready(function() {
349       $('#action_save').click(function() {
350         if ($('#profile_name').val() != '')
351           return true;
352         alert('[% LxERP.t8('Please enter a profile name.') %]');
353         return false;
354       });
355       $('#add_empty_mapping_line').click(function(){
356         $.get('controller.pl', { action: 'CsvImport/add_empty_mapping_line', 'profile.type': $('#profile_type').val() }, kivi.eval_json_result);
357       });
358       $('#add_mapping_from_upload').click(function(){
359         $.get('controller.pl?action_add_mapping_from_upload=1', $('form').serialize() , kivi.eval_json_result);
360       });
361       $('#csv_import_mappings').on('click', '.remove_line', function(){ $(this).closest('tr').remove(); if (1==$('#csv_import_mappings tr:visible').length) $('#mapping_empty').show() });
362     });
363     -->
364  </script>