Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 1adadc30

Von Bernd Bleßmann vor fast 10 Jahren hinzugefügt

  • ID 1adadc3007e658f3c0723701e79f42569e71df9f
  • Vorgänger b1c40d4d
  • Nachfolger 11c945f2

CSV-Import für Lagerbewegung, bzw. Lagerbestand.

Conflicts:

SL/Controller/CsvImport.pm
locale/de/all
locale/en/all

Übernahme aus Kundenprojekt.

Unterschiede anzeigen:

SL/Controller/CsvImport.pm
13 13
use SL::Controller::CsvImport::Contact;
14 14
use SL::Controller::CsvImport::CustomerVendor;
15 15
use SL::Controller::CsvImport::Part;
16
use SL::Controller::CsvImport::Inventory;
16 17
use SL::Controller::CsvImport::Shipto;
17 18
use SL::Controller::CsvImport::Project;
18 19
use SL::Controller::CsvImport::Order;
......
221 222
sub check_type {
222 223
  my ($self) = @_;
223 224

  
224
  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts customers_vendors addresses contacts projects orders);
225
  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts inventories customers_vendors addresses contacts projects orders);
225 226
  $self->type($::form->{profile}->{type});
226 227
}
227 228

  
......
263 264
            : $self->type eq 'addresses'         ? $::locale->text('CSV import: shipping addresses')
264 265
            : $self->type eq 'contacts'          ? $::locale->text('CSV import: contacts')
265 266
            : $self->type eq 'parts'             ? $::locale->text('CSV import: parts and services')
267
            : $self->type eq 'inventories'       ? $::locale->text('CSV import: inventories')
266 268
            : $self->type eq 'projects'          ? $::locale->text('CSV import: projects')
267 269
            : $self->type eq 'orders'            ? $::locale->text('CSV import: orders')
268 270
            : die;
......
608 610
       : $self->{type} eq 'contacts'          ? SL::Controller::CsvImport::Contact->new(@args)
609 611
       : $self->{type} eq 'addresses'         ? SL::Controller::CsvImport::Shipto->new(@args)
610 612
       : $self->{type} eq 'parts'             ? SL::Controller::CsvImport::Part->new(@args)
613
       : $self->{type} eq 'inventories'       ? SL::Controller::CsvImport::Inventory->new(@args)
611 614
       : $self->{type} eq 'projects'          ? SL::Controller::CsvImport::Project->new(@args)
612 615
       : $self->{type} eq 'orders'            ? SL::Controller::CsvImport::Order->new(@args)
613 616
       :                                        die "Program logic error";
SL/Controller/CsvImport/Inventory.pm
1
package SL::Controller::CsvImport::Inventory;
2

  
3

  
4
use strict;
5

  
6
use SL::Helper::Csv;
7
use SL::Helper::DateTime;
8

  
9
use SL::DBUtils;
10
use SL::DB::Inventory;
11
use SL::DB::Part;
12
use SL::DB::Warehouse;
13
use SL::DB::Bin;
14
use SL::DB::TransferType;
15
use SL::DB::Employee;
16

  
17
use parent qw(SL::Controller::CsvImport::Base);
18

  
19

  
20
use Rose::Object::MakeMethods::Generic
21
(
22
 'scalar --get_set_init' => [ qw(settings parts_by warehouses_by bins_by) ],
23
);
24

  
25

  
26
sub init_class {
27
  my ($self) = @_;
28
  $self->class('SL::DB::Inventory');
29
}
30

  
31
sub init_profile {
32
  my ($self) = @_;
33

  
34
  my $profile = $self->SUPER::init_profile;
35
  delete @{$profile}{qw(trans_id oe_id orderitems_id bestbefore trans_type_id project_id)};
36

  
37
  return $profile;
38
}
39

  
40
sub init_settings {
41
  my ($self) = @_;
42

  
43
  return { map { ( $_ => $self->controller->profile->get($_) ) } qw(warehouse apply_warehouse
44
                                                                    bin       apply_bin
45
                                                                    comment   apply_comment) };
46
}
47

  
48
sub init_parts_by {
49
  my ($self) = @_;
50

  
51
  my $all_parts = SL::DB::Manager::Part->get_all;
52
  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_parts } } ) } qw(id partnumber ean description) };
53
}
54

  
55
sub init_warehouses_by {
56
  my ($self) = @_;
57

  
58
  my $all_warehouses = SL::DB::Manager::Warehouse->get_all(query => [ or => [ invalid => 0, invalid => undef ]]);
59
  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_warehouses } } ) } qw(id description) };
60
}
61

  
62
sub init_bins_by {
63
  my ($self) = @_;
64

  
65
  my $all_bins = SL::DB::Manager::Bin->get_all();
66
  my $bins_by;
67
  $bins_by->{'wh_id+id'}          = { map { ( $_->warehouse_id . '+' . $_->id          => $_ ) } @{ $all_bins } };
68
  $bins_by->{'wh_id+description'} = { map { ( $_->warehouse_id . '+' . $_->description => $_ ) } @{ $all_bins } };
69

  
70
  return $bins_by;
71
}
72

  
73
sub check_objects {
74
  my ($self) = @_;
75

  
76
  $self->controller->track_progress(phase => 'building data', progress => 0);
77

  
78
  my $i;
79
  my $num_data = scalar @{ $self->controller->data };
80
  foreach my $entry (@{ $self->controller->data }) {
81
    $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
82

  
83
    $self->check_warehouse($entry);
84
    $self->check_bin($entry);
85
    $self->check_part($entry);
86
    $self->check_qty($entry)            unless scalar @{ $entry->{errors} };
87
    $self->handle_comment($entry);
88
    $self->handle_employee($entry);
89
    $self->handle_transfer_type($entry) unless scalar @{ $entry->{errors} };
90
    $self->handle_shippingdate($entry);
91
  } continue {
92
    $i++;
93
  }
94

  
95
  $self->add_info_columns(qw(warehouse bin partnumber employee target_qty));
96
}
97

  
98
sub setup_displayable_columns {
99
  my ($self) = @_;
100

  
101
  $self->SUPER::setup_displayable_columns;
102

  
103
  $self->add_displayable_columns({ name => 'bin',          description => $::locale->text('Bin')                     },
104
                                 { name => 'bin_id',       description => $::locale->text('Bin (database ID)')       },
105
                                 { name => 'chargenumber', description => $::locale->text('Charge number')           },
106
                                 { name => 'comment',      description => $::locale->text('Comment')                 },
107
                                 { name => 'employee_id',  description => $::locale->text('Employee (database ID)')  },
108
                                 { name => 'partnumber',   description => $::locale->text('Part Number')             },
109
                                 { name => 'parts_id',     description => $::locale->text('Part (database ID)')      },
110
                                 { name => 'qty',          description => $::locale->text('qty (to transfer)')       },
111
                                 { name => 'shippingdate', description => $::locale->text('Shipping date')           },
112
                                 { name => 'target_qty',   description => $::locale->text('Target Qty')              },
113
                                 { name => 'warehouse',    description => $::locale->text('Warehouse')               },
114
                                 { name => 'warehouse_id', description => $::locale->text('Warehouse (database ID)') },
115
                                );
116
}
117

  
118
sub check_warehouse {
119
  my ($self, $entry) = @_;
120

  
121
  my $object = $entry->{object};
122

  
123
  # If warehouse from front-end is enforced for all transfers, use this, if valid.
124
  if ($self->settings->{apply_warehouse} eq 'all') {
125
    $object->warehouse_id(undef);
126
    my $wh = $self->warehouses_by->{description}->{ $self->settings->{warehouse} };
127
    if (!$wh) {
128
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid warehouse');
129
      return 0;
130
    }
131

  
132
    $object->warehouse_id($wh->id);
133
  }
134

  
135
  # If warehouse from front-end is enforced for transfers with missing warehouse, use this, if valid.
136
  if (    $self->settings->{apply_warehouse} eq 'missing'
137
       && ! $object->warehouse_id
138
       && ! $entry->{raw_data}->{warehouse} ) {
139
    my $wh = $self->warehouses_by->{description}->{ $self->settings->{warehouse} };
140
    if (!$wh) {
141
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid warehouse');
142
      return 0;
143
    }
144

  
145
    $object->warehouse_id($wh->id);
146
  }
147

  
148
  # Check wether or not warehouse ID is valid.
149
  if ($object->warehouse_id && !$self->warehouses_by->{id}->{ $object->warehouse_id }) {
150
    push @{ $entry->{errors} }, $::locale->text('Error: Invalid warehouse');
151
    return 0;
152
  }
153

  
154
  # Map description to ID if given.
155
  if (!$object->warehouse_id && $entry->{raw_data}->{warehouse}) {
156
    my $wh = $self->warehouses_by->{description}->{ $entry->{raw_data}->{warehouse} };
157
    if (!$wh) {
158
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid warehouse');
159
      return 0;
160
    }
161

  
162
    $object->warehouse_id($wh->id);
163
  }
164

  
165
  if ($object->warehouse_id) {
166
    $entry->{info_data}->{warehouse} = $self->warehouses_by->{id}->{ $object->warehouse_id }->description;
167
  } else {
168
    push @{ $entry->{errors} }, $::locale->text('Error: Warehouse not found');
169
    return 0;
170
  }
171

  
172
  return 1;
173
}
174

  
175
# Check bin fior given warehouse, so check_warehouse must be called first.
176
sub check_bin {
177
  my ($self, $entry) = @_;
178

  
179
  my $object = $entry->{object};
180

  
181
  # If bin from front-end is enforced for all transfers, use this, if valid.
182
  if ($self->settings->{apply_bin} eq 'all') {
183
    $object->bin_id(undef);
184
    my $bin = $self->bins_by->{'wh_id+description'}->{ $object->warehouse_id . '+' . $self->settings->{bin} };
185
    if (!$bin) {
186
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid bin');
187
      return 0;
188
    }
189

  
190
    $object->bin_id($bin->id);
191
  }
192

  
193
  # If bin from front-end is enforced for transfers with missing bin, use this, if valid.
194
  if (    $self->settings->{apply_bin} eq 'missing'
195
       && ! $object->bin_id
196
       && ! $entry->{raw_data}->{bin} ) {
197
    my $bin = $self->bins_by->{'wh_id+description'}->{ $object->warehouse_id . '+' . $self->settings->{bin} };
198
    if (!$bin) {
199
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid bin');
200
      return 0;
201
    }
202

  
203
    $object->bin_id($bin->id);
204
  }
205

  
206
  # Check wether or not bin ID is valid.
207
  if ($object->bin_id && !$self->bins_by->{'wh_id+id'}->{ $object->warehouse_id . '+' . $object->bin_id }) {
208
    push @{ $entry->{errors} }, $::locale->text('Error: Invalid bin');
209
    return 0;
210
  }
211
  
212
  # Map description to ID if given.
213
  if (!$object->bin_id && $entry->{raw_data}->{bin}) {
214
    my $bin = $self->bins_by->{'wh_id+description'}->{ $object->warehouse_id . '+' . $entry->{raw_data}->{bin} };
215
    if (!$bin) {
216
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid bin');
217
      return 0;
218
    }
219

  
220
    $object->bin_id($bin->id);
221
  }
222

  
223
  if ($object->bin_id) {
224
    $entry->{info_data}->{bin} = $self->bins_by->{'wh_id+id'}->{ $object->warehouse_id . '+' . $object->bin_id }->description;
225
  } else {
226
    push @{ $entry->{errors} }, $::locale->text('Error: Bin not found');
227
    return 0;
228
  }
229

  
230
  return 1;
231
}
232

  
233
sub check_part {
234
  my ($self, $entry) = @_;
235

  
236
  my $object = $entry->{object};
237

  
238
  # Check wether or non part ID is valid.
239
  if ($object->parts_id && !$self->parts_by->{id}->{ $object->parts_id }) {
240
    push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
241
    return 0;
242
  }
243

  
244
  # Map number to ID if given.
245
  if (!$object->parts_id && $entry->{raw_data}->{partnumber}) {
246
    my $part = $self->parts_by->{partnumber}->{ $entry->{raw_data}->{partnumber} };
247
    if (!$part) {
248
      push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
249
      return 0;
250
    }
251

  
252
    $object->parts_id($part->id);
253
  }
254

  
255
  if ($object->parts_id) {
256
    $entry->{info_data}->{partnumber} = $self->parts_by->{id}->{ $object->parts_id }->partnumber;
257
  } else {
258
    push @{ $entry->{errors} }, $::locale->text('Error: Part not found');
259
    return 0;
260
  }
261

  
262
  return 1;
263
}
264

  
265
# This imports inventories when target_qty is given, transfers else.
266
# So we get the actual qty in stock and transfer the difference in case of
267
# a given target_qty
268
sub check_qty{
269
  my ($self, $entry) = @_;
270

  
271
  my $object = $entry->{object};
272

  
273
  if (! exists $entry->{raw_data}->{target_qty} && ! exists $entry->{raw_data}->{qty}) {
274
    push @{ $entry->{errors} }, $::locale->text('Error: A quantity or a target quantity must be given.');
275
    return 0;
276
  }
277

  
278
  if (exists $entry->{raw_data}->{target_qty} && exists $entry->{raw_data}->{qty}) {
279
    push @{ $entry->{errors} }, $::locale->text('Error: a quantity and a target quantity could not be given both.');
280
    return 0;
281
  }
282

  
283
  if (exists $entry->{raw_data}->{target_qty} && ($entry->{raw_data}->{target_qty} * 1) < 0) {
284
    push @{ $entry->{errors} }, $::locale->text('Error: A negative target quantity is not allowed.');
285
    return 0;
286
  }
287

  
288
  # Actual quantity is read from stock or is the result of transfers for the
289
  # same part, warehouse and bin done before.
290
  my $key = $object->parts_id . '+' . $object->warehouse_id . '+' . $object->bin_id;
291
  if (!exists $self->{resulting_quantities}->{$key}) {
292
    my $stock = $object->part->get_simple_stock;
293
    my @stocked = grep { $_->{warehouse_id} == $object->warehouse_id && $_->{bin_id} == $object->bin_id } @$stock;
294
    my $stocked_qty = 0;
295
    foreach (@stocked) {
296
      $stocked_qty += $stocked[0]->{sum} * 1;
297
    }
298
    $self->{resulting_quantities}->{$key} = $stocked_qty;
299
  }
300
  my $actual_qty = $self->{resulting_quantities}->{$key};
301

  
302
  if (exists $entry->{raw_data}->{target_qty}) {
303
    my $target_qty = $entry->{raw_data}->{target_qty} * 1;
304

  
305
    $object->qty($target_qty - $actual_qty);
306
    $self->add_columns(qw(qty));
307
  }
308

  
309
  if ($object->qty == 0) {
310
    push @{ $entry->{errors} }, $::locale->text('Error: Quantity to transfer is zero.');
311
    return 0;
312
  }
313

  
314
  # Check if resulting quantity is below zero.
315
  if ( ($actual_qty + $object->qty) < 0 ) {
316
    push @{ $entry->{errors} }, $::locale->text('Error: Transfer would result in a negative target quantity.');
317
    return 0;
318
  }
319

  
320
  $self->{resulting_quantities}->{$key} += $object->qty;
321
  $entry->{info_data}->{target_qty} = $self->{resulting_quantities}->{$key};
322

  
323
  return 1;
324
}
325

  
326
sub handle_comment {
327
  my ($self, $entry) = @_;
328

  
329
  my $object = $entry->{object};
330

  
331
  # If comment from front-end is enforced for all transfers, use this, if valid.
332
  if ($self->settings->{apply_comment} eq 'all') {
333
    $object->comment($self->settings->{comment});
334
  }
335

  
336
  # If comment from front-end is enforced for transfers with missing comment, use this, if valid.
337
  if ($self->settings->{apply_comment} eq 'missing' && ! $object->comment) {
338
    $object->comment($self->settings->{comment});
339
  }
340

  
341
  return;
342
}
343

  
344
sub handle_transfer_type  {
345
  my ($self, $entry) = @_;
346

  
347
  my $object = $entry->{object};
348

  
349
  my $transfer_type = SL::DB::Manager::TransferType->find_by(description => 'correction',
350
                                                             direction   => ($object->qty > 0)? 'in': 'out');
351
  $object->trans_type($transfer_type);
352

  
353
  return;
354
}
355

  
356
# ToDo: employee by name
357
sub handle_employee {
358
  my ($self, $entry) = @_;
359

  
360
  my $object = $entry->{object};
361

  
362
  # employee from front end if not given
363
  if (!$object->employee_id) {
364
    $object->employee_id($self->controller->{employee_id})
365
  }
366

  
367
  # employee from login if not given
368
  if (!$object->employee_id) {
369
    $object->employee_id(SL::DB::Manager::Employee->find_by(login => $::myconfig{login})->id);
370
  }
371

  
372
  if ($object->employee_id) {
373
    $entry->{info_data}->{employee} = $object->employee->name;
374
  }
375

  
376
}
377

  
378
sub handle_shippingdate {
379
  my ($self, $entry) = @_;
380

  
381
  my $object = $entry->{object};
382

  
383
  if (!$object->shippingdate) {
384
    $object->shippingdate(DateTime->today_local);
385
  }
386
}
387

  
388
sub save_objects {
389
  my ($self, %params) = @_;
390

  
391
  my $data = $params{data} || $self->controller->data;
392

  
393
  foreach my $entry (@{ $data }) {
394
    my ($trans_id) = selectrow_query($::form, $::form->get_standard_dbh, qq|SELECT nextval('id')|);
395
    $entry->{object}->trans_id($trans_id);
396
  }
397

  
398
  $self->SUPER::save_objects(%params);
399
}
400

  
401
1;
locale/de/all
238 238
  'Application Error. No Format given' => 'Fehler in der Anwendung. Das Ausgabeformat fehlt.',
239 239
  'Application Error. Wrong Format' => 'Fehler in der Anwendung. Falsches Format: ',
240 240
  'Apply to all parts'          => 'Bei allen Artikeln setzen',
241
  'Apply to all transfers'      => 'Bei allen Lagerbewegungen setzen',
241 242
  'Apply to parts without buchungsgruppe' => 'Bei allen Artikeln ohne gültige Buchungsgruppe setzen',
243
  'Apply to transfers without bin' => 'Bei allen Lagerbewegungen ohne Lagerplatz setzen',
244
  'Apply to transfers without comment' => 'Bei allen Lagerbewegungen ohne Kommentar setzen',
245
  'Apply to transfers without warehouse' => 'Bei allen Lagerbewegungen ohne Lager setzen',
242 246
  'Applying #1:'                => 'Führe #1 aus:',
243 247
  'Appointment Category'        => 'Termin Kategorie',
244 248
  'Appointments'                => 'Termine',
......
355 359
  'Billing/shipping address (street)' => 'Rechnungsadresse (Straße)',
356 360
  'Billing/shipping address (zipcode)' => 'Rechnungsadresse (PLZ)',
357 361
  'Bin'                         => 'Lagerplatz',
362
  'Bin (database ID)'           => 'Lagerplatz (Datenbank-ID)',
358 363
  'Bin From'                    => 'Quelllagerplatz',
359 364
  'Bin List'                    => 'Lagerliste',
360 365
  'Bin To'                      => 'Ziellagerplatz',
......
416 421
  'CSV export -- options'       => 'CSV-Export -- Optionen',
417 422
  'CSV import: contacts'        => 'CSV-Import: Ansprechpersonen',
418 423
  'CSV import: customers and vendors' => 'CSV-Import: Kunden und Lieferanten',
424
  'CSV import: inventories'     => 'CSV-Import: Lagerbewegungen/-bestände',
419 425
  'CSV import: orders'          => 'CSV-Import: Aufträge',
420 426
  'CSV import: parts and services' => 'CSV-Import: Waren und Dienstleistungen',
421 427
  'CSV import: projects'        => 'CSV-Import: Projekte',
......
859 865
  'Do not link to a project.'   => 'Nicht mit einem Projekt verknüpfen.',
860 866
  'Do not modify this position' => 'Diese Position nicht verändern',
861 867
  'Do not set default buchungsgruppe' => 'Nie Standardbuchungsgruppe setzen',
868
  'Do not set this bin'         => 'Diesen Lagerplatz nicht setzen',
869
  'Do not set this comment'     => 'Diesen Kommentar nicht setzen',
870
  'Do not set this warehouse'   => 'Dieses Lager nicht setzen',
862 871
  'Do you really want do continue?' => 'Wollen Sie wirklich fortfahren?',
863 872
  'Do you really want to cancel?' => 'Wollen Sie wirklich abbrechen?',
864 873
  'Do you really want to close the following SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.' => 'Wollen Sie wirklich die folgenden SEPA-Exporte abschließen? Für Überweisungen, die noch nicht gebucht wurden, werden dann keine Zahlungen verbucht.',
......
1043 1052
  'Error when saving: #1'       => 'Fehler beim Speichern: #1',
1044 1053
  'Error with default taxzone'  => 'Ungültige Standardsteuerzone',
1045 1054
  'Error!'                      => 'Fehler!',
1055
  'Error: A negative target quantity is not allowed.' => 'Fehler: Eine negative Zielmenge ist nicht erlaubt.',
1056
  'Error: A quantity or a target quantity must be given.' => 'Fehler: Menge oder Zielmenge muss angegeben werden.',
1057
  'Error: Bin not found'        => 'Fehler: Lagerplatz nicht gefunden',
1046 1058
  'Error: Buchungsgruppe missing or invalid' => 'Fehler: Buchungsgruppe fehlt oder ungültig',
1047 1059
  'Error: Customer/vendor missing' => 'Fehler: Kunde/Lieferant fehlt',
1048 1060
  'Error: Customer/vendor not found' => 'Fehler: Kunde/Lieferant nicht gefunden',
1049 1061
  'Error: Gender (cp_gender) missing or invalid' => 'Fehler: Geschlecht (cp_gender) fehlt oder ungültig',
1062
  'Error: Invalid bin'          => 'Fehler: Ungültiger Lagerplatz',
1050 1063
  'Error: Invalid business'     => 'Fehler: Kunden-/Lieferantentyp ungültig',
1051 1064
  'Error: Invalid contact'      => 'Fehler: Ansprechperson ungültig',
1052 1065
  'Error: Invalid currency'     => 'Fehler: ungültige Währung',
......
1064 1077
  'Error: Invalid shipto'       => 'Fehler: Lieferadresse ungültig',
1065 1078
  'Error: Invalid tax zone'     => 'Fehler: Steuerzone ungültig',
1066 1079
  'Error: Invalid vendor in column make_#1' => 'Fehler: Lieferant ungültig in Spalte make_#1',
1080
  'Error: Invalid warehouse'    => 'Fehler: Ungültiges Lager',
1067 1081
  'Error: Name missing'         => 'Fehler: Name fehlt',
1068 1082
  'Error: Part not found'       => 'Fehler: Artikel nicht gefunden',
1083
  'Error: Quantity to transfer is zero.' => 'Fehler: Zu bewegende Menge ist Null.',
1084
  'Error: Transfer would result in a negative target quantity.' => 'Fehler: Lagerbewegung würde zu einer negativen Zielmenge führen.',
1069 1085
  'Error: Unit missing or invalid' => 'Fehler: Einheit fehlt oder ungültig',
1086
  'Error: Warehouse not found'  => 'Fehler: Lager nicht gefunden',
1087
  'Error: a quantity and a target quantity could not be given both.' => 'Fehler: Menge und Zielmenge können nicht beide angegeben werden.',
1070 1088
  'Error: this feature requires that articles with a time-based unit (e.g. \'h\' or \'min\') exist.' => 'Fehler: dieses Feature setzt voraus, dass Artikel mit einer Zeit-basierenden Einheit (z.B. "Std") existieren.',
1071 1089
  'Errors'                      => 'Fehler',
1072 1090
  'Ertrag'                      => 'Ertrag',
......
1327 1345
  'Invalid variable #1'         => 'Ungültige Variable #1',
1328 1346
  'Invdate'                     => 'Rechnungsdatum',
1329 1347
  'Invdate from'                => 'Rechnungen von',
1348
  'Inventories'                 => 'Lagerbewegungen/-bestände',
1330 1349
  'Inventory'                   => 'Inventar',
1331 1350
  'Inventory Account'           => 'Warenbestand',
1332 1351
  'Inventory quantity must be zero before you can set this assembly obsolete!' => 'Bevor dieses Erzeugnis als ungültig markiert werden kann, muß das Inventar auf Null sein!',
......
1678 1697
  'On'                          => 'An',
1679 1698
  'On Hand'                     => 'Auf Lager',
1680 1699
  'On Order'                    => 'Ist bestellt',
1700
  '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.' => 'Eine der Spalten "qty" oder "target_qty" muss angegeben werden. Wird "target_qty" angegeben, so wird die zu bewegende Menge für jede Lagerbewegung so berechnet, dass die Lagermenge für diesen Artikel, Lager und Lagerplatz nach jeder Lagerbewegung der angebenen Zielmenge entspricht.',
1681 1701
  'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
1682 1702
  '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.' => 'Das Import-Feld Auf Lager setzt nur die Menge in den Stammdaten, nicht im Lagerbereich. Dies ist historisch gewachsen nur ein Informationsfeld was mit dem tatsächlichen Wert überschrieben wird, sobald eine wirkliche Lagerbewegung stattfindet (DB-Trigger).',
1683 1703
  'Only Warnings and Errors'    => 'Nur Warnungen und Fehler',
......
2222 2242
  'Ship via'                    => 'Transportmittel',
2223 2243
  'Shipping Address'            => 'Lieferadresse',
2224 2244
  'Shipping Point'              => 'Versandort',
2245
  'Shipping date'               => 'Lieferdatum',
2225 2246
  'Shipto'                      => 'Lieferanschriften',
2226 2247
  'Shipto deleted.'             => 'Lieferadresse gelöscht',
2227 2248
  'Shipto is in use and was flagged invalid.' => 'Lieferadresse ist noch in Verwendung, und wurde als ungültig markiert.',
......
2356 2377
  'TOP100'                      => 'Top 100',
2357 2378
  'TOTAL'                       => 'TOTAL',
2358 2379
  'Tab'                         => 'Tabulator',
2380
  'Target Qty'                  => 'Zielmenge',
2359 2381
  'Target bank account'         => 'Zielkonto',
2360 2382
  'Target table'                => 'Zieltabelle',
2361 2383
  'Task Server is not running, starting it now. If this does not change, please check your task server config' => 'Der Taskserver läuft nicht, starte ihn jetzt. Das kann ein paar Sekunden dauern. Sollte das nicht funktionieren, prüfen Sie bitte die Taskserver-Konfiguration.',
......
2944 2966
  'WARN: Tax included value!'   => 'ACHTUNG: Steuer im Preis inbegriffen!',
2945 2967
  'WHJournal'                   => 'Lagerbuchungen',
2946 2968
  'Warehouse'                   => 'Lager',
2969
  'Warehouse (database ID)'     => 'Lager (Datenbank-ID)',
2947 2970
  'Warehouse From'              => 'Quelllager',
2948 2971
  'Warehouse Migration'         => 'Lagermigration',
2949 2972
  'Warehouse To'                => 'Ziellager',
......
3194 3217
  'purchase_delivery_order_list' => 'lieferscheinliste_einkauf',
3195 3218
  'purchase_order'              => 'Auftrag',
3196 3219
  'purchase_order_list'         => 'lieferantenauftragsliste',
3220
  'qty (to transfer)'           => 'zu bewegende Menge',
3197 3221
  'quarter'                     => 'Vierteljährliche (quartalsweise) Abgabe',
3198 3222
  'quotation_list'              => 'angebotsliste',
3199 3223
  'release_material'            => 'Materialausgabebe',
locale/en/all
211 211
  'Application Error. No Format given' => '',
212 212
  'Application Error. Wrong Format' => '',
213 213
  'Apply to all parts'          => '',
214
  'Apply to all transfers'      => '',
214 215
  'Apply to parts without buchungsgruppe' => '',
216
  'Apply to transfers without bin' => '',
217
  'Apply to transfers without comment' => '',
218
  'Apply to transfers without warehouse' => '',
215 219
  'Applying #1:'                => '',
216 220
  'Appointment Category'        => '',
217 221
  'Appointments'                => '',
......
307 311
  'Billing/shipping address (street)' => '',
308 312
  'Billing/shipping address (zipcode)' => '',
309 313
  'Bin'                         => '',
314
  'Bin (database ID)'           => '',
310 315
  'Bin From'                    => '',
311 316
  'Bin List'                    => '',
312 317
  'Bin To'                      => '',
......
365 370
  'CSV export -- options'       => '',
366 371
  'CSV import: contacts'        => '',
367 372
  'CSV import: customers and vendors' => '',
373
  'CSV import: inventories'     => '',
368 374
  'CSV import: orders'          => '',
369 375
  'CSV import: parts and services' => '',
370 376
  'CSV import: projects'        => '',
......
724 730
  'Do not change the tax rate of taxkey 0.' => '',
725 731
  'Do not check for duplicates' => '',
726 732
  'Do not set default buchungsgruppe' => '',
733
  'Do not set this bin'         => '',
734
  'Do not set this comment'     => '',
735
  'Do not set this warehouse'   => '',
727 736
  'Do you really want to close the following SEPA exports? No payment will be recorded for bank collections that haven\'t been marked as executed yet.' => '',
728 737
  'Do you really want to close the following SEPA exports? No payment will be recorded for bank transfers that haven\'t been marked as executed yet.' => '',
729 738
  'Do you really want to delete AP transaction #1?' => '',
......
881 890
  'Error message from the database: #1' => '',
882 891
  'Error when saving: #1'       => '',
883 892
  'Error!'                      => '',
893
  'Error: A negative target quantity is not allowed.' => '',
894
  'Error: A quantity or a target quantity must be given.' => '',
895
  'Error: Bin not found'        => '',
884 896
  'Error: Buchungsgruppe missing or invalid' => '',
885 897
  'Error: Customer/vendor missing' => '',
886 898
  'Error: Customer/vendor not found' => '',
887 899
  'Error: Gender (cp_gender) missing or invalid' => '',
900
  'Error: Invalid bin'          => '',
888 901
  'Error: Invalid business'     => '',
889 902
  'Error: Invalid contact'      => '',
890 903
  'Error: Invalid currency'     => '',
......
902 915
  'Error: Invalid shipto'       => '',
903 916
  'Error: Invalid tax zone'     => '',
904 917
  'Error: Invalid vendor in column make_#1' => '',
918
  'Error: Invalid warehouse'    => '',
905 919
  'Error: Name missing'         => '',
906 920
  'Error: Part not found'       => '',
921
  'Error: Quantity to transfer is zero.' => '',
922
  'Error: Transfer would result in a negative target quantity.' => '',
907 923
  'Error: Unit missing or invalid' => '',
924
  'Error: Warehouse not found'  => '',
925
  'Error: a quantity and a target quantity could not be given both.' => '',
908 926
  'Errors'                      => '',
909 927
  'Ertrag'                      => '',
910 928
  'Ertrag prozentual'           => '',
......
1135 1153
  'Invalid transactions'        => '',
1136 1154
  'Invdate'                     => '',
1137 1155
  'Invdate from'                => '',
1156
  'Inventories'                 => '',
1138 1157
  'Inventory'                   => '',
1139 1158
  'Inventory Account'           => '',
1140 1159
  'Inventory quantity must be zero before you can set this assembly obsolete!' => '',
......
1435 1454
  'On'                          => '',
1436 1455
  'On Hand'                     => '',
1437 1456
  'On Order'                    => '',
1457
  '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.' => '',
1438 1458
  'One or more Perl modules missing' => '',
1439 1459
  'Only Warnings and Errors'    => '',
1440 1460
  'Only due follow-ups'         => '',
......
1869 1889
  'Ship via'                    => '',
1870 1890
  'Shipping Address'            => '',
1871 1891
  'Shipping Point'              => '',
1892
  'Shipping date'               => '',
1872 1893
  'Shipto'                      => '',
1873 1894
  'Shipto deleted.'             => '',
1874 1895
  'Shipto is in use and was flagged invalid.' => '',
......
1989 2010
  'TOP100'                      => '',
1990 2011
  'TOTAL'                       => '',
1991 2012
  'Tab'                         => '',
2013
  'Target Qty'                  => '',
1992 2014
  'Target bank account'         => '',
1993 2015
  'Target table'                => '',
1994 2016
  'Task Server is not running, starting it now. If this does not change, please check your task server config' => '',
......
2452 2474
  'Von Konto: '                 => '',
2453 2475
  'WHJournal'                   => '',
2454 2476
  'Warehouse'                   => '',
2477
  'Warehouse (database ID)'     => '',
2455 2478
  'Warehouse From'              => '',
2456 2479
  'Warehouse Migration'         => '',
2457 2480
  'Warehouse To'                => '',
......
2689 2712
  'purchase_delivery_order_list' => '',
2690 2713
  'purchase_order'              => '',
2691 2714
  'purchase_order_list'         => '',
2715
  'qty (to transfer)'           => '',
2692 2716
  'quarter'                     => '',
2693 2717
  'quotation_list'              => '',
2694 2718
  'release_material'            => '',
menus/erp.ini
745 745
action=CsvImport/new
746 746
profile.type=parts
747 747

  
748
[System--Import CSV--Inventories]
749
module=controller.pl
750
action=CsvImport/new
751
profile.type=inventories
752

  
748 753
[System--Import CSV--Projects]
749 754
module=controller.pl
750 755
action=CsvImport/new
templates/webpages/csv_import/_form_inventories.html
1
[% USE LxERP %]
2
[% USE L %]
3
<tr>
4
 <th align="right" valign="top">[%- LxERP.t8('Warehouse') %]/[%- LxERP.t8('Bin') %]:</th>
5
 <td colspan="2" valign="top">
6
  [% L.input_tag('settings.warehouse', SELF.profile.get('warehouse'), style = 'width: 300px') %]
7
  <br>
8
  [% opts = [ [ 'never', LxERP.t8('Do not set this warehouse') ],[ 'missing', LxERP.t8('Apply to transfers without warehouse') ], [ 'all', LxERP.t8('Apply to all transfers') ] ] %]
9
  [% L.select_tag('settings.apply_warehouse', opts, default = SELF.profile.get('apply_warehouse'), style = 'width: 300px') %]
10
 </td>
11
 <td colspan="8" valign="top">
12
  [% L.input_tag('settings.bin', SELF.profile.get('bin'), style = 'width: 300px') %]
13
  <br>
14
  [% opts = [ [ 'never', LxERP.t8('Do not set this bin') ], [ 'missing', LxERP.t8('Apply to transfers without bin') ], [ 'all', LxERP.t8('Apply to all transfers') ] ] %]
15
  [% L.select_tag('settings.apply_bin', opts, default = SELF.profile.get('apply_bin'), style = 'width: 300px') %]
16
 </td>
17
</tr>
18

  
19
<tr>
20
</tr>
21

  
22
<tr>
23
 <th align="right" valign="top">[%- LxERP.t8('Comment') %]:</th>
24
 <td colspan="10" valign="top">
25
  [% L.input_tag('settings.comment', SELF.profile.get('comment'), style = 'width: 300px') %]
26
  <br>
27
  [% opts = [ [ 'never', LxERP.t8('Do not set this comment') ], [ 'missing', LxERP.t8('Apply to transfers without comment') ], [ 'all', LxERP.t8('Apply to all transfers') ] ] %]
28
  [% L.select_tag('settings.apply_comment', opts, default = SELF.profile.get('apply_comment'), style = 'width: 300px') %]
29
 </td>
30
</tr>
templates/webpages/csv_import/form.html
130 130
    [% 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.") %]
131 131
   </p>
132 132

  
133
[%- ELSIF SELF.type == 'inventories' %]
134
   <p>
135
    [%- 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.') %]
136
   </p>
137

  
133 138
[%- ELSIF SELF.type == 'orders' %]
134 139
   <p>
135 140
    [1]:
......
251 256
 [%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
252 257
[%- ELSIF SELF.type == 'contacts' %]
253 258
 [%- INCLUDE 'csv_import/_form_contacts.html' %]
259
[%- ELSIF SELF.type == 'inventories' %]
260
 [%- INCLUDE 'csv_import/_form_inventories.html' %]
254 261
[%- ELSIF SELF.type == 'orders' %]
255 262
 [%- INCLUDE 'csv_import/_form_orders.html' %]
256 263
[%- END %]

Auch abrufbar als: Unified diff