]> wagnertech.de Git - mfinanz.git/commitdiff
Merge branch 'master' of github.com:kivitendo/kivitendo-erp
authorJan Büren <jan@kivitendo-premium.de>
Tue, 10 Dec 2013 10:07:54 +0000 (11:07 +0100)
committerJan Büren <jan@kivitendo-premium.de>
Tue, 10 Dec 2013 10:07:54 +0000 (11:07 +0100)
92 files changed:
SL/CT.pm
SL/Common.pm
SL/Controller/CsvImport.pm
SL/Controller/CsvImport/Base.pm
SL/Controller/CsvImport/BaseMulti.pm [new file with mode: 0644]
SL/Controller/CsvImport/CustomerVendor.pm
SL/Controller/CsvImport/Helper/Consistency.pm [new file with mode: 0644]
SL/Controller/CsvImport/Order.pm [new file with mode: 0644]
SL/Controller/CustomerVendor.pm
SL/Controller/DeliveryTerm.pm [new file with mode: 0644]
SL/DB/CsvImportProfile.pm
SL/DB/DeliveryTerm.pm [new file with mode: 0644]
SL/DB/FollowUp.pm
SL/DB/Helper/ALL.pm
SL/DB/Helper/FlattenToForm.pm
SL/DB/Invoice.pm
SL/DB/Manager/DeliveryTerm.pm [new file with mode: 0644]
SL/DB/MetaSetup/CsvImportReport.pm
SL/DB/MetaSetup/Customer.pm
SL/DB/MetaSetup/DeliveryOrder.pm
SL/DB/MetaSetup/DeliveryTerm.pm [new file with mode: 0644]
SL/DB/MetaSetup/FollowUp.pm
SL/DB/MetaSetup/GLTransaction.pm
SL/DB/MetaSetup/Invoice.pm
SL/DB/MetaSetup/Order.pm
SL/DB/MetaSetup/PurchaseInvoice.pm
SL/DB/MetaSetup/Vendor.pm
SL/DB/Note.pm
SL/DO.pm
SL/GL.pm
SL/Helper/Csv.pm
SL/Helper/Csv/Dispatcher.pm
SL/IR.pm
SL/IS.pm
SL/OE.pm
SL/Presenter/Part.pm
bin/mozilla/common.pl
bin/mozilla/ct.pl
bin/mozilla/do.pl
bin/mozilla/gl.pl
bin/mozilla/io.pl
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
doc/changelog
doc/dokumentation.xml
doc/html/ch03s02.html
doc/html/ch04.html
doc/html/index.html
doc/kivitendo-Dokumentation.pdf
locale/de/all
locale/en/all
menus/erp.ini
modules/override/PDF/Table.pm
scripts/rose_auto_create_model.pl
sql/Pg-upgrade2/csv_import_reports_add_numheaders.sql [new file with mode: 0644]
sql/Pg-upgrade2/delivery_terms.sql [new file with mode: 0644]
sql/Pg-upgrade2/gl_add_employee_foreign_key.sql [new file with mode: 0644]
t/helper/csv.t
templates/print/RB/deutsch.tex
templates/print/RB/english.tex
templates/print/RB/invoice.tex
templates/print/RB/purchase_delivery_order.tex
templates/print/RB/purchase_order.tex
templates/print/RB/request_quotation.tex
templates/print/RB/sales_delivery_order.tex
templates/print/RB/sales_order.tex
templates/print/RB/sales_quotation.tex
templates/webpages/common/show_vc_details.html
templates/webpages/csv_import/_form_orders.html [new file with mode: 0644]
templates/webpages/csv_import/form.html
templates/webpages/csv_import/report.html
templates/webpages/ct/_contact.html [deleted file]
templates/webpages/ct/_shipto.html [deleted file]
templates/webpages/ct/ajax_autocomplete.html [deleted file]
templates/webpages/ct/form_footer.html [deleted file]
templates/webpages/ct/form_header.html [deleted file]
templates/webpages/ct/get_delivery.html [deleted file]
templates/webpages/ct/list_names_bottom.html
templates/webpages/ct/testpage.html [deleted file]
templates/webpages/customer_vendor/form.html
templates/webpages/customer_vendor/tabs/billing.html
templates/webpages/customer_vendor/tabs/vcnotes.html
templates/webpages/delivery_term/form.html [new file with mode: 0755]
templates/webpages/delivery_term/list.html [new file with mode: 0644]
templates/webpages/do/form_footer.html
templates/webpages/do/search.html
templates/webpages/gl/form_header.html
templates/webpages/ir/form_footer.html
templates/webpages/is/form_footer.html
templates/webpages/oe/form_footer.html
templates/webpages/oe/search.html

index cae50f0fba7e18e7111a8e7522e670c77f7579eb..8ceab3aa6a31896331bbb9908cac66a73fdd7ea9 100644 (file)
--- a/SL/CT.pm
+++ b/SL/CT.pm
@@ -322,7 +322,8 @@ sub save_customer {
     qq|c_vendor_id = ?, | .
     qq|klass = ?, | .
     qq|currency_id = (SELECT id FROM currencies WHERE name = ?), | .
-    qq|taxincluded_checked = ? | .
+    qq|taxincluded_checked = ?, | .
+    qq|delivery_term_id = ? | .
     qq|WHERE id = ?|;
   my @values = (
     $form->{customernumber},
@@ -366,6 +367,7 @@ sub save_customer {
     conv_i($form->{klass}),
     $form->{currency},
     $form->{taxincluded_checked} ne '' ? $form->{taxincluded_checked} : undef,
+    conv_i($form->{delivery_term_id}),
     $form->{id}
     );
   do_query( $form, $dbh, $query, @values );
@@ -473,7 +475,8 @@ sub save_vendor {
     qq|  username = ?, | .
     qq|  user_password = ?, | .
     qq|  v_customer_id = ?, | .
-    qq|  currency_id = (SELECT id FROM currencies WHERE name = ?) | .
+    qq|  currency_id = (SELECT id FROM currencies WHERE name = ?), | .
+    qq|  delivery_term_id = ? | .
     qq|WHERE id = ?|;
   my @values = (
     $form->{vendornumber},
@@ -514,6 +517,7 @@ sub save_vendor {
     $form->{user_password},
     $form->{v_customer_id},
     $form->{currency},
+    conv_i($form->{delivery_term_id}),
     $form->{id}
     );
   do_query($form, $dbh, $query, @values);
index d6260251eb0040b8bb436660277ae52bec79833d..127209a7ff7975e56a521a96fc7beef70e215783 100644 (file)
@@ -397,11 +397,13 @@ sub get_vc_details {
          vc.*,
          pt.description AS payment_terms,
          b.description AS business,
-         l.description AS language
+         l.description AS language,
+         dt.description AS delivery_terms
        FROM ${vc} vc
        LEFT JOIN payment_terms pt ON (vc.payment_id = pt.id)
        LEFT JOIN business b ON (vc.business_id = b.id)
        LEFT JOIN language l ON (vc.language_id = l.id)
+       LEFT JOIN delivery_terms dt ON (vc.delivery_term_id = dt.id)
        WHERE vc.id = ?|;
   my $ref = selectfirst_hashref_query($form, $dbh, $query, $vc_id);
 
index c3b9d99cce256dd9454a023b8e5e4f3a34fda815..0ebf141645117dd114b558efc4a8d16c6bcf7c4b 100644 (file)
@@ -15,6 +15,7 @@ use SL::Controller::CsvImport::CustomerVendor;
 use SL::Controller::CsvImport::Part;
 use SL::Controller::CsvImport::Shipto;
 use SL::Controller::CsvImport::Project;
+use SL::Controller::CsvImport::Order;
 use SL::BackgroundJob::CsvImport;
 use SL::System::TaskServer;
 
@@ -125,10 +126,21 @@ sub action_download_sample {
   my $file      = SL::SessionFile->new($file_name, mode => '>', encoding => $self->profile->get('charset'));
   my $csv       = Text::CSV_XS->new({ binary => 1, map { ( $_ => $self->profile->get($_) ) } qw(sep_char escape_char quote_char),});
 
-  $csv->print($file->fh, [ map { $_->{name}        } @{ $self->displayable_columns } ]);
-  $file->fh->print("\r\n");
-  $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns } ]);
-  $file->fh->print("\r\n");
+  if ($self->worker->is_multiplexed) {
+    foreach my $p (@{ $self->worker->profile }) {
+      $csv->print($file->fh, [ map { $_->{name}        } @{ $self->displayable_columns->{$p->{row_ident}} } ]);
+      $file->fh->print("\r\n");
+    }
+    foreach my $p (@{ $self->worker->profile }) {
+      $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns->{$p->{row_ident}} } ]);
+      $file->fh->print("\r\n");
+    }
+  } else {
+    $csv->print($file->fh, [ map { $_->{name}        } @{ $self->displayable_columns } ]);
+    $file->fh->print("\r\n");
+    $csv->print($file->fh, [ map { $_->{description} } @{ $self->displayable_columns } ]);
+    $file->fh->print("\r\n");
+  }
 
   $file->fh->close;
 
@@ -158,20 +170,30 @@ sub action_report {
                             : $page;
   $pages->{common}          = [ grep { $_->{visible} } @{ SL::DB::Helper::Paginated::make_common_pages($pages->{cur}, $pages->{max}) } ];
 
+  $self->{report_numheaders} = $self->{report}->numheaders;
+  my $first_row_header = 0;
+  my $last_row_header  = $self->{report_numheaders} - 1;
+  my $first_row_data   = $pages->{per_page} * ($pages->{cur}-1) + $self->{report_numheaders};
+  my $last_row_data    = min($pages->{per_page} * $pages->{cur}, $num_rows) + $self->{report_numheaders} - 1;
   $self->{display_rows} = [
-    0,
-    $pages->{per_page} * ($pages->{cur}-1) + 1
+    $first_row_header
+      ..
+    $last_row_header,
+    $first_row_data
       ..
-    min($pages->{per_page} * $pages->{cur}, $num_rows)
+    $last_row_data
   ];
 
   my @query = (
     csv_import_report_id => $report_id,
     or => [
-      row => 0,
       and => [
-        row => { gt => $pages->{per_page} * ($pages->{cur}-1) },
-        row => { le => $pages->{per_page} * $pages->{cur} },
+        row => { ge => $first_row_header },
+        row => { le => $last_row_header },
+      ],
+      and => [
+        row => { ge => $first_row_data },
+        row => { le => $last_row_data },
       ]
     ]
   );
@@ -199,7 +221,7 @@ sub check_auth {
 sub check_type {
   my ($self) = @_;
 
-  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts customers_vendors addresses contacts projects);
+  die "Invalid CSV import type" if none { $_ eq $::form->{profile}->{type} } qw(parts customers_vendors addresses contacts projects orders);
   $self->type($::form->{profile}->{type});
 }
 
@@ -242,6 +264,7 @@ sub render_inputs {
             : $self->type eq 'contacts'          ? $::locale->text('CSV import: contacts')
             : $self->type eq 'parts'             ? $::locale->text('CSV import: parts and services')
             : $self->type eq 'projects'          ? $::locale->text('CSV import: projects')
+            : $self->type eq 'orders'            ? $::locale->text('CSV import: orders')
             : die;
 
   if ($self->{type} eq 'parts') {
@@ -363,6 +386,10 @@ sub profile_from_form {
     $::form->{settings}->{sellprice_adjustment} = $::form->parse_amount(\%::myconfig, $::form->{settings}->{sellprice_adjustment});
   }
 
+  if ($self->type eq 'orders') {
+    $::form->{settings}->{max_amount_diff} = $::form->parse_amount(\%::myconfig, $::form->{settings}->{max_amount_diff});
+  }
+
   delete $::form->{profile}->{id};
   $self->profile($existing_profile || SL::DB::CsvImportProfile->new(login => $::myconfig{login}));
   $self->profile->assign_attributes(%{ $::form->{profile} });
@@ -389,6 +416,16 @@ sub char_map {
 sub save_report {
   my ($self, $report_id) = @_;
 
+  if ($self->worker->is_multiplexed) {
+    return $self->save_report_multi($report_id);
+  } else {
+    return $self->save_report_single($report_id);
+  }
+}
+
+sub save_report_single {
+  my ($self, $report_id) = @_;
+
   $self->track_progress(phase => 'building report', progress => 0);
 
   my $clone_profile = $self->profile->clone_and_reset_deep;
@@ -400,6 +437,7 @@ sub save_report {
     type       => $self->type,
     file       => '',
     numrows    => scalar @{ $self->data },
+    numheaders => 1,
   );
 
   $report->save(cascade => 1) or die $report->db->error;
@@ -455,6 +493,98 @@ sub save_report {
   return $report->id;
 }
 
+sub save_report_multi {
+  my ($self, $report_id) = @_;
+
+  $self->track_progress(phase => 'building report', progress => 0);
+
+  my $clone_profile = $self->profile->clone_and_reset_deep;
+  $clone_profile->save; # weird bug. if this isn't saved before adding it to the report, it will default back to the last profile.
+
+  my $report = SL::DB::CsvImportReport->new(
+    session_id => $::auth->create_or_refresh_session,
+    profile    => $clone_profile,
+    type       => $self->type,
+    file       => '',
+    numrows    => scalar @{ $self->data },
+    numheaders => scalar @{ $self->worker->profile },
+  );
+
+  $report->save(cascade => 1) or die $report->db->error;
+
+  my $dbh = $::form->get_standard_dbh;
+  $dbh->begin_work;
+
+  my $query  = 'INSERT INTO csv_import_report_rows (csv_import_report_id, col, row, value) VALUES (?, ?, ?, ?)';
+  my $query2 = 'INSERT INTO csv_import_report_status (csv_import_report_id, row, type, value) VALUES (?, ?, ?, ?)';
+
+  my $sth = $dbh->prepare($query);
+  my $sth2 = $dbh->prepare($query2);
+
+  # save headers
+  my ($headers, $info_methods, $raw_methods, $methods);
+
+  for my $i (0 .. $#{ $self->worker->profile }) {
+    my $row_ident = $self->worker->profile->[$i]->{row_ident};
+
+    for my $i (0 .. $#{ $self->info_headers->{$row_ident}->{headers} }) {
+      next unless                            $self->info_headers->{$row_ident}->{used}->{ $self->info_headers->{$row_ident}->{methods}->[$i] };
+      push @{ $headers->{$row_ident} },      $self->info_headers->{$row_ident}->{headers}->[$i];
+      push @{ $info_methods->{$row_ident} }, $self->info_headers->{$row_ident}->{methods}->[$i];
+    }
+    for my $i (0 .. $#{ $self->headers->{$row_ident}->{headers} }) {
+      next unless                       $self->headers->{$row_ident}->{used}->{ $self->headers->{$row_ident}->{headers}->[$i] };
+      push @{ $headers->{$row_ident} }, $self->headers->{$row_ident}->{headers}->[$i];
+      push @{ $methods->{$row_ident} }, $self->headers->{$row_ident}->{methods}->[$i];
+    }
+
+    for my $i (0 .. $#{ $self->raw_data_headers->{$row_ident}->{headers} }) {
+    next unless                           $self->raw_data_headers->{$row_ident}->{used}->{ $self->raw_data_headers->{$row_ident}->{headers}->[$i] };
+    push @{ $headers->{$row_ident} },     $self->raw_data_headers->{$row_ident}->{headers}->[$i];
+    push @{ $raw_methods->{$row_ident} }, $self->raw_data_headers->{$row_ident}->{headers}->[$i];
+  }
+
+  }
+
+  for my $i (0 .. $#{ $self->worker->profile }) {
+    my $row_ident = $self->worker->profile->[$i]->{row_ident};
+    $sth->execute($report->id, $_, $i, $headers->{$row_ident}->[$_]) for 0 .. $#{ $headers->{$row_ident} };
+  }
+
+  # col offsets
+  my ($off1, $off2);
+  for my $i (0 .. $#{ $self->worker->profile }) {
+    my $row_ident = $self->worker->profile->[$i]->{row_ident};
+    my $n_info_methods = $info_methods->{$row_ident} ? scalar @{ $info_methods->{$row_ident} } : 0;
+    my $n_methods      = $methods->{$row_ident} ?      scalar @{ $methods->{$row_ident} }      : 0;
+
+    $off1->{$row_ident} = $n_info_methods;
+    $off2->{$row_ident} = $off1->{$row_ident} + $n_methods;
+  }
+
+  my $n_header_rows = scalar @{ $self->worker->profile };
+
+  for my $row (0 .. $#{ $self->data }) {
+    $self->track_progress(progress => $row / @{ $self->data } * 100) if $row % 1000 == 0;
+    my $data_row = $self->{data}[$row];
+    my $row_ident = $data_row->{raw_data}{datatype};
+
+    my $o1 = $off1->{$row_ident};
+    my $o2 = $off2->{$row_ident};
+
+    $sth->execute($report->id,       $_, $row + $n_header_rows, $data_row->{info_data}{ $info_methods->{$row_ident}->[$_] }) for 0 .. $#{ $info_methods->{$row_ident} };
+    $sth->execute($report->id, $o1 + $_, $row + $n_header_rows, $data_row->{object}->${ \ $methods->{$row_ident}->[$_] })    for 0 .. $#{ $methods->{$row_ident} };
+    $sth->execute($report->id, $o2 + $_, $row + $n_header_rows, $data_row->{raw_data}{ $raw_methods->{$row_ident}->[$_] })   for 0 .. $#{ $raw_methods->{$row_ident} };
+
+    $sth2->execute($report->id, $row + $n_header_rows, 'information', $_) for @{ $data_row->{information} || [] };
+    $sth2->execute($report->id, $row + $n_header_rows, 'errors', $_)      for @{ $data_row->{errors}      || [] };
+  }
+
+  $dbh->commit;
+
+  return $report->id;
+}
+
 sub csv_file_name {
   my ($self) = @_;
   return "csv-import-" . $self->type . ".csv";
@@ -474,6 +604,7 @@ sub init_worker {
        : $self->{type} eq 'addresses'         ? SL::Controller::CsvImport::Shipto->new(@args)
        : $self->{type} eq 'parts'             ? SL::Controller::CsvImport::Part->new(@args)
        : $self->{type} eq 'projects'          ? SL::Controller::CsvImport::Project->new(@args)
+       : $self->{type} eq 'orders'            ? SL::Controller::CsvImport::Order->new(@args)
        :                                        die "Program logic error";
 }
 
index b3b9368c79105156fe4eb839ffb9a176cae08707..f8e9b591c6f2e3f08773db4da467215688646070 100644 (file)
@@ -5,10 +5,10 @@ use strict;
 use List::MoreUtils qw(pairwise any);
 
 use SL::Helper::Csv;
-use SL::DB::Currency;
 use SL::DB::Customer;
 use SL::DB::Language;
 use SL::DB::PaymentTerm;
+use SL::DB::DeliveryTerm;
 use SL::DB::Vendor;
 use SL::DB::Contact;
 use SL::DB::History;
@@ -18,7 +18,7 @@ use parent qw(Rose::Object);
 use Rose::Object::MakeMethods::Generic
 (
  scalar                  => [ qw(controller file csv test_run save_with_cascade) ],
- 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by all_currencies default_currency_id all_vc vc_by) ],
+ 'scalar --get_set_init' => [ qw(profile displayable_columns existing_objects class manager_class cvar_columns all_cvar_configs all_languages payment_terms_by delivery_terms_by all_vc vc_by) ],
 );
 
 sub run {
@@ -31,8 +31,7 @@ sub run {
   my $profile = $self->profile;
   $self->csv(SL::Helper::Csv->new(file                   => $self->file->file_name,
                                   encoding               => $self->controller->profile->get('charset'),
-                                  class                  => $self->class,
-                                  profile                => $profile,
+                                  profile                => [{ profile => $profile, class => $self->class }],
                                   ignore_unknown_columns => 1,
                                   strict_profile         => 1,
                                   case_insensitive_header => 1,
@@ -141,23 +140,18 @@ sub init_all_languages {
   return SL::DB::Manager::Language->get_all;
 }
 
-sub init_all_currencies {
+sub init_payment_terms_by {
   my ($self) = @_;
 
-  return SL::DB::Manager::Currency->get_all;
+  my $all_payment_terms = SL::DB::Manager::PaymentTerm->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_payment_terms } } ) } qw(id description) };
 }
 
-sub init_default_currency_id {
+sub init_delivery_terms_by {
   my ($self) = @_;
 
-  return SL::DB::Default->get->currency_id;
-}
-
-sub init_payment_terms_by {
-  my ($self) = @_;
-
-  my $all_payment_terms = SL::DB::Manager::PaymentTerm->get_all;
-  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_payment_terms } } ) } qw(id description) };
+  my $all_delivery_terms = SL::DB::Manager::DeliveryTerm->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_delivery_terms } } ) } qw(id description) };
 }
 
 sub init_all_vc {
@@ -308,6 +302,8 @@ sub init_manager_class {
   $self->manager_class("SL::DB::Manager::" . $1);
 }
 
+sub is_multiplexed { 0 }
+
 sub check_objects {
 }
 
@@ -394,6 +390,32 @@ sub check_payment {
   return 1;
 }
 
+sub check_delivery_term {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check whether or not delivery term ID is valid.
+  if ($object->delivery_term_id && !$self->delivery_terms_by->{id}->{ $object->delivery_term_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid delivery terms');
+    return 0;
+  }
+
+  # Map name to ID if given.
+  if (!$object->delivery_term_id && $entry->{raw_data}->{delivery_term}) {
+    my $terms = $self->delivery_terms_by->{description}->{ $entry->{raw_data}->{delivery_term} };
+
+    if (!$terms) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid delivery terms');
+      return 0;
+    }
+
+    $object->delivery_term_id($terms->id);
+  }
+
+  return 1;
+}
+
 sub save_objects {
   my ($self, %params) = @_;
 
@@ -413,7 +435,10 @@ sub save_objects {
 
     my $object = $entry->{object_to_save} || $entry->{object};
 
-    if ( !$object->save(cascade => !!$self->save_with_cascade()) ) {
+    my $ret;
+    if (!eval { $ret = $object->save(cascade => !!$self->save_with_cascade()); 1 }) {
+      push @{ $entry->{errors} }, $::locale->text('Error when saving: #1', $@);
+    } elsif ( !$ret ) {
       push @{ $entry->{errors} }, $::locale->text('Error when saving: #1', $entry->{object}->db->error);
     } else {
       $self->_save_history($object);
@@ -462,17 +487,22 @@ sub clean_fields {
 sub _save_history {
   my ($self, $object) = @_;
 
-  if (any { $_ eq $self->controller->{type} } qw(parts customers_vendors)) {
+  if (any { $_ eq $self->controller->{type} } qw(parts customers_vendors orders)) {
     my $snumbers = $self->controller->{type} eq 'parts'             ? 'partnumber_' . $object->partnumber
                  : $self->controller->{type} eq 'customers_vendors' ?
                      ($self->table eq 'customer' ? 'customernumber_' . $object->customernumber : 'vendornumber_' . $object->vendornumber)
+                 : $self->controller->{type} eq 'orders'            ? 'ordnumber_' . $object->ordnumber
                  : '';
 
+    my $what_done = $self->controller->{type} eq 'orders' ? 'sales_order'
+                  : '';
+
     SL::DB::History->new(
       trans_id    => $object->id,
       snumbers    => $snumbers,
       employee_id => $self->controller->{employee_id},
       addition    => 'SAVED',
+      what_done   => $what_done,
     )->save();
   }
 }
diff --git a/SL/Controller/CsvImport/BaseMulti.pm b/SL/Controller/CsvImport/BaseMulti.pm
new file mode 100644 (file)
index 0000000..d5010a2
--- /dev/null
@@ -0,0 +1,256 @@
+package SL::Controller::CsvImport::BaseMulti;
+
+use strict;
+
+use List::MoreUtils qw(pairwise);
+
+use SL::Helper::Csv;
+
+use parent qw(SL::Controller::CsvImport::Base);
+
+use Rose::Object::MakeMethods::Generic
+(
+'scalar --get_set_init' => [ qw(cvar_configs_by cvar_columns_by) ],
+);
+
+sub run {
+  my ($self, %params) = @_;
+
+  $self->test_run($params{test_run});
+
+  $self->controller->track_progress(phase => 'parsing csv', progress => 0);
+
+  my $profile = $self->profile;
+
+  $self->csv(SL::Helper::Csv->new(file                    => $self->file->file_name,
+                                  encoding                => $self->controller->profile->get('charset'),
+                                  profile                 => $profile,
+                                  ignore_unknown_columns  => 1,
+                                  strict_profile          => 1,
+                                  case_insensitive_header => 1,
+                                  map { ( $_ => $self->controller->profile->get($_) ) } qw(sep_char escape_char quote_char),
+                                 ));
+
+  $self->controller->track_progress(progress => 10);
+
+  my $old_numberformat      = $::myconfig{numberformat};
+  $::myconfig{numberformat} = $self->controller->profile->get('numberformat');
+
+  $self->csv->parse;
+
+  $self->controller->track_progress(progress => 50);
+
+  $self->controller->errors([ $self->csv->errors ]) if $self->csv->errors;
+
+  return if ( !$self->csv->header || $self->csv->errors );
+
+  my $headers;
+  my $i = 0;
+  foreach my $header (@{ $self->csv->header }) {
+
+    my $profile   = $self->csv->profile->[$i]->{profile};
+    my $row_ident = $self->csv->profile->[$i]->{row_ident};
+
+    my $h = { headers => [ grep { $profile->{$_} } @{ $header } ] };
+    $h->{methods} = [ map { $profile->{$_} } @{ $h->{headers} } ];
+    $h->{used}    = { map { ($_ => 1) }      @{ $h->{headers} } };
+
+    $headers->{$row_ident} = $h;
+    $i++;
+  }
+
+  $self->controller->headers($headers);
+
+  my $raw_data_headers;
+  my $info_headers;
+  foreach my $p (@{ $self->csv->profile }) {
+    $raw_data_headers->{ $p->{row_ident} } = { used => { }, headers => [ ] };
+    $info_headers->{ $p->{row_ident} }     = { used => { }, headers => [ ] };
+  }
+  $self->controller->raw_data_headers($raw_data_headers);
+  $self->controller->info_headers($info_headers);
+
+  my @objects  = $self->csv->get_objects;
+
+  $self->controller->track_progress(progress => 70);
+
+  my @raw_data = @{ $self->csv->get_data };
+
+  $self->controller->track_progress(progress => 80);
+
+  $self->controller->data([ pairwise { { object => $a, raw_data => $b, errors => [], information => [], info_data => {} } } @objects, @raw_data ]);
+
+  $self->controller->track_progress(progress => 90);
+
+  $self->check_objects;
+  if ( $self->controller->profile->get('duplicates', 'no_check') ne 'no_check' ) {
+    $self->check_std_duplicates();
+    $self->check_duplicates();
+  }
+  $self->fix_field_lengths;
+
+  $self->controller->track_progress(progress => 100);
+
+  $::myconfig{numberformat} = $old_numberformat;
+}
+
+sub add_columns {
+  my ($self, $row_ident, @columns) = @_;
+
+  my $h = $self->controller->headers->{$row_ident};
+
+  foreach my $column (grep { !$h->{used}->{$_} } @columns) {
+    $h->{used}->{$column} = 1;
+    push @{ $h->{methods} }, $column;
+    push @{ $h->{headers} }, $column;
+  }
+}
+
+sub add_info_columns {
+  my ($self, $row_ident, @columns) = @_;
+
+  my $h = $self->controller->info_headers->{$row_ident};
+
+  foreach my $column (grep { !$h->{used}->{ $_->{method} } } map { ref $_ eq 'HASH' ? $_ : { method => $_, header => $_ } } @columns) {
+    $h->{used}->{ $column->{method} } = 1;
+    push @{ $h->{methods} }, $column->{method};
+    push @{ $h->{headers} }, $column->{header};
+  }
+}
+
+sub add_raw_data_columns {
+  my ($self, $row_ident, @columns) = @_;
+
+  my $h = $self->controller->raw_data_headers->{$row_ident};
+
+  foreach my $column (grep { !$h->{used}->{$_} } @columns) {
+    $h->{used}->{$column} = 1;
+    push @{ $h->{headers} }, $column;
+  }
+}
+
+sub add_cvar_raw_data_columns {
+  my ($self) = @_;
+
+  foreach my $data (@{ $self->controller->data }) {
+    my $ri = $data->{raw_data}->{datatype};
+    map { $self->add_raw_data_columns($ri, $_) if exists $data->{raw_data}->{$_} } @{ $self->cvar_columns_by->{row_ident}->{$ri} };
+  }
+}
+
+sub init_cvar_configs_by {
+  # Must be overridden by derived specialized importer classes.
+  return {};
+}
+
+sub init_cvar_columns_by {
+  my ($self) = @_;
+
+  my $ccb;
+  foreach my $p (@{ $self->profile }) {
+    my $ri = $p->{row_ident};
+    $ccb->{row_ident}->{$ri} = [ map { "cvar_" . $_->name } (@{ $self->cvar_configs_by->{row_ident}->{$ri} }) ];
+  }
+    
+  return $ccb;
+}
+
+sub handle_cvars {
+  my ($self, $entry, %params) = @_;
+
+  my %type_to_column = ( text      => 'text_value',
+                         textfield => 'text_value',
+                         select    => 'text_value',
+                         date      => 'timestamp_value_as_date',
+                         timestamp => 'timestamp_value_as_date',
+                         number    => 'number_value_as_number',
+                         bool      => 'bool_value' );
+
+  $params{sub_module} ||= '';
+  my @cvars;
+  foreach my $config (@{ $self->cvar_configs_by->{row_ident}->{$entry->{raw_data}->{datatype}} }) {
+    next unless exists $entry->{raw_data}->{ "cvar_" . $config->name };
+    my $value  = $entry->{raw_data}->{ "cvar_" . $config->name };
+    my $column = $type_to_column{ $config->type } || die "Program logic error: unknown custom variable storage type";
+
+    push @cvars, SL::DB::CustomVariable->new(config_id => $config->id, $column => $value, sub_module => $params{sub_module});
+  }
+
+  $entry->{object}->custom_variables(\@cvars) if @cvars;
+}
+
+sub init_profile {
+  my ($self) = @_;
+
+  my @profile;
+  foreach my $class (@{ $self->class }) {
+    eval "require " . $class;
+
+    my %unwanted = map { ( $_ => 1 ) } (qw(itime mtime), map { $_->name } @{ $class->meta->primary_key_columns });
+    my %prof;
+    $prof{datatype} = '';
+    for my $col ($class->meta->columns) {
+      next if $unwanted{$col};
+
+      my $name = $col->isa('Rose::DB::Object::Metadata::Column::Numeric')   ? "$col\_as_number"
+          :      $col->isa('Rose::DB::Object::Metadata::Column::Date')      ? "$col\_as_date"
+          :      $col->isa('Rose::DB::Object::Metadata::Column::Timestamp') ? "$col\_as_date"
+          :                                                                   $col->name;
+
+      $prof{$col} = $name;
+    }
+
+    $prof{ 'cvar_' . $_->name } = '' for @{ $self->cvar_configs_by->{class}->{$class} };
+
+    $class =~ m/^SL::DB::(.+)/;
+    push @profile, {'profile' => \%prof, 'class' => $class, 'row_ident' => $::locale->text($1)};
+  }
+
+  \@profile;
+}
+
+sub add_displayable_columns {
+  my ($self, $row_ident, @columns) = @_;
+
+  my $dis_cols = $self->controller->displayable_columns || {};
+
+  my @cols       = @{ $dis_cols->{$row_ident} || [] };
+  my %ex_col_map = map { $_->{name} => $_ } @cols;
+
+  foreach my $column (@columns) {
+    if ($ex_col_map{ $column->{name} }) {
+      @{ $ex_col_map{ $column->{name} } }{ keys %{ $column } } = @{ $column }{ keys %{ $column } };
+    } else {
+      push @cols, $column;
+    }
+  }
+
+  my $by_name_datatype_first = sub { 'datatype' eq $a->{name} ? -1 :
+                                     'datatype' eq $b->{name} ?  1 :
+                                     $a->{name} cmp $b->{name} };
+  $dis_cols->{$row_ident} = [ sort $by_name_datatype_first @cols ];
+
+  $self->controller->displayable_columns($dis_cols);
+}
+
+sub setup_displayable_columns {
+  my ($self) = @_;
+
+  foreach my $p (@{ $self->profile }) {
+    $self->add_displayable_columns($p->{row_ident}, map { { name => $_ } } keys %{ $p->{profile} });
+  }
+}
+
+sub add_cvar_columns_to_displayable_columns {
+  my ($self, $row_ident) = @_;
+
+  $self->add_displayable_columns($row_ident,
+                                 map { { name        => 'cvar_' . $_->name,
+                                         description => $::locale->text('#1 (custom variable)', $_->description) } }
+                                     @{ $self->cvar_configs_by->{row_ident}->{$row_ident} });
+}
+
+sub is_multiplexed { 1 }
+
+1;
+
index 47964936ef340a9393b03540713a09a8c17e8ec4..14b67305fba9703cf1937c56c616f1b1d1604f9a 100644 (file)
@@ -3,6 +3,7 @@ package SL::Controller::CsvImport::CustomerVendor;
 use strict;
 
 use SL::Helper::Csv;
+use SL::Controller::CsvImport::Helper::Consistency;
 use SL::DB::Business;
 use SL::DB::CustomVariable;
 use SL::DB::CustomVariableConfig;
@@ -13,7 +14,7 @@ use parent qw(SL::Controller::CsvImport::Base);
 
 use Rose::Object::MakeMethods::Generic
 (
- 'scalar --get_set_init' => [ qw(table languages_by businesses_by currencies_by) ],
+ 'scalar --get_set_init' => [ qw(table languages_by businesses_by) ],
 );
 
 sub init_table {
@@ -44,12 +45,6 @@ sub init_languages_by {
   return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_languages } } ) } qw(id description article_code) };
 }
 
-sub init_currencies_by {
-  my ($self) = @_;
-
-  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_currencies } } ) } qw(id name) };
-}
-
 sub check_objects {
   my ($self) = @_;
 
@@ -71,7 +66,8 @@ sub check_objects {
     $self->check_language($entry);
     $self->check_business($entry);
     $self->check_payment($entry);
-    $self->check_currency($entry);
+    $self->check_delivery_term($entry);
+    $self->check_currency($entry, take_default => 1);
     $self->handle_cvars($entry);
 
     next if @{ $entry->{errors} };
@@ -104,7 +100,7 @@ sub check_objects {
     $i++;
   }
 
-  $self->add_columns(map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(language business payment));
+  $self->add_columns(map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(language business payment delivery_term));
   $self->add_cvar_raw_data_columns;
 }
 
@@ -162,36 +158,6 @@ sub check_language {
   return 1;
 }
 
-sub check_currency {
-  my ($self, $entry) = @_;
-
-  my $object = $entry->{object};
-
-  # Check whether or not currency ID is valid.
-  if ($object->currency_id && !$self->currencies_by->{id}->{ $object->currency_id }) {
-    push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
-    return 0;
-  }
-
-  # Map name to ID if given.
-  if (!$object->currency_id && $entry->{raw_data}->{currency}) {
-    my $currency = $self->currencies_by->{name}->{  $entry->{raw_data}->{currency} };
-    if (!$currency) {
-      push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
-      return 0;
-    }
-
-    $object->currency_id($currency->id);
-  }
-
-  # Set default currency if none was given.
-  $object->currency_id($self->default_currency_id) if !$object->currency_id;
-
-  $entry->{raw_data}->{currency_id} = $object->currency_id;
-
-  return 1;
-}
-
 sub check_business {
   my ($self, $entry) = @_;
 
@@ -266,7 +232,7 @@ sub init_profile {
   my ($self) = @_;
 
   my $profile = $self->SUPER::init_profile;
-  delete @{$profile}{qw(business datevexport language payment salesman salesman_id taxincluded terms)};
+  delete @{$profile}{qw(business datevexport language payment delivery_term salesman salesman_id taxincluded terms)};
 
   return $profile;
 }
@@ -295,6 +261,8 @@ sub setup_displayable_columns {
                                  { name => 'customernumber',    description => $::locale->text('Customer Number')                 },
                                  { name => 'department_1',      description => $::locale->text('Department 1')                    },
                                  { name => 'department_2',      description => $::locale->text('Department 2')                    },
+                                 { name => 'delivery_term_id',  description => $::locale->text('Delivery terms (database ID)')    },
+                                 { name => 'delivery_term',     description => $::locale->text('Delivery terms (name)')           },
                                  { name => 'direct_debit',      description => $::locale->text('direct debit')                    },
                                  { name => 'discount',          description => $::locale->text('Discount')                        },
                                  { name => 'email',             description => $::locale->text('E-mail')                          },
diff --git a/SL/Controller/CsvImport/Helper/Consistency.pm b/SL/Controller/CsvImport/Helper/Consistency.pm
new file mode 100644 (file)
index 0000000..a666e6f
--- /dev/null
@@ -0,0 +1,69 @@
+package SL::Controller::CsvImport::Helper::Consistency;
+
+use strict;
+
+use SL::DB::Default;
+use SL::DB::Currency;
+
+use SL::Helper::Csv::Error;
+
+use parent qw(Exporter);
+our @EXPORT = qw(check_currency);
+
+#
+# public functions
+#
+
+sub check_currency {
+  my ($self, $entry, %params) = @_;
+
+  my $object = $entry->{object};
+
+  # Check whether or not currency ID is valid.
+  if ($object->currency_id && ! _currencies_by($self)->{id}->{ $object->currency_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
+    return 0;
+  }
+
+  # Map name to ID if given.
+  if (!$object->currency_id && $entry->{raw_data}->{currency}) {
+    my $currency = _currencies_by($self)->{name}->{  $entry->{raw_data}->{currency} };
+    if (!$currency) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid currency');
+      return 0;
+    }
+
+    $object->currency_id($currency->id);
+  }
+
+  # Set default currency if none was given and take_default is true.
+  $object->currency_id(_default_currency_id($self)) if !$object->currency_id and $params{take_default};
+
+  $entry->{raw_data}->{currency_id} = $object->currency_id;
+
+  return 1;
+}
+
+#
+# private functions
+#
+
+sub _currencies_by {
+  my ($self) = @_;
+
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ _all_currencies($self) } } ) } qw(id name) };
+}
+
+sub _all_currencies {
+  my ($self) = @_;
+
+  return SL::DB::Manager::Currency->get_all;
+}
+
+sub _default_currency_id {
+  my ($self) = @_;
+
+  return SL::DB::Default->get->currency_id;
+}
+
+1;
diff --git a/SL/Controller/CsvImport/Order.pm b/SL/Controller/CsvImport/Order.pm
new file mode 100644 (file)
index 0000000..fe73019
--- /dev/null
@@ -0,0 +1,763 @@
+package SL::Controller::CsvImport::Order;
+
+
+use strict;
+
+use List::MoreUtils qw(any);
+
+use SL::Helper::Csv;
+use SL::Controller::CsvImport::Helper::Consistency;
+use SL::DB::Order;
+use SL::DB::OrderItem;
+use SL::DB::Part;
+use SL::DB::PaymentTerm;
+use SL::DB::Contact;
+use SL::DB::Department;
+use SL::DB::PriceFactor;
+use SL::DB::Pricegroup;
+use SL::DB::Project;
+use SL::DB::Shipto;
+use SL::DB::TaxZone;
+use SL::TransNumber;
+
+use parent qw(SL::Controller::CsvImport::BaseMulti);
+
+
+use Rose::Object::MakeMethods::Generic
+(
+ 'scalar --get_set_init' => [ qw(settings languages_by parts_by contacts_by departments_by projects_by ct_shiptos_by taxzones_by price_factors_by pricegroups_by) ],
+);
+
+
+sub init_class {
+  my ($self) = @_;
+  $self->class(['SL::DB::Order', 'SL::DB::OrderItem']);
+}
+
+
+sub init_settings {
+  my ($self) = @_;
+
+  return { map { ( $_ => $self->controller->profile->get($_) ) } qw(order_column item_column max_amount_diff) };
+}
+
+
+sub init_cvar_configs_by {
+  my ($self) = @_;
+
+  my $item_cvar_configs = SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'IC' ]);
+  $item_cvar_configs = [grep { $_->has_flag('editable') } @{ $item_cvar_configs }];
+
+  my $ccb;
+  $ccb->{class}->{'SL::DB::Order'}          = [];
+  $ccb->{class}->{'SL::DB::OrderItem'}      = $item_cvar_configs;
+  $ccb->{row_ident}->{$self->_order_column} = [];
+  $ccb->{row_ident}->{$self->_item_column}  = $item_cvar_configs;
+
+  return $ccb;
+}
+
+
+sub init_profile {
+  my ($self) = @_;
+
+  my $profile = $self->SUPER::init_profile;
+
+  # SUPER::init_profile sets row_ident to the translated class name
+  # overwrite it with the user specified settings
+  foreach my $p (@{ $profile }) {
+    if ($p->{class} eq 'SL::DB::Order') {
+      $p->{row_ident} = $self->_order_column;
+    }
+    if ($p->{class} eq 'SL::DB::OrderItem') {
+      $p->{row_ident} = $self->_item_column;
+    }
+  }
+
+  foreach my $p (@{ $profile }) {
+    my $prof = $p->{profile};
+    if ($p->{row_ident} eq $self->_order_column) {
+      # no need to handle
+      delete @{$prof}{qw(delivery_customer_id delivery_vendor_id proforma quotation amount netamount)};
+    }
+    if ($p->{row_ident} eq $self->_item_column) {
+      # no need to handle
+      delete @{$prof}{qw(trans_id)};
+    }
+  }
+
+  return $profile;
+}
+
+
+sub setup_displayable_columns {
+  my ($self) = @_;
+
+  $self->SUPER::setup_displayable_columns;
+
+  $self->add_displayable_columns($self->_order_column,
+                                 { name => 'datatype',                description => $self->_order_column . ' [1]'                            },
+                                 { name => 'closed',                  description => $::locale->text('Closed')                                },
+                                 { name => 'currency',                description => $::locale->text('Currency')                              },
+                                 { name => 'currency_id',             description => $::locale->text('Currency (database ID)')                },
+                                 { name => 'cusordnumber',            description => $::locale->text('Customer Order Number')                 },
+                                 { name => 'delivered',               description => $::locale->text('Delivered')                             },
+                                 { name => 'delivery_term_id',        description => $::locale->text('Delivery terms (database ID)')          },
+                                 { name => 'delivery_term',           description => $::locale->text('Delivery terms (name)')                 },
+                                 { name => 'employee_id',             description => $::locale->text('Employee (database ID)')                },
+                                 { name => 'intnotes',                description => $::locale->text('Internal Notes')                        },
+                                 { name => 'marge_percent',           description => $::locale->text('Margepercent')                          },
+                                 { name => 'marge_total',             description => $::locale->text('Margetotal')                            },
+                                 { name => 'notes',                   description => $::locale->text('Notes')                                 },
+                                 { name => 'ordnumber',               description => $::locale->text('Order Number')                          },
+                                 { name => 'quonumber',               description => $::locale->text('Quotation Number')                      },
+                                 { name => 'reqdate',                 description => $::locale->text('Reqdate')                               },
+                                 { name => 'salesman_id',             description => $::locale->text('Salesman (database ID)')                },
+                                 { name => 'shippingpoint',           description => $::locale->text('Shipping Point')                        },
+                                 { name => 'shipvia',                 description => $::locale->text('Ship via')                              },
+                                 { name => 'transaction_description', description => $::locale->text('Transaction description')               },
+                                 { name => 'transdate',               description => $::locale->text('Order Date')                            },
+                                 { name => 'verify_amount',           description => $::locale->text('Amount (for verification)') . ' [2]'    },
+                                 { name => 'verify_netamount',        description => $::locale->text('Net amount (for verification)') . ' [2]'},
+                                 { name => 'taxincluded',             description => $::locale->text('Tax Included')                          },
+                                 { name => 'customer',                description => $::locale->text('Customer (name)')                       },
+                                 { name => 'customernumber',          description => $::locale->text('Customer Number')                       },
+                                 { name => 'customer_id',             description => $::locale->text('Customer (database ID)')                },
+                                 { name => 'vendor',                  description => $::locale->text('Vendor (name)')                         },
+                                 { name => 'vendornumber',            description => $::locale->text('Vendor Number')                         },
+                                 { name => 'vendor_id',               description => $::locale->text('Vendor (database ID)')                  },
+                                 { name => 'language_id',             description => $::locale->text('Language (database ID)')                },
+                                 { name => 'language',                description => $::locale->text('Language (name)')                       },
+                                 { name => 'payment_id',              description => $::locale->text('Payment terms (database ID)')           },
+                                 { name => 'payment',                 description => $::locale->text('Payment terms (name)')                  },
+                                 { name => 'taxzone_id',              description => $::locale->text('Tax zone (database ID)')                },
+                                 { name => 'taxzone',                 description => $::locale->text('Tax zone (description)')                },
+                                 { name => 'cp_id',                   description => $::locale->text('Contact Person (database ID)')          },
+                                 { name => 'contact',                 description => $::locale->text('Contact Person (name)')                 },
+                                 { name => 'department_id',           description => $::locale->text('Department (database ID)')              },
+                                 { name => 'department',              description => $::locale->text('Department (description)')              },
+                                 { name => 'globalproject_id',        description => $::locale->text('Document Project (database ID)')        },
+                                 { name => 'globalprojectnumber',     description => $::locale->text('Document Project (number)')             },
+                                 { name => 'globalproject',           description => $::locale->text('Document Project (description)')        },
+                                 { name => 'shipto_id',               description => $::locale->text('Ship to (database ID)')                 },
+                                );
+
+  $self->add_cvar_columns_to_displayable_columns($self->_item_column);
+
+  $self->add_displayable_columns($self->_item_column,
+                                 { name => 'datatype',        description => $self->_item_column . ' [1]'                  },
+                                 { name => 'cusordnumber',    description => $::locale->text('Customer Order Number')      },
+                                 { name => 'description',     description => $::locale->text('Description')                },
+                                 { name => 'discount',        description => $::locale->text('Discount')                   },
+                                 { name => 'lastcost',        description => $::locale->text('Lastcost')                   },
+                                 { name => 'longdescription', description => $::locale->text('Long Description')           },
+                                 { name => 'marge_percent',   description => $::locale->text('Margepercent')               },
+                                 { name => 'marge_total',     description => $::locale->text('Margetotal')                 },
+                                 { name => 'ordnumber',       description => $::locale->text('Order Number')               },
+                                 { name => 'parts_id',        description => $::locale->text('Part (database ID)')         },
+                                 { name => 'partnumber',      description => $::locale->text('Part Number')                },
+                                 { name => 'project_id',      description => $::locale->text('Project (database ID)')      },
+                                 { name => 'projectnumber',   description => $::locale->text('Project (number)')           },
+                                 { name => 'project',         description => $::locale->text('Project (description)')      },
+                                 { name => 'price_factor_id', description => $::locale->text('Price factor (database ID)') },
+                                 { name => 'price_factor',    description => $::locale->text('Price factor (name)')        },
+                                 { name => 'pricegroup_id',   description => $::locale->text('Price group (database ID)')  },
+                                 { name => 'pricegroup',      description => $::locale->text('Price group (name)')         },
+                                 { name => 'qty',             description => $::locale->text('Quantity')                   },
+                                 { name => 'reqdate',         description => $::locale->text('Reqdate')                    },
+                                 { name => 'sellprice',       description => $::locale->text('Sellprice')                  },
+                                 { name => 'serialnumber',    description => $::locale->text('Serial No.')                 },
+                                 { name => 'subtotal',        description => $::locale->text('Subtotal')                   },
+                                 { name => 'unit',            description => $::locale->text('Unit')                       },
+                                );
+}
+
+
+sub init_languages_by {
+  my ($self) = @_;
+
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $self->all_languages } } ) } qw(id description article_code) };
+}
+
+sub init_parts_by {
+  my ($self) = @_;
+
+  my $all_parts = SL::DB::Manager::Part->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_parts } } ) } qw(id partnumber ean description) };
+}
+
+sub init_contacts_by {
+  my ($self) = @_;
+
+  my $all_contacts = SL::DB::Manager::Contact->get_all;
+
+  my $cby;
+  # by customer/vendor id  _and_  contact person id
+  $cby->{'cp_cv_id+cp_id'}   = { map { ( $_->cp_cv_id . '+' . $_->cp_id   => $_ ) } @{ $all_contacts } };
+  # by customer/vendor id  _and_  contact person name
+  $cby->{'cp_cv_id+cp_name'} = { map { ( $_->cp_cv_id . '+' . $_->cp_name => $_ ) } @{ $all_contacts } };
+
+  return $cby;
+}
+
+sub init_departments_by {
+  my ($self) = @_;
+
+  my $all_departments = SL::DB::Manager::Department->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_departments } } ) } qw(id description) };
+}
+
+sub init_projects_by {
+  my ($self) = @_;
+
+  my $all_projects = SL::DB::Manager::Project->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_projects } } ) } qw(id projectnumber description) };
+}
+
+sub init_ct_shiptos_by {
+  my ($self) = @_;
+
+  my $all_ct_shiptos = SL::DB::Manager::Shipto->get_all(query => [module => 'CT']);
+
+  my $sby;
+  # by trans_id  _and_  shipto_id
+  $sby->{'trans_id+shipto_id'} = { map { ( $_->trans_id . '+' . $_->shipto_id => $_ ) } @{ $all_ct_shiptos } };
+
+  return $sby;
+}
+
+sub init_taxzones_by {
+  my ($self) = @_;
+
+  my $all_taxzones = SL::DB::Manager::TaxZone->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_taxzones } } ) } qw(id description) };
+}
+
+sub init_price_factors_by {
+  my ($self) = @_;
+
+  my $all_price_factors = SL::DB::Manager::PriceFactor->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_price_factors } } ) } qw(id description) };
+}
+
+sub init_pricegroups_by {
+  my ($self) = @_;
+
+  my $all_pricegroups = SL::DB::Manager::Pricegroup->get_all;
+  return { map { my $col = $_; ( $col => { map { ( $_->$col => $_ ) } @{ $all_pricegroups } } ) } qw(id pricegroup) };
+}
+
+sub check_objects {
+  my ($self) = @_;
+
+  $self->controller->track_progress(phase => 'building data', progress => 0);
+
+  my $i = 0;
+  my $num_data = scalar @{ $self->controller->data };
+  foreach my $entry (@{ $self->controller->data }) {
+    $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
+
+    if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+      $self->handle_order($entry);
+    } elsif ($entry->{raw_data}->{datatype} eq $self->_item_column && $entry->{object}->can('part')) {
+      $self->handle_item($entry);
+    }
+    $self->handle_cvars($entry, sub_module => 'orderitems');
+
+  } continue {
+    $i++;
+  }
+
+  $self->add_info_columns($self->_order_column,
+                          { header => $::locale->text('Customer/Vendor'), method => 'vc_name' });
+  # Todo: access via ->[0] ok? Better: search first order column and use this
+  $self->add_columns($self->_order_column,
+                     map { "${_}_id" } grep { exists $self->controller->data->[0]->{raw_data}->{$_} } qw(payment delivery_term language department globalproject taxzone cp currency));
+  $self->add_columns($self->_order_column, 'globalproject_id') if exists $self->controller->data->[0]->{raw_data}->{globalprojectnumber};
+  $self->add_columns($self->_order_column, 'cp_id')            if exists $self->controller->data->[0]->{raw_data}->{contact};
+
+  $self->add_info_columns($self->_item_column,
+                          { header => $::locale->text('Part Number'), method => 'partnumber' });
+  # Todo: access via ->[1] ok? Better: search first item column and use this
+  $self->add_columns($self->_item_column,
+                     map { "${_}_id" } grep { exists $self->controller->data->[1]->{raw_data}->{$_} } qw(project price_factor pricegroup));
+  $self->add_columns($self->_item_column, 'project_id') if exists $self->controller->data->[1]->{raw_data}->{projectnumber};
+
+  $self->add_cvar_raw_data_columns();
+
+  $self->add_items_to_order();
+  $self->handle_prices_and_taxes();
+
+
+  # If order has errors set error for orderitems as well
+  my $order_entry;
+  foreach my $entry (@{ $self->controller->data }) {
+    # Search first order
+    if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+      $order_entry = $entry;
+    } elsif ( defined $order_entry
+              && $entry->{raw_data}->{datatype} eq $self->_item_column
+              && scalar @{ $order_entry->{errors} } > 0 ) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid order for this order item');
+    }
+  }
+
+}
+
+sub handle_order {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  my $vc_obj;
+  if (any { $entry->{raw_data}->{$_} } qw(customer customernumber customer_id)) {
+    $self->check_vc($entry, 'customer_id');
+    $vc_obj = SL::DB::Customer->new(id => $object->customer_id)->load if $object->customer_id;
+  } elsif (any { $entry->{raw_data}->{$_} } qw(vendor vendornumber vendor_id)) {
+    $self->check_vc($entry, 'vendor_id');
+    $vc_obj = SL::DB::Vendor->new(id => $object->vendor_id)->load if $object->vendor_id;
+  } else {
+    push @{ $entry->{errors} }, $::locale->text('Error: Customer/vendor missing');
+  }
+
+  $self->check_contact($entry);
+  $self->check_language($entry);
+  $self->check_payment($entry);
+  $self->check_delivery_term($entry);
+  $self->check_department($entry);
+  $self->check_project($entry, global => 1);
+  $self->check_ct_shipto($entry);
+  $self->check_taxzone($entry);
+  $self->check_currency($entry, take_default => 0);
+
+  if ($vc_obj) {
+    # copy from customer if not given
+    foreach (qw(payment_id language_id taxzone_id currency_id)) {
+      $object->$_($vc_obj->$_) unless $object->$_;
+    }
+  }
+
+  $self->handle_salesman($entry);
+  $self->handle_employee($entry);
+}
+
+# ToDo: salesman by name
+sub handle_salesman {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+  my $vc_obj = SL::DB::Customer->new(id => $object->customer_id)->load if $object->customer_id;
+  $vc_obj    = SL::DB::Vendor->new(id   => $object->vendor_id)->load   if (!$vc_obj && $object->vendor_id);
+
+  # salesman from customer/vendor or login if not given
+  if (!$object->salesman) {
+    if ($vc_obj && $vc_obj->salesman_id) {
+      $object->salesman(SL::DB::Manager::Employee->find_by(id => $vc_obj->salesman_id));
+    } else {
+      $object->salesman(SL::DB::Manager::Employee->find_by(login => $::myconfig{login}));
+    }
+  }
+}
+
+# ToDo: employee by name
+sub handle_employee {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # employee from login if not given
+  if (!$object->employee_id) {
+    $object->employee_id(SL::DB::Manager::Employee->find_by(login => $::myconfig{login})->id);
+  }
+}
+
+sub check_language {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check whether or not language ID is valid.
+  if ($object->language_id && !$self->languages_by->{id}->{ $object->language_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid language');
+    return 0;
+  }
+
+  # Map name to ID if given.
+  if (!$object->language_id && $entry->{raw_data}->{language}) {
+    my $language = $self->languages_by->{description}->{  $entry->{raw_data}->{language} }
+                || $self->languages_by->{article_code}->{ $entry->{raw_data}->{language} };
+
+    if (!$language) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid language');
+      return 0;
+    }
+
+    $object->language_id($language->id);
+  }
+
+  if ($object->language_id) {
+    $entry->{info_data}->{language} = $self->languages_by->{id}->{ $object->language_id }->description;
+  }
+
+  return 1;
+}
+
+sub handle_item {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+  return unless $self->check_part($entry);
+
+  my $part_obj = SL::DB::Part->new(id => $object->parts_id)->load;
+
+  # copy from part if not given
+  $object->description($part_obj->description) unless $object->description;
+  $object->longdescription($part_obj->notes)   unless $object->longdescription;
+  $object->unit($part_obj->unit)               unless $object->unit;
+  $object->sellprice($part_obj->sellprice)     unless defined $object->sellprice;
+  $object->lastcost($part_obj->lastcost)       unless defined $object->lastcost;
+
+  # set to 0 if not given
+  $object->discount(0) unless $object->discount;
+  $object->ship(0)     unless $object->ship;
+
+  $self->check_project($entry, global => 0);
+  $self->check_price_factor($entry);
+  $self->check_pricegroup($entry);
+}
+
+sub check_part {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check wether or non part ID is valid.
+  if ($object->parts_id && !$self->parts_by->{id}->{ $object->parts_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
+    return 0;
+  }
+
+  # Map number to ID if given.
+  if (!$object->parts_id && $entry->{raw_data}->{partnumber}) {
+    my $part = $self->parts_by->{partnumber}->{ $entry->{raw_data}->{partnumber} };
+    if (!$part) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid part');
+      return 0;
+    }
+
+    $object->parts_id($part->id);
+  }
+
+  if ($object->parts_id) {
+    $entry->{info_data}->{partnumber} = $self->parts_by->{id}->{ $object->parts_id }->partnumber;
+  } else {
+    push @{ $entry->{errors} }, $::locale->text('Error: Part not found');
+    return 0;
+  }
+
+  return 1;
+}
+
+sub check_contact {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  my $cp_cv_id = $object->customer_id || $object->vendor_id;
+  return 0 unless $cp_cv_id;
+
+  # Check wether or not contact ID is valid.
+  if ($object->cp_id && !$self->contacts_by->{'cp_cv_id+cp_id'}->{ $cp_cv_id . '+' . $object->cp_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid contact');
+    return 0;
+  }
+
+  # Map name to ID if given.
+  if (!$object->cp_id && $entry->{raw_data}->{contact}) {
+    my $cp = $self->contacts_by->{'cp_cv_id+cp_name'}->{ $cp_cv_id . '+' . $entry->{raw_data}->{contact} };
+    if (!$cp) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid contact');
+      return 0;
+    }
+
+    $object->cp_id($cp->cp_id);
+  }
+
+  if ($object->cp_id) {
+    $entry->{info_data}->{contact} = $self->contacts_by->{'cp_cv_id+cp_id'}->{ $cp_cv_id . '+' . $object->cp_id }->cp_name;
+  }
+
+  return 1;
+}
+
+sub check_department {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check wether or not department ID is valid.
+  if ($object->department_id && !$self->departments_by->{id}->{ $object->department_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid department');
+    return 0;
+  }
+
+  # Map description to ID if given.
+  if (!$object->department_id && $entry->{raw_data}->{department}) {
+    my $dep = $self->departments_by->{description}->{ $entry->{raw_data}->{department} };
+    if (!$dep) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid department');
+      return 0;
+    }
+
+    $object->department_id($dep->id);
+  }
+
+  return 1;
+}
+
+sub check_project {
+  my ($self, $entry, %params) = @_;
+
+  my $id_column          = ($params{global} ? 'global' : '') . 'project_id';
+  my $number_column      = ($params{global} ? 'global' : '') . 'projectnumber';
+  my $description_column = ($params{global} ? 'global' : '') . 'project';
+
+  my $object = $entry->{object};
+
+  # Check wether or not projetc ID is valid.
+  if ($object->$id_column && !$self->projects_by->{id}->{ $object->$id_column }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+    return 0;
+  }
+
+  # Map number to ID if given.
+  if (!$object->$id_column && $entry->{raw_data}->{$number_column}) {
+    my $proj = $self->projects_by->{projectnumber}->{ $entry->{raw_data}->{$number_column} };
+    if (!$proj) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+      return 0;
+    }
+
+    $object->$id_column($proj->id);
+  }
+
+  # Map description to ID if given.
+  if (!$object->$id_column && $entry->{raw_data}->{$description_column}) {
+    my $proj = $self->projects_by->{description}->{ $entry->{raw_data}->{$description_column} };
+    if (!$proj) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid project');
+      return 0;
+    }
+
+    $object->$id_column($proj->id);
+  }
+
+  return 1;
+}
+
+sub check_ct_shipto {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  my $trans_id = $object->customer_id || $object->vendor_id;
+  return 0 unless $trans_id;
+
+  # Check wether or not shipto ID is valid.
+  if ($object->shipto_id && !$self->ct_shiptos_by->{'trans_id+shipto_id'}->{ $trans_id . '+' . $object->shipto_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid shipto');
+    return 0;
+  }
+
+  return 1;
+}
+
+sub check_taxzone {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check wether or not taxzone ID is valid.
+  if ($object->taxzone_id && !$self->taxzones_by->{id}->{ $object->taxzone_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid tax zone');
+    return 0;
+  }
+
+  # Map description to ID if given.
+  if (!$object->taxzone_id && $entry->{raw_data}->{taxzone}) {
+    my $taxzone = $self->taxzones_by->{description}->{ $entry->{raw_data}->{taxzone} };
+    if (!$taxzone) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid tax zone');
+      return 0;
+    }
+
+    $object->taxzone_id($taxzone->id);
+  }
+
+  return 1;
+}
+
+sub check_price_factor {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check wether or not price_factor ID is valid.
+  if ($object->price_factor_id && !$self->price_factors_by->{id}->{ $object->price_factor_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid price factor');
+    return 0;
+  }
+
+  # Map description to ID if given.
+  if (!$object->price_factor_id && $entry->{raw_data}->{price_factor}) {
+    my $price_factor = $self->price_factors_by->{description}->{ $entry->{raw_data}->{price_factor} };
+    if (!$price_factor) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid price factor');
+      return 0;
+    }
+
+    $object->price_factor_id($price_factor->id);
+  }
+
+  return 1;
+}
+
+sub check_pricegroup {
+  my ($self, $entry) = @_;
+
+  my $object = $entry->{object};
+
+  # Check wether or not pricegroup ID is valid.
+  if ($object->pricegroup_id && !$self->pricegroups_by->{id}->{ $object->pricegroup_id }) {
+    push @{ $entry->{errors} }, $::locale->text('Error: Invalid price group');
+    return 0;
+  }
+
+  # Map pricegroup to ID if given.
+  if (!$object->pricegroup_id && $entry->{raw_data}->{pricegroup}) {
+    my $pricegroup = $self->pricegroups_by->{pricegroup}->{ $entry->{raw_data}->{pricegroup} };
+    if (!$pricegroup) {
+      push @{ $entry->{errors} }, $::locale->text('Error: Invalid price group');
+      return 0;
+    }
+
+    $object->pricegroup_id($pricegroup->id);
+  }
+
+  return 1;
+}
+
+sub add_items_to_order {
+  my ($self) = @_;
+
+  # add orderitems to order
+  my $order_entry;
+  my @orderitems;
+  foreach my $entry (@{ $self->controller->data }) {
+    # search first order
+    if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+
+      # new order entry: add collected orderitems to the previous one
+      if (defined $order_entry) {
+        $order_entry->{object}->orderitems(@orderitems);
+        @orderitems = ();
+      }
+
+      $order_entry = $entry;
+
+    } elsif ( defined $order_entry && $entry->{raw_data}->{datatype} eq $self->_item_column ) {
+      # collect orderitems to add to order (if they have no errors)
+      # ( add_orderitems does not work here if we want to call
+      #   calculate_prices_and_taxes afterwards ...
+      #   so collect orderitems and add them at once )
+      push @orderitems, $entry->{object} if (scalar @{ $entry->{errors} } == 0);
+    }
+  }
+  # add last collected orderitems to last order
+  $order_entry->{object}->orderitems(@orderitems) if $order_entry;
+}
+
+sub handle_prices_and_taxes() {
+  my ($self) = @_;
+
+  # calculate prices and taxes
+  foreach my $entry (@{ $self->controller->data }) {
+    next if @{ $entry->{errors} };
+
+    if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+
+      $entry->{object}->calculate_prices_and_taxes;
+
+      $entry->{info_data}->{calc_amount}    = $entry->{object}->amount_as_number;
+      $entry->{info_data}->{calc_netamount} = $entry->{object}->netamount_as_number;
+    }
+  }
+
+  # If amounts are given, show calculated amounts as info and given amounts (verify_xxx).
+  # And throw an error if the differences are too big.
+  my @to_verify = ( { column      => 'amount',
+                      raw_column  => 'verify_amount',
+                      info_header => 'Calc. Amount',
+                      info_method => 'calc_amount',
+                      err_msg     => 'Amounts differ too much',
+                    },
+                    { column      => 'netamount',
+                      raw_column  => 'verify_netamount',
+                      info_header => 'Calc. Net amount',
+                      info_method => 'calc_netamount',
+                      err_msg     => 'Net amounts differ too much',
+                    } );
+
+  foreach my $tv (@to_verify) {
+    # Todo: access via ->[0] ok? Better: search first order column and use this
+    if (exists $self->controller->data->[0]->{raw_data}->{ $tv->{raw_column} }) {
+      $self->add_raw_data_columns($self->_order_column, $tv->{raw_column});
+      $self->add_info_columns($self->_order_column,
+                              { header => $::locale->text($tv->{info_header}), method => $tv->{info_method} });
+    }
+
+    # check differences
+    foreach my $entry (@{ $self->controller->data }) {
+      next if @{ $entry->{errors} };
+      if ($entry->{raw_data}->{datatype} eq $self->_order_column) {
+        next if !$entry->{raw_data}->{ $tv->{raw_column} };
+        my $parsed_value = $::form->parse_amount(\%::myconfig, $entry->{raw_data}->{ $tv->{raw_column} });
+        if (abs($entry->{object}->${ \$tv->{column} } - $parsed_value) > $self->settings->{'max_amount_diff'}) {
+          push @{ $entry->{errors} }, $::locale->text($tv->{err_msg});
+        }
+      }
+    }
+  }
+
+}
+
+sub save_objects {
+  my ($self, %params) = @_;
+
+  # set order number and collect to save
+  my $objects_to_save;
+  foreach my $entry (@{ $self->controller->data }) {
+    next if $entry->{raw_data}->{datatype} ne $self->_order_column;
+    next if @{ $entry->{errors} };
+
+    if (!$entry->{object}->ordnumber) {
+      my $number = SL::TransNumber->new(type => 'sales_order',
+                                        save => 1);
+      $entry->{object}->ordnumber($number->create_unique());
+    }
+
+    push @{ $objects_to_save }, $entry;
+  }
+
+  $self->SUPER::save_objects(data => $objects_to_save);
+}
+
+sub _order_column {
+  $_[0]->settings->{'order_column'}
+}
+
+sub _item_column {
+  $_[0]->settings->{'item_column'}
+}
+
+1;
index da4b7c9d5d1c99ac187953cb725a36bf770a4192..de7749037cc16b2f8f5e401df6ac01e222f5028b 100644 (file)
@@ -147,6 +147,19 @@ sub _save {
       employee_id => SL::DB::Manager::Employee->current->id,
       addition => 'SAVED',
     )->save();
+
+    if ( $::form->{delete_notes} ) {
+      foreach my $note_id (@{ $::form->{delete_notes} }) {
+        my $note = SL::DB::Note->new(id => $note_id)->load();
+        if ( $note->follow_up ) {
+          if ( $note->follow_up->follow_up_link ) {
+            $note->follow_up->follow_up_link->delete(cascade => 'delete');
+          }
+          $note->follow_up->delete(cascade => 'delete');
+        }
+        $note->delete(cascade => 'delete');
+      }
+    }
   }) || die($db->error);
 
 }
@@ -170,10 +183,6 @@ sub action_save {
     push(@redirect_params, shipto_id => $self->{shipto}->shipto_id);
   }
 
-  if ( $self->{note}->id ) {
-    push(@redirect_params, note_id => $self->{note}->id);
-  }
-
   $self->redirect_to(@redirect_params);
 }
 
@@ -385,6 +394,8 @@ sub action_search_contact {
 sub action_get_delivery {
   my ($self) = @_;
 
+  $::auth->assert('sales_all_edit');
+
   my $dbh = $::form->get_standard_dbh();
 
   my ($arap, $db, $qty_sign);
@@ -398,21 +409,24 @@ sub action_get_delivery {
     $qty_sign = '';
   }
 
-  my $where = ' WHERE 1=1 ';
+  my $where = ' WHERE 1=1';
   my @values;
 
   if ( !$self->is_vendor() && $::form->{shipto_id} && $::form->{shipto_id} ne 'all' ) {
-    $where .= "AND ${arap}.shipto_id = ?";
+    $where .= " AND ${arap}.shipto_id = ?";
     push(@values, $::form->{shipto_id});
+  } else {
+    $where .= " AND ${arap}.${db}_id = ?";
+    push(@values, $::form->{id});
   }
 
   if ( $::form->{delivery_from} ) {
-    $where .= "AND ${arap}.transdate >= ?";
+    $where .= " AND ${arap}.transdate >= ?";
     push(@values, conv_date($::form->{delivery_from}));
   }
 
   if ( $::form->{delivery_to} ) {
-    $where .= "AND ${arap}.transdate <= ?";
+    $where .= " AND ${arap}.transdate <= ?";
     push(@values, conv_date($::form->{delivery_to}));
   }
 
@@ -626,17 +640,8 @@ sub _instantiate_args {
 
   if ( $::form->{note}->{id} ) {
     $self->{note} = SL::DB::Note->new(id => $::form->{note}->{id})->load();
-
-    $self->{note_followup_link} = SL::DB::Manager::FollowUpLink->get_all(
-      query => [
-        'follow_up.note_id' => $self->{note}->id,
-        trans_id => $self->{cv}->id,
-        trans_type => ($self->is_vendor() ? 'vendor' : 'customer'),
-      ],
-      with_objects => ['follow_up'],
-    )->[0];
-
-    $self->{note_followup} = $self->{note_followup_link}->follow_up;
+    $self->{note_followup} = $self->{note}->follow_up;
+    $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
   } else {
     $self->{note} = SL::DB::Note->new();
     $self->{note_followup} = SL::DB::FollowUp->new();
@@ -691,17 +696,8 @@ sub _load_customer_vendor {
 
   if ( $::form->{note_id} ) {
     $self->{note} = SL::DB::Note->new(id => $::form->{note_id})->load();
-
-    $self->{note_followup_link} = SL::DB::Manager::FollowUpLink->get_all(
-      query => [
-        'follow_up.note_id' => $self->{note}->id,
-        trans_id => $self->{cv}->id,
-        trans_type => ($self->is_vendor() ? 'vendor' : 'customer'),
-      ],
-      with_objects => ['follow_up'],
-    )->[0];
-
-    $self->{note_followup} = $self->{note_followup_link}->follow_up;
+    $self->{note_followup} = $self->{note}->follow_up;
+    $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
   } else {
     $self->{note} = SL::DB::Note->new();
     $self->{note_followup} = SL::DB::FollowUp->new();
@@ -816,6 +812,8 @@ sub _pre_render {
 
   $self->{all_payment_terms} = SL::DB::Manager::PaymentTerm->get_all();
 
+  $self->{all_delivery_terms} = SL::DB::Manager::DeliveryTerm->get_all();
+
   $self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all();
 
   $query =
@@ -836,6 +834,14 @@ sub _pre_render {
   $self->{shiptos} = $self->{cv}->shipto;
   $self->{shiptos} ||= [];
 
+  $self->{notes} = SL::DB::Manager::Note->get_all(
+    query => [
+      trans_id => $self->{cv}->id,
+      trans_module => 'ct',
+    ],
+    with_objects => ['follow_up'],
+  );
+
   $self->{template_args} ||= {};
 
   $::request->{layout}->add_javascripts('autocomplete_customer.js');
diff --git a/SL/Controller/DeliveryTerm.pm b/SL/Controller/DeliveryTerm.pm
new file mode 100644 (file)
index 0000000..4810d21
--- /dev/null
@@ -0,0 +1,123 @@
+package SL::Controller::DeliveryTerm;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+
+use SL::DB::DeliveryTerm;
+use SL::DB::Language;
+use SL::Helper::Flash;
+
+use Rose::Object::MakeMethods::Generic
+(
+ scalar => [ qw(delivery_term languages) ],
+);
+
+__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('load_delivery_term', only => [ qw(edit update destroy) ]);
+__PACKAGE__->run_before('load_languages',     only => [ qw(new list edit create update) ]);
+
+
+#
+# actions
+#
+
+sub action_list {
+  my ($self) = @_;
+
+  $self->render('delivery_term/list',
+                title          => $::locale->text('Delivery terms'),
+                DELIVERY_TERMS => SL::DB::Manager::DeliveryTerm->get_all_sorted);
+}
+
+sub action_new {
+  my ($self) = @_;
+
+  $self->{delivery_term} = SL::DB::DeliveryTerm->new;
+  $self->render('delivery_term/form', title => $::locale->text('Create a new delivery term'));
+}
+
+sub action_edit {
+  my ($self) = @_;
+  $self->render('delivery_term/form', title => $::locale->text('Edit delivery term'));
+}
+
+sub action_create {
+  my ($self) = @_;
+
+  $self->{delivery_term} = SL::DB::DeliveryTerm->new;
+  $self->create_or_update;
+}
+
+sub action_update {
+  my ($self) = @_;
+  $self->create_or_update;
+}
+
+sub action_destroy {
+  my ($self) = @_;
+
+  if (eval { $self->{delivery_term}->delete; 1; }) {
+    flash_later('info',  $::locale->text('The delivery term has been deleted.'));
+  } else {
+    flash_later('error', $::locale->text('The delivery term is in use and cannot be deleted.'));
+  }
+
+  $self->redirect_to(action => 'list');
+}
+
+sub action_reorder {
+  my ($self) = @_;
+
+  SL::DB::DeliveryTerm->reorder_list(@{ $::form->{delivery_term_id} || [] });
+
+  $self->render(\'', { type => 'json' });     # ' make Emacs happy
+}
+
+#
+# filters
+#
+
+sub check_auth {
+  $::auth->assert('config');
+}
+
+#
+# helpers
+#
+
+sub create_or_update {
+  my $self   = shift;
+  my $is_new = !$self->{delivery_term}->id;
+  my $params = delete($::form->{delivery_term}) || { };
+
+  $self->{delivery_term}->assign_attributes(%{ $params });
+
+  my @errors = $self->{delivery_term}->validate;
+
+  if (@errors) {
+    flash('error', @errors);
+    $self->render('delivery_term/form', title => $is_new ? $::locale->text('Create a new delivery term') : $::locale->text('Edit delivery term'));
+    return;
+  }
+
+  $self->{delivery_term}->save;
+  foreach my $language (@{ $self->{languages} }) {
+    $self->{delivery_term}->save_attribute_translation('description_long', $language, $::form->{"translation_" . $language->id});
+  }
+
+  flash_later('info', $is_new ? $::locale->text('The delivery term has been created.') : $::locale->text('The delivery term has been saved.'));
+  $self->redirect_to(action => 'list');
+}
+
+sub load_delivery_term {
+  my ($self) = @_;
+  $self->{delivery_term} = SL::DB::DeliveryTerm->new(id => $::form->{id})->load;
+}
+
+sub load_languages {
+  my ($self) = @_;
+  $self->{languages} = SL::DB::Manager::Language->get_all_sorted;
+}
+
+1;
index 21dd4842d825a464296dbca931b41d8793619ea3..1902a51ec4ca5cba76eee76cb25a9b4289db19d0 100644 (file)
@@ -54,6 +54,11 @@ sub set_defaults {
                          default_buchungsgruppe    => ($bugru ? $bugru->id : undef),
                          apply_buchungsgruppe      => 'all',
                         );
+  } elsif ($self->type eq 'orders') {
+    $self->_set_defaults(order_column    => $::locale->text('Order'),
+                         item_column     => $::locale->text('OrderItem'),
+                         max_amount_diff => 0.02,
+                        );
   } else {
     $self->_set_defaults(table => 'customer');
   }
diff --git a/SL/DB/DeliveryTerm.pm b/SL/DB/DeliveryTerm.pm
new file mode 100644 (file)
index 0000000..68c5d92
--- /dev/null
@@ -0,0 +1,24 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::DeliveryTerm;
+
+use strict;
+
+use SL::DB::MetaSetup::DeliveryTerm;
+use SL::DB::Manager::DeliveryTerm;
+use SL::DB::Helper::ActsAsList;
+use SL::DB::Helper::TranslatedAttributes;
+
+
+sub validate {
+  my ($self) = @_;
+
+  my @errors;
+  push @errors, $::locale->text('The description is missing.')      if !$self->description;
+  push @errors, $::locale->text('The long description is missing.') if !$self->description_long;
+
+  return @errors;
+}
+
+1;
index 734ebedba2e8a2c8d36cc14f28b7cb83820f0443..b7121b237d522e0f3e6c9720d1870b48e4edf103 100644 (file)
@@ -7,6 +7,14 @@ use strict;
 
 use SL::DB::MetaSetup::FollowUp;
 
+__PACKAGE__->meta->add_relationships(
+  follow_up_link => {
+    type         => 'one to one',
+    class        => 'SL::DB::FollowUpLink',
+    column_map   => { id => 'follow_up_id' },
+  },
+);
+
 __PACKAGE__->meta->initialize;
 
 # Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
index 325a96cc2015ad6de0b032acbf4fc156d52cd5a1..446356775337b555d247d152c56f4c55f80c3cf2 100644 (file)
@@ -36,6 +36,7 @@ use SL::DB::Default;
 use SL::DB::DeliveryOrder;
 use SL::DB::DeliveryOrderItem;
 use SL::DB::DeliveryOrderItemsStock;
+use SL::DB::DeliveryTerm;
 use SL::DB::Department;
 use SL::DB::Draft;
 use SL::DB::Dunning;
index 95152593bd3b94ca0a4256b33f76fb7221430d8f..105cf020f9265c9d09f30ea897eaf0ca38f4648b 100644 (file)
@@ -17,7 +17,7 @@ sub flatten_to_form {
   _copy($self, $form, '', '', 0, qw(id type taxzone_id ordnumber quonumber invnumber donumber cusordnumber taxincluded shippingpoint shipvia notes intnotes cp_id
                                     employee_id salesman_id closed department_id language_id payment_id delivery_customer_id delivery_vendor_id shipto_id proforma
                                     globalproject_id delivered transaction_description container_type accepted_by_customer invoice terms storno storno_id dunning_config_id
-                                    orddate quodate reqdate gldate duedate deliverydate datepaid transdate));
+                                    orddate quodate reqdate gldate duedate deliverydate datepaid transdate delivery_term_id));
   $form->{currency} = $form->{curr} = $self->currency_id ? $self->currency->name || '' : '';
 
   if (_has($self, 'transdate')) {
index 3842af6e76dddb84f47d1c99f309d50cfebae218..7f7269923833951288cd1ef1ab740f46888b2f3c 100644 (file)
@@ -117,7 +117,7 @@ sub new_from {
 
   my %args = ( map({ ( $_ => $source->$_ ) } qw(customer_id taxincluded shippingpoint shipvia notes intnotes salesman_id cusordnumber ordnumber quonumber
                                                 department_id cp_id language_id payment_id delivery_customer_id delivery_vendor_id taxzone_id shipto_id
-                                                globalproject_id transaction_description currency_id)),
+                                                globalproject_id transaction_description currency_id delivery_term_id)),
                transdate   => DateTime->today_local,
                gldate      => DateTime->today_local,
                duedate     => DateTime->today_local->add(days => $terms * 1),
diff --git a/SL/DB/Manager/DeliveryTerm.pm b/SL/DB/Manager/DeliveryTerm.pm
new file mode 100644 (file)
index 0000000..6ac2f0a
--- /dev/null
@@ -0,0 +1,21 @@
+package SL::DB::Manager::DeliveryTerm;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Sorted;
+
+sub object_class { 'SL::DB::DeliveryTerm' }
+
+__PACKAGE__->make_manager_methods;
+
+sub _sort_spec {
+  return ( default => [ 'sortkey', 1 ],
+           columns => { SIMPLE => 'ALL',
+                        map { ( $_ => "lower(delivery_terms.${_})" ) } qw(description description_long),
+                      });
+}
+
+1;
index 9b2fc3972d565d536d5c1940d9c66e19d9e21c3c..3a8433c51dfebaf1fc6ca33993960355ab272841 100644 (file)
@@ -11,6 +11,7 @@ __PACKAGE__->meta->table('csv_import_reports');
 __PACKAGE__->meta->columns(
   file       => { type => 'text', not_null => 1 },
   id         => { type => 'serial', not_null => 1 },
+  numheaders => { type => 'integer', not_null => 1 },
   numrows    => { type => 'integer', not_null => 1 },
   profile_id => { type => 'integer', not_null => 1 },
   session_id => { type => 'text', not_null => 1 },
index 1752d2a162a4d9ce45b11df423cf6556e052b9a7..2d5a155b9b7578774b099dbcfbcbe564ca47ed07 100644 (file)
@@ -23,6 +23,7 @@ __PACKAGE__->meta->columns(
   creditlimit         => { type => 'numeric', default => '0', precision => 5, scale => 15 },
   currency_id         => { type => 'integer', not_null => 1 },
   customernumber      => { type => 'text' },
+  delivery_term_id    => { type => 'integer' },
   department_1        => { type => 'varchar', length => 75 },
   department_2        => { type => 'varchar', length => 75 },
   direct_debit        => { type => 'boolean', default => 'false' },
@@ -71,6 +72,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { currency_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   language_obj => {
     class       => 'SL::DB::Language',
     key_columns => { language_id => 'id' },
index 92d45ae14860ea4ff6bd0aec81c8d90489d666c0..a1efe9a84ac25835fc73cca675f1ffc3ddb49fc3 100644 (file)
@@ -15,6 +15,7 @@ __PACKAGE__->meta->columns(
   cusordnumber            => { type => 'text' },
   customer_id             => { type => 'integer' },
   delivered               => { type => 'boolean', default => 'false' },
+  delivery_term_id        => { type => 'integer' },
   department_id           => { type => 'integer' },
   donumber                => { type => 'text', not_null => 1 },
   employee_id             => { type => 'integer' },
@@ -61,6 +62,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { customer_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   department => {
     class       => 'SL::DB::Department',
     key_columns => { department_id => 'id' },
diff --git a/SL/DB/MetaSetup/DeliveryTerm.pm b/SL/DB/MetaSetup/DeliveryTerm.pm
new file mode 100644 (file)
index 0000000..ebcc7ea
--- /dev/null
@@ -0,0 +1,27 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::DeliveryTerm;
+
+use strict;
+
+use base qw(SL::DB::Object);
+
+__PACKAGE__->meta->setup(
+  table   => 'delivery_terms',
+
+  columns => [
+    id               => { type => 'integer', not_null => 1, sequence => 'id' },
+    description      => { type => 'text' },
+    description_long => { type => 'text' },
+    sortkey          => { type => 'integer', not_null => 1 },
+    itime            => { type => 'timestamp', default => 'now()' },
+    mtime            => { type => 'timestamp' },
+  ],
+
+  primary_key_columns => [ 'id' ],
+
+  allow_inline_column_values => 1,
+);
+
+1;
+;
index fc461512c8b6e915edad47b8349bd08663eaa6ef..bda14ec5a82919114086985a85a8a01a45c4ac87 100644 (file)
@@ -24,14 +24,14 @@ __PACKAGE__->meta->primary_key_columns([ 'id' ]);
 __PACKAGE__->meta->allow_inline_column_values(1);
 
 __PACKAGE__->meta->foreign_keys(
-  employee => {
+  created_for => {
     class       => 'SL::DB::Employee',
-    key_columns => { created_by => 'id' },
+    key_columns => { created_for_user => 'id' },
   },
 
-  employee_obj => {
+  employee => {
     class       => 'SL::DB::Employee',
-    key_columns => { created_for_user => 'id' },
+    key_columns => { created_by => 'id' },
   },
 
   note => {
index 33f7121fd8ded3f83b9a20fcf14a8dc8eaf07149..14f386a7a6251bb0a8636710c93f585202a16f74 100644 (file)
@@ -37,6 +37,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { department_id => 'id' },
   },
 
+  employee => {
+    class       => 'SL::DB::Employee',
+    key_columns => { employee_id => 'id' },
+  },
+
   storno_obj => {
     class       => 'SL::DB::GLTransaction',
     key_columns => { storno_id => 'id' },
index eedecb4ad199a47b837c8d1aa36a3357b2918dac..1a8d1f00d4db9c115809f01933b80c21a5091cff 100644 (file)
@@ -16,6 +16,7 @@ __PACKAGE__->meta->columns(
   customer_id               => { type => 'integer' },
   datepaid                  => { type => 'date' },
   delivery_customer_id      => { type => 'integer' },
+  delivery_term_id          => { type => 'integer' },
   delivery_vendor_id        => { type => 'integer' },
   deliverydate              => { type => 'date' },
   department_id             => { type => 'integer' },
@@ -78,6 +79,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { customer_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   department => {
     class       => 'SL::DB::Department',
     key_columns => { department_id => 'id' },
index 249ea62fc75cd50be97765b89845fae77f009666..3e4a3102fcbde6c9974c4e173af2bafdbed71269 100644 (file)
@@ -17,6 +17,7 @@ __PACKAGE__->meta->columns(
   customer_id             => { type => 'integer' },
   delivered               => { type => 'boolean', default => 'false' },
   delivery_customer_id    => { type => 'integer' },
+  delivery_term_id        => { type => 'integer' },
   delivery_vendor_id      => { type => 'integer' },
   department_id           => { type => 'integer' },
   employee_id             => { type => 'integer' },
@@ -72,6 +73,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { delivery_customer_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   delivery_vendor => {
     class       => 'SL::DB::Vendor',
     key_columns => { delivery_vendor_id => 'id' },
index 4b89dca767a9dff7d59c2a0cf42d4738fe6aa56e..5aa2785668d2af22838ba1740e9e4e1905170971 100644 (file)
@@ -13,6 +13,7 @@ __PACKAGE__->meta->columns(
   cp_id                   => { type => 'integer' },
   currency_id             => { type => 'integer', not_null => 1 },
   datepaid                => { type => 'date' },
+  delivery_term_id        => { type => 'integer' },
   deliverydate            => { type => 'date' },
   department_id           => { type => 'integer' },
   direct_debit            => { type => 'boolean', default => 'false' },
@@ -61,6 +62,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { currency_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   department => {
     class       => 'SL::DB::Department',
     key_columns => { department_id => 'id' },
index 1a691ff6d2c68076568bc5003c3399f442b3798f..f2988ad74de66e863790bd00252a8e42bce903ec 100644 (file)
@@ -9,49 +9,50 @@ use base qw(SL::DB::Object);
 __PACKAGE__->meta->table('vendor');
 
 __PACKAGE__->meta->columns(
-  account_number => { type => 'varchar', length => 15 },
-  bank           => { type => 'text' },
-  bank_code      => { type => 'varchar', length => 10 },
-  bcc            => { type => 'text' },
-  bic            => { type => 'varchar', length => 100 },
-  business_id    => { type => 'integer' },
-  cc             => { type => 'text' },
-  city           => { type => 'varchar', length => 75 },
-  contact        => { type => 'varchar', length => 75 },
-  country        => { type => 'varchar', length => 75 },
-  creditlimit    => { type => 'numeric', precision => 5, scale => 15 },
-  currency_id    => { type => 'integer', not_null => 1 },
-  department_1   => { type => 'varchar', length => 75 },
-  department_2   => { type => 'varchar', length => 75 },
-  direct_debit   => { type => 'boolean', default => 'false' },
-  discount       => { type => 'float', precision => 4 },
-  email          => { type => 'text' },
-  fax            => { type => 'varchar', length => 30 },
-  greeting       => { type => 'text' },
-  homepage       => { type => 'text' },
-  iban           => { type => 'varchar', length => 100 },
-  id             => { type => 'integer', not_null => 1, sequence => 'id' },
-  itime          => { type => 'timestamp', default => 'now()' },
-  language       => { type => 'varchar', length => 5 },
-  language_id    => { type => 'integer' },
-  mtime          => { type => 'timestamp' },
-  name           => { type => 'varchar', length => 75, not_null => 1 },
-  notes          => { type => 'text' },
-  obsolete       => { type => 'boolean', default => 'false' },
-  payment_id     => { type => 'integer' },
-  phone          => { type => 'text' },
-  salesman_id    => { type => 'integer' },
-  street         => { type => 'varchar', length => 75 },
-  taxincluded    => { type => 'boolean' },
-  taxnumber      => { type => 'text' },
-  taxzone_id     => { type => 'integer', default => '0', not_null => 1 },
-  terms          => { type => 'integer', default => '0' },
-  user_password  => { type => 'varchar', length => 12 },
-  username       => { type => 'varchar', length => 50 },
-  ustid          => { type => 'varchar', length => 14 },
-  v_customer_id  => { type => 'text' },
-  vendornumber   => { type => 'text' },
-  zipcode        => { type => 'varchar', length => 10 },
+  account_number   => { type => 'varchar', length => 15 },
+  bank             => { type => 'text' },
+  bank_code        => { type => 'varchar', length => 10 },
+  bcc              => { type => 'text' },
+  bic              => { type => 'varchar', length => 100 },
+  business_id      => { type => 'integer' },
+  cc               => { type => 'text' },
+  city             => { type => 'varchar', length => 75 },
+  contact          => { type => 'varchar', length => 75 },
+  country          => { type => 'varchar', length => 75 },
+  creditlimit      => { type => 'numeric', precision => 5, scale => 15 },
+  currency_id      => { type => 'integer', not_null => 1 },
+  delivery_term_id => { type => 'integer' },
+  department_1     => { type => 'varchar', length => 75 },
+  department_2     => { type => 'varchar', length => 75 },
+  direct_debit     => { type => 'boolean', default => 'false' },
+  discount         => { type => 'float', precision => 4 },
+  email            => { type => 'text' },
+  fax              => { type => 'varchar', length => 30 },
+  greeting         => { type => 'text' },
+  homepage         => { type => 'text' },
+  iban             => { type => 'varchar', length => 100 },
+  id               => { type => 'integer', not_null => 1, sequence => 'id' },
+  itime            => { type => 'timestamp', default => 'now()' },
+  language         => { type => 'varchar', length => 5 },
+  language_id      => { type => 'integer' },
+  mtime            => { type => 'timestamp' },
+  name             => { type => 'varchar', length => 75, not_null => 1 },
+  notes            => { type => 'text' },
+  obsolete         => { type => 'boolean', default => 'false' },
+  payment_id       => { type => 'integer' },
+  phone            => { type => 'text' },
+  salesman_id      => { type => 'integer' },
+  street           => { type => 'varchar', length => 75 },
+  taxincluded      => { type => 'boolean' },
+  taxnumber        => { type => 'text' },
+  taxzone_id       => { type => 'integer', default => '0', not_null => 1 },
+  terms            => { type => 'integer', default => '0' },
+  user_password    => { type => 'varchar', length => 12 },
+  username         => { type => 'varchar', length => 50 },
+  ustid            => { type => 'varchar', length => 14 },
+  v_customer_id    => { type => 'text' },
+  vendornumber     => { type => 'text' },
+  zipcode          => { type => 'varchar', length => 10 },
 );
 
 __PACKAGE__->meta->primary_key_columns([ 'id' ]);
@@ -69,6 +70,11 @@ __PACKAGE__->meta->foreign_keys(
     key_columns => { currency_id => 'id' },
   },
 
+  delivery_term => {
+    class       => 'SL::DB::DeliveryTerm',
+    key_columns => { delivery_term_id => 'id' },
+  },
+
   language_obj => {
     class       => 'SL::DB::Language',
     key_columns => { language_id => 'id' },
index 441a2731ef0fd87ef6179b11d96bbca9ee962960..c539d6a0510c4367a248f519b2ac76f5ffec0a11 100644 (file)
@@ -7,17 +7,19 @@ use strict;
 
 use SL::DB::MetaSetup::Note;
 
-__PACKAGE__->meta->initialize;
-
-# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
-__PACKAGE__->meta->make_manager_class;
 
 __PACKAGE__->meta->add_relationships(
-  followup => {
+  follow_up => {
     type         => 'one to one',
     class        => 'SL::DB::FollowUp',
-    column_map   => { id => 'id' },
+    column_map   => { id => 'note_id' },
   },
 );
 
+__PACKAGE__->meta->initialize;
+
+# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
+__PACKAGE__->meta->make_manager_class;
+
+
 1;
index 7d3a5e61d902c7c16238a4b8b56b848d10cc0dfc..d83c49b3ef1b46e57887e76a61270ab3a09b0f74 100644 (file)
--- a/SL/DO.pm
+++ b/SL/DO.pm
@@ -145,6 +145,16 @@ sub transactions {
     push @values, conv_date($form->{transdateto});
   }
 
+  if($form->{reqdatefrom}) {
+    push @where,  qq|dord.reqdate >= ?|;
+    push @values, conv_date($form->{reqdatefrom});
+  }
+
+  if($form->{reqdateto}) {
+    push @where,  qq|dord.reqdate <= ?|;
+    push @values, conv_date($form->{reqdateto});
+  }
+
   if (@where) {
     $query .= " WHERE " . join(" AND ", map { "($_)" } @where);
   }
@@ -357,7 +367,8 @@ sub save {
          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, closed = ?,
          delivered = ?, department_id = ?, language_id = ?, shipto_id = ?,
          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?,
-         is_sales = ?, taxzone_id = ?, taxincluded = ?, terms = ?, currency_id = (SELECT id FROM currencies WHERE name = ?)
+         is_sales = ?, taxzone_id = ?, taxincluded = ?, terms = ?, currency_id = (SELECT id FROM currencies WHERE name = ?),
+         delivery_term_id = ?
        WHERE id = ?|;
 
   @values = ($form->{donumber}, $form->{ordnumber},
@@ -372,6 +383,7 @@ sub save {
              $form->{transaction_description},
              $form->{type} =~ /^sales/ ? 't' : 'f',
              conv_i($form->{taxzone_id}), $form->{taxincluded} ? 't' : 'f', conv_i($form->{terms}), $form->{currency},
+             conv_i($form->{delivery_term_id}),
              conv_i($form->{id}));
   do_query($form, $dbh, $query, @values);
 
@@ -582,7 +594,8 @@ sub retrieve {
          d.description AS department, dord.language_id,
          dord.shipto_id,
          dord.globalproject_id, dord.delivered, dord.transaction_description,
-         dord.taxzone_id, dord.taxincluded, dord.terms, (SELECT cu.name FROM currencies cu WHERE cu.id=dord.currency_id) AS currency
+         dord.taxzone_id, dord.taxincluded, dord.terms, (SELECT cu.name FROM currencies cu WHERE cu.id=dord.currency_id) AS currency,
+         dord.delivery_term_id
        FROM delivery_orders dord
        JOIN ${vc} cv ON (dord.${vc}_id = cv.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
@@ -881,6 +894,9 @@ sub order_details {
   $h_pg->finish();
   $h_bin_wh->finish();
 
+  $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+  $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
   $form->{username} = $myconfig->{name};
 
   $main::lxdebug->leave_sub();
index 594d57c7f8ff1bda887b76440fde6c83bb24ef99..f97e2b2489da2c586a8fa221bd0a57764ad46300 100644 (file)
--- a/SL/GL.pm
+++ b/SL/GL.pm
@@ -789,23 +789,26 @@ sub get_chart_balances {
 }
 
 sub get_tax_dropdown {
+  my ($self, $accno) = @_;
+
   my $myconfig = \%main::myconfig;
   my $form = $main::form;
 
   my $dbh = $form->get_standard_dbh($myconfig);
 
   my $query = qq|SELECT category FROM chart WHERE accno = ?|;
-  my ($category) = selectrow_query($form, $dbh, $query, $form->{accno});
+  my ($category) = selectrow_query($form, $dbh, $query, $accno);
 
   $query = qq|SELECT * FROM tax WHERE chart_categories like '%$category%' order by taxkey, rate|;
 
   my $sth = prepare_execute_query($form, $dbh, $query);
 
-  $form->{TAX_ACCOUNTS} = [];
+  my @tax_accounts = ();
   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-    push(@{ $form->{TAX_ACCOUNTS} }, $ref);
+    push(@tax_accounts, $ref);
   }
 
+  return @tax_accounts;
 }
 
 1;
index 3f4eaaaa3727de05a6500fd0ea73a59ecc3f862a..2730502b385d3cdaebc28d5b7aeb73675deb49fb 100644 (file)
@@ -7,11 +7,12 @@ use version 0.77;
 use Carp;
 use IO::File;
 use Params::Validate qw(:all);
+use List::MoreUtils qw(all pairwise);
 use Text::CSV_XS;
 use Rose::Object::MakeMethods::Generic scalar => [ qw(
-  file encoding sep_char quote_char escape_char header profile class
-  numberformat dateformat ignore_unknown_columns strict_profile _io _csv
-  _objects _parsed _data _errors all_cvar_configs case_insensitive_header
+  file encoding sep_char quote_char escape_char header profile
+  numberformat dateformat ignore_unknown_columns strict_profile is_multiplexed
+  _row_header _io _csv _objects _parsed _data _errors all_cvar_configs case_insensitive_header
 ) ];
 
 use SL::Helper::Csv::Dispatcher;
@@ -26,10 +27,9 @@ sub new {
     quote_char             => { default => '"' },
     escape_char            => { default => '"' },
     header                 => { type    => ARRAYREF, optional => 1 },
-    profile                => { type    => HASHREF,  optional => 1 },
+    profile                => { type    => ARRAYREF, optional => 1 },
     file                   => 1,
     encoding               => 0,
-    class                  => 0,
     numberformat           => 0,
     dateformat             => 0,
     ignore_unknown_columns => 0,
@@ -57,6 +57,7 @@ sub parse {
   my ($self, %params) = @_;
 
   $self->_open_file;
+  return if ! $self->_check_multiplexed;
   return if ! $self->_check_header;
   return if ! $self->dispatcher->parse_profile;
   return if ! $self->_parse_data;
@@ -71,7 +72,6 @@ sub get_data {
 
 sub get_objects {
   my ($self, %params) = @_;
-  croak 'no class given'   unless $self->class;
   croak 'must parse first' unless $self->_parsed;
 
   $self->_make_objects unless $self->_objects;
@@ -99,29 +99,95 @@ sub _open_file {
   return $self->_io;
 }
 
-sub _check_header {
+# check, if data is multiplexed and if all nessesary infos are given
+sub _check_multiplexed {
   my ($self, %params) = @_;
-  my $header = $self->header;
 
-  if (! $header) {
-    $header = $self->_csv->getline($self->_io);
+  $self->is_multiplexed(0);
+
+  # If more than one profile is given, it is multiplexed.
+  if ($self->profile) {
+    my @profile = @{ $self->profile };
+    if (scalar @profile > 1) {
+      # Each profile needs a class and a row_ident
+      my $info_ok = all { defined $_->{class} && defined $_->{row_ident} } @profile;
+      $self->_push_error([
+        0,
+        "missing class or row_ident in one of the profiles for multiplexed data",
+        0,
+        0]) unless $info_ok;
+
+      # If header is given, there need to be a header for each profile
+      # and no empty headers.
+      if ($info_ok && $self->header) {
+        my @header = @{ $self->header };
+        my $t_ok = scalar @profile == scalar @header;
+        $self->_push_error([
+          0,
+          "number of headers and number of profiles must be the same for multiplexed data",
+          0,
+          0]) unless $t_ok;
+        $info_ok = $info_ok && $t_ok;
+
+        $t_ok = all { scalar @$_ > 0} @header;
+        $self->_push_error([
+          0,
+          "no empty headers are allowed for multiplexed data",
+          0,
+          0]) unless $t_ok;
+        $info_ok = $info_ok && $t_ok;
+      }
+      $self->is_multiplexed($info_ok);
+      return $info_ok;
+    }
+  }
+
+  # ok, if not multiplexed
+  return 1;
+}
 
-    $self->_push_error([
-      $self->_csv->error_input,
-      $self->_csv->error_diag,
-      0,
-    ]) unless $header;
+sub _check_header {
+  my ($self, %params) = @_;
+  my $header;
+
+  $header = $self->header;
+  if (!$header) {
+    my $n_header = ($self->is_multiplexed)? scalar @{ $self->profile } : 1;
+    foreach my $p_num (0..$n_header - 1) {
+      my $h = $self->_csv->getline($self->_io);
+
+      $self->_push_error([
+        $self->_csv->error_input,
+        $self->_csv->error_diag,
+        0,
+      ]) unless $h;
+
+      if ($self->is_multiplexed) {
+        push @{ $header }, $h;
+      } else {
+        $header = $h;
+      }
+    }
   }
 
   # Special case: utf8 BOM.
   # certain software (namely MS Office and notepad.exe insist on prefixing
   # data with a discouraged but valid byte order mark
   # if not removed, the first header field will not be recognized
-  if ($header && $header->[0] && $self->encoding =~ /utf-?8/i) {
-    $header->[0] =~ s/^\x{FEFF}//;
+  if ($header) {
+    my $h = ($self->is_multiplexed)? $header->[0] : $header;
+
+    if ($h && $h->[0] && $self->encoding =~ /utf-?8/i) {
+      $h->[0] =~ s/^\x{FEFF}//;
+    }
   }
 
-  return unless $header;
+  # check, if all header fields are parsed well
+  if ($self->is_multiplexed) {
+    return unless $header && all { $_ } @$header;
+  } else {
+    return unless $header;
+  }
 
   # Special case: human stupidity
   # people insist that case sensitivity doesn't exist and try to enter all
@@ -130,12 +196,19 @@ sub _check_header {
   # mopst likely meant that field, so rewrite the header
   if ($self->case_insensitive_header) {
     die 'case_insensitive_header is only possible with profile' unless $self->profile;
-    my @names = (
-      keys %{ $self->profile || {} },
-    );
-    for my $name (@names) {
-      for my $i (0..$#$header) {
-        $header->[$i] = $name if lc $header->[$i] eq lc $name;
+    if ($header) {
+      my $h_aref = ($self->is_multiplexed)? $header : [ $header ];
+      my $p_num  = 0;
+      foreach my $h (@{ $h_aref }) {
+        my @names = (
+          keys %{ $self->profile->[$p_num]->{profile} || {} },
+        );
+        for my $name (@names) {
+          for my $i (0..$#$h) {
+            $h->[$i] = $name if lc $h->[$i] eq lc $name;
+          }
+        }
+        $p_num++;
       }
     }
   }
@@ -147,13 +220,12 @@ sub _parse_data {
   my ($self, %params) = @_;
   my (@data, @errors);
 
-  $self->_csv->column_names(@{ $self->header });
-
   while (1) {
     my $row = $self->_csv->getline($self->_io);
     if ($row) {
+      my $header = $self->_header_by_row($row);
       my %hr;
-      @hr{@{ $self->header }} = @$row;
+      @hr{@{ $header }} = @$row;
       push @data, \%hr;
     } else {
       last if $self->_csv->eof;
@@ -180,6 +252,21 @@ sub _parse_data {
   return ! @errors;
 }
 
+sub _header_by_row {
+  my ($self, $row) = @_;
+
+  # initialize lookup hash if not already done
+  if ($self->is_multiplexed && ! defined $self->_row_header ) {
+    $self->_row_header({ pairwise { no warnings 'once'; $a->{row_ident} => $b } @{ $self->profile }, @{ $self->header } });
+  }
+
+  if ($self->is_multiplexed) {
+    return $self->_row_header->{$row->[0]}
+  } else {
+    return $self->header;
+  }
+}
+
 sub _encode_layer {
   ':encoding(' . $_[0]->encoding . ')';
 }
@@ -188,13 +275,11 @@ sub _make_objects {
   my ($self, %params) = @_;
   my @objs;
 
-  eval "require " . $self->class;
   local $::myconfig{numberformat} = $self->numberformat if $self->numberformat;
   local $::myconfig{dateformat}   = $self->dateformat   if $self->dateformat;
 
   for my $line (@{ $self->_data }) {
-    my $tmp_obj = $self->class->new;
-    $self->dispatcher->dispatch($tmp_obj, $line);
+    my $tmp_obj = $self->dispatcher->dispatch($line);
     push @objs, $tmp_obj;
   }
 
@@ -247,9 +332,9 @@ SL::Helper::Csv - take care of csv file uploads
     sep_char    => ',',     # default ';'
     quote_char  => '\'',    # default '"'
     escape_char => '"',     # default '"'
-    header      => [qw(id text sellprice word)], # see later
-    profile     => { sellprice => 'sellprice_as_number' },
-    class       => 'SL::DB::CsvLine',   # if present, map lines to this
+    header      => [ qw(id text sellprice word) ], # see later
+    profile     => [ { profile => { sellprice => 'sellprice_as_number'},
+                       class   => 'SL::DB::Part' } ],
   );
 
   my $status  = $csv->parse;
@@ -290,6 +375,13 @@ unique, other data needs to be connected to existing data sets. This will not
 happen here. You will receive a plain mapping of the data into the class tree,
 nothing more.
 
+=item Multiplex data
+
+This module can handle multiplexed data of different class types. In that case
+multiple profiles with classes and row identifiers must be given. Multiple
+headers may also be given or read from csv data. Data must contain the row
+identifier in the first column and it's field name must be 'datatype'.
+
 =back
 
 =head1 METHODS
@@ -343,16 +435,38 @@ guessing. Know what your data is. Defaults to utf-8.
 
 Same as in L<Text::CSV>
 
-=item C<header> \@FIELDS
+=item C<header> \@HEADERS
+
+If given, it contains an ARRAY of the header fields for not multiplexed data.
+Or an ARRAYREF for each different class type for multiplexed data. These
+ARRAYREFS are the header fields which are an array of columns. In this case
+the first lines are not used as a header. Empty header fields will be ignored
+in objects.
 
-Can be an array of columns, in this case the first line is not used as a
-header. Empty header fields will be ignored in objects.
+If not given, headers are taken from the first n lines of data, where n is the
+number of different class types.
 
-=item C<profile> \%ACCESSORS
+In case of multiplexed data the first column must be named 'datatype'. This
+name must be given in the header.
 
-May be used to map header fields to custom accessors. Example:
+Examples:
 
-  { listprice => listprice_as_number }
+  classic data of one type:
+  [ 'name', 'street', 'zipcode', 'city' ]
+
+  multiplexed data with two different types
+  [ [ 'datatype', 'ordernumber', 'customer', 'transdate' ],
+    [ 'datatype', 'partnumber', 'qty', 'sellprice' ] ]
+
+=item C<profile> [{profile => \%ACCESSORS, class => class, row_ident => ri},]
+
+This is an ARRAYREF to HASHREFs which may contain the keys C<profile>, C<class>
+and C<row_ident>.
+
+The C<profile> is a HASHREF which may be used to map header fields to custom
+accessors. Example:
+
+  [ {profile => { listprice => listprice_as_number }} ]
 
 In this case C<listprice_as_number> will be used to read in values from the
 C<listprice> column.
@@ -360,7 +474,7 @@ C<listprice> column.
 In case of a One-To-One relationsship these can also be set over
 relationsships by sparating the steps with a dot (C<.>). This will work:
 
-  { customer => 'customer.name' }
+  [ {profile => { customer => 'customer.name' }} ]
 
 And will result in something like this:
 
@@ -379,11 +493,25 @@ If the path in a profile entry is empty, the field will be subjected to
 C<strict_profile> and C<case_insensitive_header> checking, will be parsed into
 C<get_data>, but will not be attempted to be dispatched into objects.
 
-=item C<class>
-
-If present, the line will be handed to the new sub of this class,
+If C<class> is present, the line will be handed to the new sub of this class,
 and the return value used instead of the line itself.
 
+C<row_ident> is a string to recognize the right profile and class for each data
+line in multiplexed data. It must match the value in the column 'dataype' for
+each class.
+
+In case of multiplexed data, C<class> and C<row_ident> must be given.
+Example:
+  [ {
+      class     => 'SL::DB::Order',
+      row_ident => 'O'
+    },
+    {
+      class     => 'SL::DB::OrderItem',
+      row_ident => 'I',
+      profile   => {sellprice => sellprice_as_number}
+    } ]
+
 =item C<ignore_unknown_columns>
 
 If set, the import will ignore unkown header columns. Useful for lazy imports,
@@ -442,18 +570,20 @@ Encoding errors are not dealt with properly.
 Dispatch to child objects, like this:
 
  $csv = SL::Helper::Csv->new(
-   file  => ...
-   class => SL::DB::Part,
-   profile => [
-     makemodel => {
-       make_1  => make,
-       model_1 => model,
-     },
-     makemodel => {
-       make_2  => make,
-       model_2 => model,
-     },
-   ]
+   file    => ...
+   profile => [ {
+     profile => [
+       makemodel => {
+         make_1  => make,
+         model_1 => model,
+       },
+       makemodel => {
+         make_2  => make,
+         model_2 => model,
+       },
+     ],
+     class   => SL::DB::Part,
+   } ]
  );
 
 =head1 AUTHOR
index add444b9d751d670410cacf302979f5177acf89d..3a725b5a0fc3d056793d57aec5c028061d07b3c8 100644 (file)
@@ -5,8 +5,9 @@ use strict;
 use Data::Dumper;
 use Carp;
 use Scalar::Util qw(weaken);
+use List::MoreUtils qw(all pairwise);
 use Rose::Object::MakeMethods::Generic scalar => [ qw(
-  _specs _errors
+  _specs _row_class _row_spec _errors
 ) ];
 
 use SL::Helper::Csv::Error;
@@ -22,13 +23,53 @@ sub new {
 }
 
 sub dispatch {
-  my ($self, $obj, $line) = @_;
+  my ($self, $line) = @_;
 
-  for my $spec (@{ $self->_specs }) {
+  my $class = $self->_class_by_line($line);
+  croak 'no class given' unless $class;
+
+  eval "require " . $class;
+  my $obj = $class->new;
+
+  my $specs = $self->_specs_by_line($line);
+  for my $spec (@{ $specs }) {
     $self->apply($obj, $spec, $line->{$spec->{key}});
   }
+
+  return $obj;
+}
+
+sub _class_by_line {
+  my ($self, $line) = @_;
+
+  # initialize lookup hash if not already done
+  if ($self->_csv->is_multiplexed && ! defined $self->_row_class ) {
+    $self->_row_class({ map { $_->{row_ident} => $_->{class} } @{ $self->_csv->profile } });
+  }
+
+  if ($self->_csv->is_multiplexed) {
+    return $self->_row_class->{$line->{datatype}};
+  } else {
+    return $self->_csv->profile->[0]->{class};
+  }
 }
 
+sub _specs_by_line {
+  my ($self, $line) = @_;
+
+  # initialize lookup hash if not already done
+  if ($self->_csv->is_multiplexed && ! defined $self->_row_spec ) {
+    $self->_row_spec({ pairwise { no warnings 'once'; $a->{row_ident} => $b } @{ $self->_csv->profile }, @{ $self->_specs } });
+  }
+
+  if ($self->_csv->is_multiplexed) {
+    return $self->_row_spec->{$line->{datatype}};
+  } else {
+    return $self->_specs->[0];
+  }
+}
+
+
 sub apply {
   my ($self, $obj, $spec, $value) = @_;
   return unless $value;
@@ -65,41 +106,62 @@ sub is_known {
 sub parse_profile {
   my ($self, %params) = @_;
 
-  my $header  = $self->_csv->header;
-  my $profile = $self->_csv->profile;
+  my @specs;
+
+  my $csv_profile = $self->_csv->profile;
+  my $h_aref = ($self->_csv->is_multiplexed)? $self->_csv->header : [ $self->_csv->header ];
+  my $i = 0;
+  foreach my $header (@{ $h_aref }) {
+    my $spec = $self->_parse_profile(profile => $csv_profile->[$i]->{profile},
+                                     class   => $csv_profile->[$i]->{class},
+                                     header  => $header);
+    push @specs, $spec;
+    $i++;
+  }
+
+  $self->_specs(\@specs);
+
+  $self->_csv->_push_error($self->errors);
+
+  return ! $self->errors;
+}
+
+sub _parse_profile {
+  my ($self, %params) = @_;
+
+  my $profile = $params{profile};
+  my $class   = $params{class};
+  my $header  = $params{header};
+
   my @specs;
 
   for my $col (@$header) {
     next unless $col;
     if ($self->_csv->strict_profile) {
       if (exists $profile->{$col}) {
-        push @specs, $self->make_spec($col, $profile->{$col});
+        push @specs, $self->make_spec($col, $profile->{$col}, $class);
       } else {
         $self->unknown_column($col, undef);
       }
     } else {
       if (exists $profile->{$col}) {
-        push @specs, $self->make_spec($col, $profile->{$col});
+        push @specs, $self->make_spec($col, $profile->{$col}, $class);
       } else {
-        push @specs, $self->make_spec($col, $col);
+        push @specs, $self->make_spec($col, $col, $class);
       }
     }
   }
 
-  $self->_specs(\@specs);
-  $self->_csv->_push_error($self->errors);
-  return ! $self->errors;
+  return \@specs;
 }
 
 sub make_spec {
-  my ($self, $col, $path) = @_;
+  my ($self, $col, $path, $cur_class) = @_;
 
   my $spec = { key => $col, steps => [] };
 
   return unless $path;
 
-  my $cur_class = $self->_csv->class;
-
   return unless $cur_class;
 
   for my $step_index ( split /\.(?!\d)/, $path ) {
index 36f105ebdc7a1cff99d1336cf3f2a6b6eba5c1aa..84ce10a17cad00641faf270583387f9670b1f15d 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -686,7 +686,7 @@ sub post_invoice {
                 netamount    = ?, paid        = ?, duedate       = ?,
                 invoice      = ?, taxzone_id  = ?, notes         = ?, taxincluded = ?,
                 intnotes     = ?, storno_id   = ?, storno        = ?,
-                cp_id        = ?, employee_id = ?, department_id = ?,
+                cp_id        = ?, employee_id = ?, department_id = ?, delivery_term_id = ?,
                 globalproject_id = ?, direct_debit = ?
               WHERE id = ?|;
   @values = (
@@ -695,7 +695,7 @@ sub post_invoice {
                 $netamount,                  $form->{paid},      conv_date($form->{duedate}),
             '1',                             $taxzone_id,                  $form->{notes},          $form->{taxincluded} ? 't' : 'f',
                 $form->{intnotes},           conv_i($form->{storno_id}),     $form->{storno}      ? 't' : 'f',
-         conv_i($form->{cp_id}),      conv_i($form->{employee_id}), conv_i($form->{department_id}),
+         conv_i($form->{cp_id}),      conv_i($form->{employee_id}), conv_i($form->{department_id}), conv_i($form->{delivery_term_id}),
          conv_i($form->{globalproject_id}),
                 $form->{direct_debit} ? 't' : 'f',
          conv_i($form->{id})
@@ -941,7 +941,8 @@ sub retrieve_invoice {
   $query = qq|SELECT cp_id, invnumber, transdate AS invdate, duedate,
                 orddate, quodate, globalproject_id,
                 ordnumber, quonumber, paid, taxincluded, notes, taxzone_id, storno, gldate,
-                intnotes, (SELECT cu.name FROM currencies cu WHERE cu.id=ap.currency_id) AS currency, direct_debit
+                intnotes, (SELECT cu.name FROM currencies cu WHERE cu.id=ap.currency_id) AS currency, direct_debit,
+                delivery_term_id
               FROM ap
               WHERE id = ?|;
   $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
@@ -1087,7 +1088,7 @@ sub get_vendor {
     qq|SELECT
          v.id AS vendor_id, v.name AS vendor, v.discount as vendor_discount,
          v.creditlimit, v.terms, v.notes AS intnotes,
-         v.email, v.cc, v.bcc, v.language_id, v.payment_id,
+         v.email, v.cc, v.bcc, v.language_id, v.payment_id, v.delivery_term_id,
          v.street, v.zipcode, v.city, v.country, v.taxzone_id, cu.name AS curr, v.direct_debit,
          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
          b.description AS business
index cfaf730e8bc523e33efaea4365d3cc7c18969a10..23b6f80f74d881d30b935ea78b4f010bcaaa0633 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -429,6 +429,9 @@ sub invoice_details {
 
   $form->set_payment_options($myconfig, $form->{invdate});
 
+  $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+  $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
   $form->{username} = $myconfig->{name};
 
   $main::lxdebug->leave_sub();
@@ -1104,7 +1107,8 @@ sub post_invoice {
                 cp_id       = ?, marge_total   = ?, marge_percent = ?,
                 globalproject_id               = ?, delivery_customer_id             = ?,
                 transaction_description        = ?, delivery_vendor_id               = ?,
-                donumber    = ?, invnumber_for_credit_note = ?,        direct_debit  = ?
+                donumber    = ?, invnumber_for_credit_note = ?,        direct_debit  = ?,
+                delivery_term_id = ?
               WHERE id = ?|;
   @values = (          $form->{"invnumber"},           $form->{"ordnumber"},             $form->{"quonumber"},          $form->{"cusordnumber"},
              conv_date($form->{"invdate"}),  conv_date($form->{"orddate"}),    conv_date($form->{"quodate"}),    conv_i($form->{"customer_id"}),
@@ -1118,6 +1122,7 @@ sub post_invoice {
                 conv_i($form->{"globalproject_id"}),                              conv_i($form->{"delivery_customer_id"}),
                        $form->{transaction_description},                          conv_i($form->{"delivery_vendor_id"}),
                        $form->{"donumber"}, $form->{"invnumber_for_credit_note"},        $form->{direct_debit} ? 't' : 'f',
+                conv_i($form->{delivery_term_id}),
                 conv_i($form->{"id"}));
   do_query($form, $dbh, $query, @values);
 
@@ -1593,7 +1598,7 @@ sub retrieve_invoice {
            a.employee_id, a.salesman_id, a.payment_id,
            a.language_id, a.delivery_customer_id, a.delivery_vendor_id, a.type,
            a.transaction_description, a.donumber, a.invnumber_for_credit_note,
-           a.marge_total, a.marge_percent, a.direct_debit,
+           a.marge_total, a.marge_percent, a.direct_debit, a.delivery_term_id,
            e.name AS employee
          FROM ar a
          LEFT JOIN employee e ON (e.id = a.employee_id)
@@ -1763,7 +1768,7 @@ sub get_customer {
   $query =
     qq|SELECT
          c.id AS customer_id, c.name AS customer, c.discount as customer_discount, c.creditlimit, c.terms,
-         c.email, c.cc, c.bcc, c.language_id, c.payment_id,
+         c.email, c.cc, c.bcc, c.language_id, c.payment_id, c.delivery_term_id,
          c.street, c.zipcode, c.city, c.country,
          c.notes AS intnotes, c.klass as customer_klass, c.taxzone_id, c.salesman_id, cu.name AS curr,
          c.taxincluded_checked, c.direct_debit,
index 12cbb8aa126d1aa36636415e24727363a04d2ce1..0e5c05b467843c884db8dc20882795107e2f1f1f 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -88,7 +88,8 @@ sub transactions {
     qq|  ex.$rate AS exchangerate, | .
     qq|  pr.projectnumber AS globalprojectnumber, | .
     qq|  e.name AS employee, s.name AS salesman, | .
-    qq|  ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id  | .
+    qq|  ct.${vc}number AS vcnumber, ct.country, ct.ustid, ct.business_id,  | .
+    qq|  tz.description AS taxzone | .
     $periodic_invoices_columns .
     qq|FROM oe o | .
     qq|JOIN $vc ct ON (o.${vc}_id = ct.id) | .
@@ -97,6 +98,7 @@ sub transactions {
     qq|LEFT JOIN exchangerate ex ON (ex.currency_id = o.currency_id | .
     qq|  AND ex.transdate = o.transdate) | .
     qq|LEFT JOIN project pr ON (o.globalproject_id = pr.id) | .
+    qq|LEFT JOIN tax_zones tz ON (o.taxzone_id = tz.id) | .
     qq|$periodic_invoices_joins | .
     qq|WHERE (o.quotation = ?) |;
   push(@values, $quotation);
@@ -192,6 +194,16 @@ SQL
     push(@values, conv_date($form->{reqdateto}));
   }
 
+  if ($form->{shippingpoint}) {
+    $query .= qq| AND o.shippingpoint ILIKE ?|;
+    push(@values, '%' . $form->{shippingpoint} . '%');
+  }
+
+  if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
+    $query .= qq| AND tz.id = ?|;
+    push(@values, $form->{taxzone_id});
+  }
+
   if ($form->{transaction_description}) {
     $query .= qq| AND o.transaction_description ILIKE ?|;
     push(@values, '%' . $form->{transaction_description} . '%');
@@ -214,7 +226,9 @@ SQL
     "employee"                => "e.name",
     "salesman"                => "s.name",
     "shipvia"                 => "o.shipvia",
-    "transaction_description" => "o.transaction_description"
+    "transaction_description" => "o.transaction_description",
+    "shippingpoint"           => "o.shippingpoint",
+    "taxzone"                 => "tz.description",
   );
   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
     $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
@@ -504,7 +518,7 @@ sub save {
          customer_id = ?, amount = ?, netamount = ?, reqdate = ?, taxincluded = ?,
          shippingpoint = ?, shipvia = ?, notes = ?, intnotes = ?, currency_id = (SELECT id FROM currencies WHERE name=?), closed = ?,
          delivered = ?, proforma = ?, quotation = ?, department_id = ?, language_id = ?,
-         taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,
+         taxzone_id = ?, shipto_id = ?, payment_id = ?, delivery_vendor_id = ?, delivery_customer_id = ?,delivery_term_id = ?,
          globalproject_id = ?, employee_id = ?, salesman_id = ?, cp_id = ?, transaction_description = ?, marge_total = ?, marge_percent = ?
        WHERE id = ?|;
 
@@ -521,6 +535,7 @@ sub save {
              conv_i($form->{shipto_id}), conv_i($form->{payment_id}),
              conv_i($form->{delivery_vendor_id}),
              conv_i($form->{delivery_customer_id}),
+             conv_i($form->{delivery_term_id}),
              conv_i($form->{globalproject_id}), conv_i($form->{employee_id}),
              conv_i($form->{salesman_id}), conv_i($form->{cp_id}),
              $form->{transaction_description},
@@ -754,7 +769,7 @@ sub retrieve {
            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
-           o.globalproject_id, o.delivered, o.transaction_description
+           o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id
          FROM oe o
          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
          LEFT JOIN employee e ON (o.employee_id = e.id)
@@ -1320,6 +1335,9 @@ sub order_details {
 
   $dbh->disconnect;
 
+  $form->{delivery_term} = SL::DB::Manager::DeliveryTerm->find_by(id => $form->{delivery_term_id} || undef);
+  $form->{delivery_term}->description_long($form->{delivery_term}->translated_attribute('description_long', $form->{language_id})) if $form->{delivery_term} && $form->{language_id};
+
   $main::lxdebug->leave_sub();
 }
 
index 4b7b15d3d1c244ac6dcf616de550e2848901de7b..daa7d73104380daa43ed46052c70059400d114ec 100644 (file)
@@ -5,7 +5,24 @@ use strict;
 use SL::DB::Part;
 
 use Exporter qw(import);
-our @EXPORT = qw(part_picker);
+our @EXPORT = qw(part_picker part);
+
+use Carp;
+
+sub part {
+  my ($self, $part, %params) = @_;
+
+  $params{display} ||= 'inline';
+
+  croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
+
+  my $text = join '', (
+    $params{no_link} ? '' : '<a href="ic.pl?action=edit&id=' . $self->escape($part->id) . '">',
+    $self->escape($part->partnumber),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
 
 sub part_picker {
   my ($self, $name, $value, %params) = @_;
@@ -31,11 +48,15 @@ __END__
 
 =head1 NAME
 
-SL::Presenter::Part - Part lelated presenter stuff
+SL::Presenter::Part - Part related presenter stuff
 
 =head1 SYNOPSIS
 
-see L<SL::Presenter>
+  # Create an html link for editing/opening a part/service/assembly
+  my $object = my $object = SL::DB::Manager::Part->get_first;
+  my $html   = SL::Presenter->get->part($object, display => 'inline');
+
+see also L<SL::Presenter>
 
 =head1 DESCRIPTION
 
@@ -43,8 +64,29 @@ see L<SL::Presenter>
 
 =head1 FUNCTIONS
 
+=over 2
+
+=item C<part, $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the part object C<$object>
+
+C<%params> can include:
+
 =over 4
 
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the part's name linked
+to the corresponding 'edit' action.
+
+=back
+
+=back 
+
+=over 2
+
 =item C<part_picker $name, $value, %params>
 
 All-in-one picker widget for parts. The name will be both id and name
index 59c2f9481671e2090b4b05617a31b2a9f109eb21..8e374ec51b4b9300cbb3fbe2528e3b57f28aeb07 100644 (file)
@@ -455,6 +455,7 @@ sub show_vc_details {
                  $locale->text("No vendor has been selected yet."));
 
   Common->get_vc_details(\%myconfig, $form, $form->{vc}, $form->{vc_id});
+  $form->{discount_as_percent} = $form->format_amount(\%::myconfig, $form->parse_amount(\%::myconfig, $form->{discount}) * 100, 2);
 
   $form->{title} = $form->{vc} eq "customer" ?
     $locale->text("Customer details") : $locale->text("Vendor details");
index 513a7ff219663c250af6db84cfc897539a5776f5..845fc3ab4a0971e3a8683c27aaebeb9a90385671 100644 (file)
@@ -52,8 +52,10 @@ use SL::CVar;
 use SL::Request qw(flatten);
 use SL::DB::Business;
 use SL::DB::Default;
+use SL::DB::DeliveryTerm;
 use SL::Helper::Flash;
 use SL::ReportGenerator;
+use SL::MoreCommon qw(uri_encode);
 
 require "bin/mozilla/common.pl";
 require "bin/mozilla/reportgenerator.pl";
@@ -63,25 +65,6 @@ use strict;
 
 # end of main
 
-sub add {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{title}    = "Add";
-  $form->{callback} = "$form->{script}?action=add&db=$form->{db}" unless $form->{callback};
-
-  CT->populate_drop_down_boxes(\%myconfig, \%$form);
-
-  &form_header;
-  &form_footer;
-
-  $main::lxdebug->leave_sub();
-}
-
 sub search {
   $main::lxdebug->enter_sub();
 
@@ -415,530 +398,4 @@ sub list_contacts {
   $::lxdebug->leave_sub;
 }
 
-sub edit {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  # show history button
-  $form->{javascript} = qq|<script type=text/javascript src=js/show_history.js></script>|;
-  #/show hhistory button
-
-  CT->get_tuple(\%myconfig, \%$form);
-  CT->populate_drop_down_boxes(\%myconfig, \%$form);
-
-  $form->{title} = "Edit";
-
-  # format discount
-  $form->{discount} *= 100;
-  # format uri
-  $form->{homepage} = 'http://' . $form->{homepage} unless ((!$form->{homepage}) || $form->{homepage} =~ m|^https?://|);
-
-  &form_header;
-  &form_footer;
-
-  $main::lxdebug->leave_sub();
-}
-
-sub _shipto_label {
-  my $s = shift(@_);
-  join('; ', grep { $_ } map { $s->{"shipto$_"} } qw(name department_1 street city)) || ' '
-}
-
-sub _contacts_label {
-  join ", ", grep { $_ } $_[0]->{cp_name}, $_[0]->{cp_givenname};
-}
-
-sub form_header {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  $form->get_lists(taxzones   => "ALL_TAXZONES",
-                   currencies => "ALL_CURRENCIES");
-  $form->get_pricegroup(\%myconfig, { all => 1 });
-
-  $form->get_lists(customers => { key => "ALL_SALESMAN_CUSTOMERS", business_is_salesman => 1 }) if $::instance_conf->get_vertreter;
-  $form->{ALL_EMPLOYEES}          = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{FU_created_for_user},  deleted => 0 ] ]);
-  $form->{ALL_SALESMEN}           = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
-  $form->{USER}                   = SL::DB::Manager::Employee->current;
-
-  $form->{taxincluded}    = ($form->{taxincluded}) ? "checked" : "";
-  $form->{is_customer}    = $form->{db}     eq 'customer';
-  $form->{shipto_label}   = \&_shipto_label;
-  $form->{contacts_label} = \&_contacts_label;
-  $form->{taxzone_id}     = 0                                                               if !$form->{id};
-  $form->{SHIPTO_ALL}     = [ +{ shipto_id => '0', shiptoname => $::locale->text('All') }, @{ $form->{SHIPTO} } ];
-
-  $form->{title} = $form->{title_save}
-                || $locale->text("$form->{title} " . ucfirst $form->{db}) . ($form->{title} eq "Edit" ? " $form->{name}" : '');
-
-  CT->query_titles_and_greetings(\%myconfig, \%$form);
-  map { $form->{"MB_$_"} = [ map +{ id => $_, description => $_ }, @{ $form->{$_} } ] } qw(COMPANY_GREETINGS);
-
-  $form->{NOTES} ||= [ ];
-
-  if (!$form->{'language_id'}) {
-    my $l_id = SL::DB::Default->get->{'language_id'};
-    if ($l_id) {
-      $form->{'default_language_id'} = $l_id;
-    }
-  }
-
-  if (!$form->{'id'}) {
-    $form->{'currency'} = $form->get_default_currency(\%myconfig);
-  } else {
-    $form->{currency} = $form->{curr};
-  }
-
-  $::form->{CUSTOM_VARIABLES} = { };
-  my %specs = ( CT       => { field => 'id',    name_prefix => '',   },
-                Contacts => { field => 'cp_id', name_prefix => 'cp', },
-              );
-
-  for my $module (keys %specs) {
-    my $spec = $specs{$module};
-
-    $::form->{CUSTOM_VARIABLES}->{$module} = CVar->get_custom_variables(module => $module, trans_id => $::form->{ $spec->{field} });
-    CVar->render_inputs(variables => $::form->{CUSTOM_VARIABLES}->{$module}, name_prefix => $spec->{name_prefix})
-      if scalar @{ $::form->{CUSTOM_VARIABLES}->{$module} };
-  }
-
-  $form->header;
-  print $form->parse_html_template('ct/form_header');
-
-  $main::lxdebug->leave_sub();
-}
-
-sub form_footer {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-
-  print $form->parse_html_template('ct/form_footer', { is_orphaned => $form->{status} eq 'orphaned',
-                                                       is_customer => $form->{db}     eq 'customer' });
-  $main::lxdebug->leave_sub();
-}
-
-sub _do_save {
-  $main::auth->assert('customer_vendor_edit');
-
-  $::form->isblank("name", $::locale->text("Name missing!"));
-
-  if ($::form->{new_salesman_id} && $::instance_conf->get_vertreter) {
-    $::form->{salesman_id} = $::form->{new_salesman_id};
-    delete $::form->{new_salesman_id};
-  }
-
-  my $res = $::form->{db} eq 'customer' ? CT->save_customer(\%::myconfig, $::form) : CT->save_vendor(\%::myconfig, $::form);
-
-  if (3 == $res) {
-    if ($::form->{"db"} eq "customer") {
-      $::form->error($::locale->text('This customer number is already in use.'));
-    } else {
-      $::form->error($::locale->text('This vendor number is already in use.'));
-    }
-  }
-}
-
-sub add_transaction {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit & ' .
-                '(general_ledger         | invoice_edit         | vendor_invoice_edit | ' .
-                ' request_quotation_edit | sales_quotation_edit | sales_order_edit    | purchase_order_edit)');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-#  # saving the history
-#  if(!exists $form->{addition}) {
-#    $form->{addition} = "ADD TRANSACTION";
-#    $form->save_history;
-#  }
-#  # /saving the history
-
-  _do_save();
-
-  $form->{callback} = $form->escape($form->{callback}, 1);
-  my $name = $form->escape("$form->{name}", 1);
-
-  $form->{callback} =
-    "$form->{script}?action=add&vc=$form->{db}&$form->{db}_id=$form->{id}&$form->{db}=$name&type=$form->{type}&callback=$form->{callback}";
-  $form->redirect;
-
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_ap_transaction {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit & general_ledger');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{script} = "ap.pl";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_ar_transaction {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit & general_ledger');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{script} = "ar.pl";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_invoice {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  if ($form->{db} eq 'customer') {
-    $main::auth->assert('customer_vendor_edit & invoice_edit');
-  } else {
-    $main::auth->assert('customer_vendor_edit & vendor_invoice_edit');
-  }
-
-  $form->{script} = ($form->{db} eq 'customer') ? "is.pl" : "ir.pl";
-  $form->{type} = "invoice";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|invnumber_| . $form->{invnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_rfq {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit & request_quotation_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{script} = "oe.pl";
-  $form->{type}   = "request_quotation";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_quotation {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit & sales_quotation_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{script} = "oe.pl";
-  $form->{type}   = "sales_quotation";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_order {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  if ($form->{db} eq 'customer') {
-    $main::auth->assert('customer_vendor_edit & sales_order_edit');
-  } else {
-    $main::auth->assert('customer_vendor_edit & purchase_order_edit');
-  }
-
-  $form->{script} = "oe.pl";
-  $form->{type}   =
-    ($form->{db} eq 'customer') ? "sales_order" : "purchase_order";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &add_transaction;
-  $main::lxdebug->leave_sub();
-}
-
-sub save_and_close {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  my $msg = ucfirst $form->{db};
-  $msg .= " saved!";
-
-  _do_save();
-
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  $form->redirect($locale->text($msg));
-
-  $main::lxdebug->leave_sub();
-}
-
-sub save {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  my $msg = ucfirst $form->{db};
-  $msg .= " saved!";
-
-  _do_save();
-
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
-    $form->{addition} = "SAVED";
-    $form->save_history;
-  }
-  # /saving the history
-  &edit;
-
-  $main::lxdebug->leave_sub();
-  ::end_of_request();
-}
-
-sub delete {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-  my $locale   = $main::locale;
-
-  CT->delete(\%myconfig, \%$form);
-
-  my $msg = ucfirst $form->{db};
-  $msg .= " deleted!";
-  # saving the history
-  if(!exists $form->{addition}) {
-    $form->{snumbers} = ($form->{"db"} eq "customer" ? qq|customernumber_| . $form->{customernumber} : qq|vendornumber_| . $form->{vendornumber});
-    $form->{addition} = "DELETED";
-    $form->save_history;
-  }
-  # /saving the history
-  $form->redirect($locale->text($msg));
-
-  $main::lxdebug->leave_sub();
-}
-
-sub display {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-
-  &form_header();
-  &form_footer();
-
-  $main::lxdebug->leave_sub();
-}
-
-sub update {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  my $form     = $main::form;
-
-  &display();
-  $main::lxdebug->leave_sub();
-}
-
-sub get_contact {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  CT->populate_drop_down_boxes(\%::myconfig, $::form);
-  CT->query_titles_and_greetings(\%::myconfig, $::form);
-  CT->get_contact(\%::myconfig, $::form) if $::form->{cp_id};
-
-  $::form->{CUSTOM_VARIABLES}{Contacts} = CVar->get_custom_variables(module => 'Contacts', trans_id => $::form->{cp_id});
-  CVar->render_inputs(variables => $::form->{CUSTOM_VARIABLES}{Contacts}, name_prefix => 'cp')
-    if scalar @{ $::form->{CUSTOM_VARIABLES}->{Contacts} };
-
-  $::form->{contacts_label} = \&_contacts_label;
-
-  print $::form->ajax_response_header(), $::form->parse_html_template('ct/_contact');
-
-  $main::lxdebug->leave_sub();
-}
-
-sub get_shipto {
-  $main::lxdebug->enter_sub();
-
-  $main::auth->assert('customer_vendor_edit');
-
-  CT->populate_drop_down_boxes(\%::myconfig, $::form);
-  CT->get_shipto(\%::myconfig, $::form) if $::form->{shipto_id};
-
-  $::form->{shipto_label} = \&_shipto_label;
-
-  print $::form->ajax_response_header(), $::form->parse_html_template('ct/_shipto');
-
-  $main::lxdebug->leave_sub();
-}
-
-sub get_delivery {
-  $::lxdebug->enter_sub;
-
-  $::auth->assert('customer_vendor_edit');
-  $::auth->assert('sales_all_edit');
-
-  CT->get_delivery(\%::myconfig, $::form );
-
-  print $::form->ajax_response_header,
-        $::form->parse_html_template('ct/get_delivery', {
-          is_customer =>  $::form->{db} eq 'customer',
-        });
-
-  $::lxdebug->leave_sub;
-}
-
-sub delete_shipto {
-  $::lxdebug->enter_sub;
-  $::auth->assert('customer_vendor_edit');
-
-  if (!$::form->{shipto_id}) {
-    flash('error', $::locale->text('No shipto selected to delete'));
-  } else {
-
-    CT->get_shipto(\%::myconfig, $::form);
-
-    my $shipto = SL::DB::Manager::Shipto->find_by(shipto_id => $::form->{shipto_id});
-
-    if ($shipto->used) {
-      $shipto->detach->save;
-      flash('info', $::locale->text('Shipto is in use and was flagged invalid.'));
-    } else {
-      $shipto->delete;
-      flash('info', $::locale->text('Shipto deleted.'));
-    }
-    delete $::form->{$_} for grep /^shipto/, keys %$::form;
-  }
-
-  edit();
-
-  $::lxdebug->leave_sub;
-}
-
-sub delete_contact {
-  $::lxdebug->enter_sub;
-  $::auth->assert('customer_vendor_edit');
-
-  if (!$::form->{cp_id}) {
-    flash('error', $::locale->text('No contact selected to delete'));
-  } else {
-
-    CT->get_contact(\%::myconfig, $::form);
-
-    my $contact = SL::DB::Manager::Contact->find_by(cp_id => $::form->{cp_id});
-
-    if ($contact->used) {
-      $contact->detach->save;
-      flash('info', $::locale->text('Contact is in use and was flagged invalid.'));
-    } else {
-      $contact->delete;
-      flash('info', $::locale->text('Contact deleted.'));
-    }
-    delete $::form->{$_} for grep /^cp_/, keys %$::form;
-  }
-
-  edit();
-
-  $::lxdebug->leave_sub;
-}
-
-sub ajax_autocomplete {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{column}          = 'name'     unless $form->{column} =~ /^name$/;
-  $form->{vc}              = 'customer' unless $form->{vc} =~ /^customer|vendor$/;
-  $form->{db}              = $form->{vc}; # CT expects this
-  $form->{$form->{column}} = $form->{q}           || '';
-  $form->{limit}           = ($form->{limit} * 1) || 10;
-  $form->{searchitems}   ||= '';
-
-  CT->search(\%myconfig, $form);
-
-  print $form->ajax_response_header(),
-        $form->parse_html_template('ct/ajax_autocomplete');
-
-  $main::lxdebug->leave_sub();
-}
-
 sub continue { call_sub($main::form->{nextsub}); }
index 086c008e6b7021b26b0fca51762c00556e1b1b60..3e54c1ed8531caf3ea87bf23af4222d744638822 100644 (file)
@@ -172,7 +172,7 @@ sub order_links {
   DO->retrieve('vc'  => $form->{vc},
                'ids' => $form->{id});
 
-  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes currency));
+  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
   $form->{shipto} = 1 if $form->{id} || $form->{convert_from_oe_ids};
 
   # get customer / vendor
@@ -184,7 +184,7 @@ sub order_links {
     $form->{discount} = $form->{customer_discount};
   }
 
-  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id));
+  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
   $form->restore_vars(qw(taxincluded)) if $form->{id};
   $form->restore_vars(qw(salesman_id)) if $editing;
@@ -335,6 +335,7 @@ sub form_footer {
   my $form     = $main::form;
 
   $form->{PRINT_OPTIONS} = print_options('inline' => 1);
+  $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
   print $form->parse_html_template('do/form_footer',
     {transfer_default         => ($::instance_conf->get_transfer_default)});
@@ -508,13 +509,14 @@ sub orders {
 
   my @hidden_variables = map { "l_${_}" } @columns;
   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber
-                                          transaction_description transdatefrom transdateto type vc employee_id salesman_id project_id);
+                                          transaction_description transdatefrom transdateto reqdatefrom reqdateto
+                                          type vc employee_id salesman_id project_id);
 
   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
   my %column_defs = (
     'ids'                     => { 'text' => '', },
-    'transdate'               => { 'text' => $locale->text('Date'), },
+    'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
     'id'                      => { 'text' => $locale->text('ID'), },
     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
@@ -569,12 +571,16 @@ sub orders {
   if ($form->{transaction_description}) {
     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
   }
-  if ($form->{transdatefrom}) {
-    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1);
-  }
-  if ($form->{transdateto}) {
-    push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1);
-  }
+  if ( $form->{transdatefrom} or $form->{transdateto} ) {
+    push @options, $locale->text('Delivery Order Date');
+    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
+    push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
+  };
+  if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
+    push @options, $locale->text('Reqdate');
+    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
+    push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
+  };
   if ($form->{open}) {
     push @options, $locale->text('Open');
   }
index d2e0f9cc32c946e2760c416bd630591d03cc7e6e..fb0de34e94e94bc2a96e789b74129e53b69de362 100644 (file)
@@ -683,17 +683,6 @@ sub display_rows {
     $charts{$item->{accno}} = $item;
   }
 
-  my %taxchart_labels = ();
-  my @taxchart_values = ();
-  my %taxcharts = ();
-  foreach my $item (@{ $form->{TAX_ACCOUNTS} }) {
-    my $key = $item->{id} . "--" . $item->{rate};
-    $taxchart_init = $key if ($taxchart_init == $item->{id});
-    push(@taxchart_values, $key);
-    $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
-    $taxcharts{$item->{id}} = $item;
-  }
-
   my ($source, $memo, $source_hidden, $memo_hidden);
   for my $i (1 .. $form->{rowcount}) {
     if ($form->{show_details}) {
@@ -717,6 +706,20 @@ sub display_rows {
     my ($selected_accno, $selected_tax_id) = split(/--/, $selected_accno_full);
     my ($previous_accno, $previous_tax_id) = split(/--/, $form->{"previous_accno_$i"});
 
+    my %taxchart_labels = ();
+    my @taxchart_values = ();
+    my %taxcharts = ();
+    my $filter_accno;
+    $filter_accno = $::form->{ALL_CHARTS}[0]->{accno};
+    $filter_accno = $selected_accno if (!$init and $i < $form->{rowcount});
+    foreach my $item ( GL->get_tax_dropdown($filter_accno) ) {
+      my $key = $item->{id} . "--" . $item->{rate};
+      $taxchart_init = $key if ($taxchart_init == $item->{id});
+      push(@taxchart_values, $key);
+      $taxchart_labels{$key} = $item->{taxdescription} . " " . $item->{rate} * 100 . ' %';
+      $taxcharts{$item->{id}} = $item;
+    }
+
     if ($previous_accno &&
         ($previous_accno eq $selected_accno) &&
         ($previous_tax_id ne $selected_tax_id)) {
@@ -740,11 +743,11 @@ sub display_rows {
       . qq|</td>|;
     my $tax_ddbox = qq|<td>| .
       NTI($cgi->popup_menu('-name' => "taxchart_$i",
-                           '-id' => "taxchart_$i",
-                           '-style' => 'width:200px',
-                           '-values' => \@taxchart_values,
-                           '-labels' => \%taxchart_labels,
-                           '-default' => $selected_taxchart))
+            '-id' => "taxchart_$i",
+            '-style' => 'width:200px',
+            '-values' => \@taxchart_values,
+            '-labels' => \%taxchart_labels,
+            '-default' => $selected_taxchart))
       . qq|</td>|;
 
     my ($fx_transaction, $checked);
@@ -864,9 +867,6 @@ sub form_header {
                    "charts"    => { "key"       => "ALL_CHARTS",
                                     "transdate" => $::form->{transdate} });
 
-  $::form->{accno} = $::form->{ALL_CHARTS}[0]->{accno};
-  GL->get_tax_dropdown();
-
   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
 
   my $title      = $::form->{title};
@@ -1225,16 +1225,18 @@ sub continue {
 }
 
 sub get_tax_dropdown {
+  $main::lxdebug->enter_sub();
 
   my $form = $main::form;
-  $main::lxdebug->enter_sub();
-  GL->get_tax_dropdown();
+  my @tax_accounts = GL->get_tax_dropdown($form->{accno});
 
-  foreach my $item (@{ $form->{TAX_ACCOUNTS} }) {
+  foreach my $item (@tax_accounts) {
     $item->{taxdescription} = $::locale->{iconv_utf8}->convert($item->{taxdescription});
     $item->{taxdescription} .= ' ' . $form->round_amount($item->{rate} * 100);
   }
 
+  $form->{TAX_ACCOUNTS} = [ @tax_accounts ];
+
   print $form->ajax_response_header, $form->parse_html_template("gl/update_tax_accounts");
 
   $main::lxdebug->leave_sub();
index 0756448015938fe6addca9f62199c7d8730f605e..c1bc770326eda2b03e7ddc9802c8fdd1ffcd4c16 100644 (file)
@@ -1365,6 +1365,7 @@ sub print_form {
 
   my $language_saved = $form->{language_id};
   my $payment_id_saved = $form->{payment_id};
+  my $delivery_term_id_saved = $form->{delivery_term_id};
   my $salesman_id_saved = $form->{salesman_id};
   my $cp_id_saved = $form->{cp_id};
   my $taxzone_id_saved = $form->{taxzone_id};
@@ -1374,6 +1375,7 @@ sub print_form {
 
   $form->{language_id} = $language_saved;
   $form->{payment_id} = $payment_id_saved;
+  $form->{delivery_term_id} = $delivery_term_id_saved;
   $form->{taxzone_id} = $taxzone_id_saved;
   $form->{currency} = $currency_saved;
 
index 3653891f925c4f5b2fbc455cb010905e73d40be1..30124cf6459768bbccf2f2c86e1b5d11db213ecc 100644 (file)
@@ -116,7 +116,7 @@ sub invoice_links {
     }
   }
 
-  my ($payment_id, $language_id, $taxzone_id, $currency);
+  my ($payment_id, $language_id, $taxzone_id, $currency, $delivery_term_id);
   if ($form->{payment_id}) {
     $payment_id = $form->{payment_id};
   }
@@ -129,6 +129,9 @@ sub invoice_links {
   if ($form->{currency}) {
     $currency = $form->{currency};
   }
+  if ($form->{delivery_term_id}) {
+    $delivery_term_id = $form->{delivery_term_id};
+  }
 
   my $cp_id = $form->{cp_id};
   IR->get_vendor(\%myconfig, \%$form);
@@ -147,6 +150,9 @@ sub invoice_links {
   if ($currency) {
     $form->{currency} = $currency;
   }
+  if ($delivery_term_id) {
+    $form->{delivery_term_id} = $delivery_term_id;
+  }
 
   my @curr = $form->get_all_currencies();
   map { $form->{selectcurrency} .= "<option>$_\n" } @curr;
@@ -431,6 +437,8 @@ sub form_footer {
     $totalpaid += $form->{"paid_$i"};
   }
 
+  $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
   print $form->parse_html_template('ir/form_footer', {
     is_type_credit_note => ($form->{type} eq "credit_note"),
     totalpaid           => $totalpaid,
index ecc5f5577ca7836a8398c98c2b71c1421fc65f9d..b8851ff6a3ec2a63fd3c44e790e9404987f2c9eb 100644 (file)
@@ -149,7 +149,9 @@ sub invoice_links {
 
   my $editing = $form->{id};
 
-  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded currency cp_id intnotes id shipto_id));
+  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id
+                        taxincluded currency cp_id intnotes id shipto_id
+                        delivery_term_id));
 
   IS->get_customer(\%myconfig, \%$form);
 
@@ -161,7 +163,8 @@ sub invoice_links {
   $form->restore_vars(qw(id));
 
   IS->retrieve_invoice(\%myconfig, \%$form);
-  $form->restore_vars(qw(payment_id language_id taxzone_id currency intnotes cp_id shipto_id));
+  $form->restore_vars(qw(payment_id language_id taxzone_id currency intnotes
+                         cp_id shipto_id delivery_term_id));
   $form->restore_vars(qw(taxincluded)) if $form->{id};
   $form->restore_vars(qw(salesman_id)) if $editing;
 
@@ -460,6 +463,8 @@ sub form_footer {
 
   $form->{oldinvtotal} = $form->{invtotal};
 
+  $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
   print $form->parse_html_template('is/form_footer', {
     is_type_credit_note => ($form->{type} eq "credit_note"),
     totalpaid           => $totalpaid,
index 6b8410354f78e74b00e021e7006c6d3ebb434909..883507aa9c5a563a65e642954f4e4d804268b59c 100644 (file)
@@ -48,6 +48,7 @@ use List::Util qw(min max reduce sum);
 use Data::Dumper;
 
 use SL::DB::Customer;
+use SL::DB::TaxZone;
 
 require "bin/mozilla/io.pl";
 require "bin/mozilla/arap.pl";
@@ -244,14 +245,14 @@ sub order_links {
 
   $form->{"$form->{vc}_id"} ||= $form->{"all_$form->{vc}"}->[0]->{id} if $form->{"all_$form->{vc}"};
 
-  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes shipto_id currency));
+  $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes shipto_id delivery_term_id currency));
   $form->{shipto} = 1 if $form->{id} || $form->{convert_from_oe_ids};
 
   # get customer / vendor
   IR->get_vendor(\%myconfig, \%$form)   if $form->{type} =~ /(purchase_order|request_quotation)/;
   IS->get_customer(\%myconfig, \%$form) if $form->{type} =~ /sales_(order|quotation)/;
 
-  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id shipto_id));
+  $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id shipto_id delivery_term_id));
   $form->restore_vars(qw(currency))    if $form->{id};
   $form->restore_vars(qw(taxincluded)) if $form->{id};
   $form->restore_vars(qw(salesman_id)) if $editing;
@@ -522,6 +523,8 @@ sub form_footer {
 
   $form->{oldinvtotal} = $form->{invtotal};
 
+  $TMPL_VAR{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
+
   print $form->parse_html_template("oe/form_footer", {
      %TMPL_VAR,
      webdav          => $::instance_conf->get_webdav,
@@ -722,7 +725,8 @@ sub search {
   $form->get_lists("projects"     => { "key" => "ALL_PROJECTS", "all" => 1 },
                    "departments"  => "ALL_DEPARTMENTS",
                    "$form->{vc}s" => "ALL_VC",
-                   "business_types" => "ALL_BUSINESS_TYPES");
+                   "taxzones"     => "ALL_TAXZONES",
+                   "business_types" => "ALL_BUSINESS_TYPES",);
   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   # constants and subs for template
@@ -792,7 +796,8 @@ sub orders {
     "delivered",               "periodic_invoices",
     "marge_total",             "marge_percent",
     "vcnumber",                "ustid",
-    "country",
+    "country",                 "shippingpoint",
+    "taxzone",
   );
 
   # only show checkboxes if gotten here via sales_order form.
@@ -831,9 +836,12 @@ sub orders {
   push @hidden_variables, "l_subtotal", $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered ordnumber quonumber
                                                         transaction_description transdatefrom transdateto type vc employee_id salesman_id
                                                         reqdatefrom reqdateto projectnumber project_id periodic_invoices_active periodic_invoices_inactive
-                                                        business_id);
+                                                        business_id shippingpoint taxzone_id);
+
+  my   @keys_for_url = grep { $form->{$_} } @hidden_variables;
+  push @keys_for_url, 'taxzone_id' if $form->{taxzone_id} ne ''; # taxzone_id could be 0
 
-  my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
+  my $href = build_std_url('action=orders', @keys_for_url);
 
   my %column_defs = (
     'ids'                     => { 'text' => '', },
@@ -861,9 +869,11 @@ sub orders {
     'country'                 => { 'text' => $locale->text('Country'), },
     'ustid'                   => { 'text' => $locale->text('USt-IdNr.'), },
     'periodic_invoices'       => { 'text' => $locale->text('Per. Inv.'), },
+    'shippingpoint'           => { 'text' => $locale->text('Shipping Point'), },
+    'taxzone'                 => { 'text' => $locale->text('Steuersatz'), },
   );
 
-  foreach my $name (qw(id transdate reqdate quonumber ordnumber name employee salesman shipvia transaction_description)) {
+  foreach my $name (qw(id transdate reqdate quonumber ordnumber name employee salesman shipvia transaction_description shippingpoint taxzone)) {
     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
   }
@@ -888,6 +898,7 @@ sub orders {
   push @options, $locale->text('Order Number')            . " : $form->{ordnumber}"                       if $form->{ordnumber};
   push @options, $locale->text('Notes')                   . " : $form->{notes}"                           if $form->{notes};
   push @options, $locale->text('Transaction description') . " : $form->{transaction_description}"         if $form->{transaction_description};
+  push @options, $locale->text('Shipping Point')          . " : $form->{shippingpoint}"                   if $form->{shippingpoint};
   if ( $form->{transdatefrom} or $form->{transdateto} ) {
     push @options, $locale->text('Order Date');
     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
@@ -908,6 +919,9 @@ sub orders {
     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
   }
+  if ($form->{taxzone_id} ne '') { # taxzone_id could be 0
+    push @options, $locale->text('Steuersatz') . " : " . SL::DB::TaxZone->new(id => $form->{taxzone_id})->load->description;
+  }
 
   $report->set_options('top_info_text'        => join("\n", @options),
                        'raw_top_info_text'    => $form->parse_html_template('oe/orders_top'),
index 088435b8fa5d6fa1252f6af2ff82aad9963a10de..ef3f98aca8ebb12ceda83f38a400d483d49ebcb0 100644 (file)
@@ -35,10 +35,14 @@ Größere neue Features:
   WebDAV-Verzeichnis pro Mandant gibt. Die dafür notwendigen
   Umstellungen werden zusammen mit dem Datenbankupgrade durchgeführt.
 
-
+- CSV-Import von Aufträgen
 
 Kleinere neue Features und Detailverbesserungen:
 
+- Lieferbedingungen analog zu Zahlungsbedingungen eingeführt.
+  Sie können angelegt, beim Benutzer voreingestellt und in allen Ein- und Ver-
+  kaufsmasken gesetzt werden.
+
 - Rechte für die Anzeige von Debitoren- und Kreditorenbuchungen in Berichten
 __Es kann sinnvoll sein, den Standardeinkäufern und Verkäufern keinen Zugriff
 __auf Debitoren- oder Kreditorenbuchungen zu geben. Debitorenbuchungen werden 
index 801814342b362b6762a9c62d7ad5c5bd463746c7..d8dba3df77d2939d8f91f7322f1c07f969328160 100644 (file)
@@ -3219,7 +3219,7 @@ ln -s $(pwd)/kivitendo-task-server.service /etc/systemd/system/</programlisting>
         </sect3>
 
         <sect3 id="dokumentenvorlagen-und-variablen.allgemein-verkaeufer">
-          <title>Informationen über den Bearbeiter</title>
+          <title>Informationen über den Verkäufer</title>
 
           <variablelist>
             <varlistentry>
@@ -3349,6 +3349,25 @@ ln -s $(pwd)/kivitendo-task-server.service /etc/systemd/system/</programlisting>
             </varlistentry>
           </variablelist>
         </sect3>
+
+        <sect3 id="dokumentenvorlagen-und-variablen.allgemein-lieferbedingungen">
+          <title>Variablen für Lieferbedingungen</title>
+
+          <variablelist>
+            <varlistentry>
+              <term><varname>delivery_term</varname></term>
+              <listitem><para>Datenbank-Objekt der Lieferbedingung</para></listitem>
+            </varlistentry>
+            <varlistentry>
+              <term><varname>delivery_term.description</varname></term>
+              <listitem><para>Beschreibung der Lieferbedingung</para></listitem>
+            </varlistentry>
+            <varlistentry>
+              <term><varname>delivery_term.long_description</varname></term>
+              <listitem><para>Langtext bzw. übersetzter Langtext der Lieferbedingung</para></listitem>
+            </varlistentry>
+          </variablelist>
+        </sect3>
       </sect2>
 
       <sect2 id="dokumentenvorlagen-und-variablen.invoice">
index 5763062d35d0945848135f83a28679408fcd39d4..e8667b4c326a98c062fdc15fd215293c5533b985 100644 (file)
                         <code class="varname">employee_taxnumber</code>
                      </span></dt><dd><p>Steuernummer</p></dd><dt><span class="term">
                         <code class="varname">employee_tel</code>
-                     </span></dt><dd><p>Telefonnummer</p></dd></dl></div></div><div class="sect3" title="3.2.7.4. Informationen über den Bearbeiter"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-verkaeufer"></a>3.2.7.4. Informationen über den Bearbeiter</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+                     </span></dt><dd><p>Telefonnummer</p></dd></dl></div></div><div class="sect3" title="3.2.7.4. Informationen über den Verkäufer"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-verkaeufer"></a>3.2.7.4. Informationen über den Verkäufer</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
                         <code class="varname">salesman_address</code>
                      </span></dt><dd><p>Adressfeld</p></dd><dt><span class="term">
                         <code class="varname">salesman_businessnumber</code>
                         <code class="varname">taxdescription</code>
                      </span></dt><dd><p>Name der Steuer</p></dd><dt><span class="term">
                         <code class="varname">taxrate</code>
-                     </span></dt><dd><p>Steuersatz</p></dd></dl></div></div></div><div class="sect2" title="3.2.8. Variablen in Rechnungen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.invoice"></a>3.2.8. Variablen in Rechnungen</h3></div></div></div><div class="sect3" title="3.2.8.1. Allgemeine Variablen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.invoice-allgemein"></a>3.2.8.1. Allgemeine Variablen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+                     </span></dt><dd><p>Steuersatz</p></dd></dl></div></div><div class="sect3" title="3.2.7.6. Variablen für Lieferbedingungen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.allgemein-lieferbedingungen"></a>3.2.7.6. Variablen für Lieferbedingungen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
+                        <code class="varname">delivery_term</code>
+                     </span></dt><dd><p>Datenbank-Objekt der Lieferbedingung</p></dd><dt><span class="term">
+                        <code class="varname">delivery_term.description</code>
+                     </span></dt><dd><p>Beschreibung der Lieferbedingung</p></dd><dt><span class="term">
+                        <code class="varname">delivery_term.long_description</code>
+                     </span></dt><dd><p>Langtext bzw. übersetzter Langtext der Lieferbedingung</p></dd></dl></div></div></div><div class="sect2" title="3.2.8. Variablen in Rechnungen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.invoice"></a>3.2.8. Variablen in Rechnungen</h3></div></div></div><div class="sect3" title="3.2.8.1. Allgemeine Variablen"><div class="titlepage"><div><div><h4 class="title"><a name="dokumentenvorlagen-und-variablen.invoice-allgemein"></a>3.2.8.1. Allgemeine Variablen</h4></div></div></div><div class="variablelist"><dl><dt><span class="term">
                         <code class="varname">creditremaining</code>
                      </span></dt><dd><p>Verbleibender Kredit</p></dd><dt><span class="term">
                         <code class="varname">currency</code>
                         <code class="varname">invdate</code>
                      </span></dt><dd><p>Rechnungsdatum</p></dd><dt><span class="term">
                         <code class="varname">invnumber</code>
-                     </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.2.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.2.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.2.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e4654"></a>3.2.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
+                     </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.2.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.2.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.2.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e4685"></a>3.2.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
           Rechnung. Allerdings heißen die Variablen, die mit
           <code class="varname">inv</code> beginnen, jetzt anders. Bei den Angeboten
           fangen sie mit <code class="varname">quo</code> für "quotation" an:
index f6518edfe71816ab4fdd362532e85dc5e53cbbd9..8c7b140937bf5d88ef914727da477af8e3d6ea3e 100644 (file)
@@ -1,6 +1,6 @@
 <html><head>
       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-   <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s03.html" title="3.3. Excel-Vorlagen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e5261"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5267"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
+   <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s03.html" title="3.3. Excel-Vorlagen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e5292"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5298"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
         "main", der von überall erreichbar ist. Darüber hinaus sind bareword
         globs global und die meisten speziellen Variablen sind...
         speziell.</p><p>Daraus ergeben sich folgende Formen:</p><div class="variablelist"><dl><dt><span class="term">
@@ -25,7 +25,7 @@
               <code class="varname">$PACKAGE::form</code>.</p></dd><dt><span class="term">
                      <code class="literal">local $form</code>
                   </span></dt><dd><p>Alle Änderungen an <code class="varname">$form</code> werden am Ende
-              des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5368"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
+              des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5399"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
                <span class="productname">SQL-Ledger</span>™ hat fast alles im globalen
         namespace abgelegt, und erwartet, dass es da auch wiederzufinden ist.
         Unter <span class="productname">FCGI</span>™ müssen diese Sachen aber wieder
@@ -39,7 +39,7 @@
         dies hat, seit der Einführung, u.a. schon so manche langwierige
         Bug-Suche verkürzt. Da globale Variablen aber implizit mit Package
         angegeben werden, werden die nicht geprüft, und somit kann sich
-        schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5401"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
+        schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5432"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
         globale Variablen, die kanonisch sind, d.h. sie haben bestimmte
         vorgegebenen Eigenschaften, und alles andere sollte anderweitig
         umhergereicht werden.</p><p>Diese Variablen sind im Moment die folgenden neun:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
@@ -62,7 +62,7 @@
                      <code class="varname">$::request</code>
                   </p></li></ul></div><p>Damit diese nicht erneut als Müllhalde missbraucht werden, im
         Folgenden eine kurze Erläuterung der bestimmten vorgegebenen
-        Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5465"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
+        Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5496"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
               "<code class="classname">Form</code>"</p></li><li class="listitem"><p>Wird nach jedem Request gelöscht</p></li><li class="listitem"><p>Muss auch in Tests und Konsolenscripts vorhanden
               sein.</p></li><li class="listitem"><p>Enthält am Anfang eines Requests die Requestparameter vom
               User</p></li><li class="listitem"><p>Kann zwar intern über Requestgrenzen ein Datenbankhandle
   push @{ $form-&gt;{TEMPLATE_ARRAYS}{number} },          $form-&gt;{"partnumber_$i"};
   push @{ $form-&gt;{TEMPLATE_ARRAYS}{description} },     $form-&gt;{"description_$i"};
   # ...
-}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5549"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
+}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5580"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
               zugegriffen wird</p></li><li class="listitem"><p>Wird bei jedem Request neu erstellt.</p></li><li class="listitem"><p>Enthält die Userdaten des aktuellen Logins</p></li><li class="listitem"><p>Sollte nicht ohne Filterung irgendwo gedumpt werden oder
               extern serialisiert werden, weil da auch der Datenbankzugriff
               für diesen user drinsteht.</p></li><li class="listitem"><p>Enthält unter anderem Listenbegrenzung vclimit,
           überwiegend die Daten, die sich unter <span class="guimenu">Programm</span>
           -&gt; <span class="guimenuitem">Einstellungen</span> befinden, bzw. die
           Informationen über den Benutzer die über die
-          Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5588"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
+          Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5619"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
               sein.</p></li><li class="listitem"><p>Cached intern über Requestgrenzen hinweg benutzte
               Locales</p></li></ul></div><p>Lokalisierung für den aktuellen User. Alle Übersetzungen,
-          Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5606"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
+          Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5637"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
               Funktionen</p></li></ul></div><p>
                   <code class="varname">$::lxdebug</code> stellt Debuggingfunktionen
           bereit, wie "<code class="function">enter_sub</code>" und
           "<code class="function">message</code>" und "<code class="function">dump</code>" mit
           denen man flott Informationen ins Log (tmp/kivitendo-debug.log)
           packen kann.</p><p>Beispielsweise so:</p><pre class="programlisting">$main::lxdebug-&gt;message(0, 'Meine Konfig:' . Dumper (%::myconfig));
-$main::lxdebug-&gt;message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form-&gt;{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5643"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
+$main::lxdebug-&gt;message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form-&gt;{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5674"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
                   <code class="varname">$::auth</code> stellt Funktionen bereit um die
           Rechte des aktuellen Users abzufragen. Obwohl diese Informationen
           vom aktuellen User abhängen wird das Objekt aus
           Geschwindigkeitsgründen nur einmal angelegt und dann nach jedem
           Request kurz resettet.</p><p>Dieses Objekt kapselt auch den gerade aktiven Mandanten. Dessen Einstellungen können über
           <code class="literal">$::auth-&gt;client</code> abgefragt werden; Rückgabewert ist ein Hash mit den Werten aus der Tabelle
-          <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5672"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+          <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5703"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
               "<code class="classname">SL::LxOfficeConf</code>"</p></li><li class="listitem"><p>Global gecached</p></li><li class="listitem"><p>Repräsentation der
               <code class="filename">config/kivitendo.conf[.default]</code>-Dateien</p></li></ul></div><p>Globale Konfiguration. Configdateien werden zum Start gelesen
           und danach nicht mehr angefasst. Es ist derzeit nicht geplant, dass
@@ -152,16 +152,16 @@ $main::lxdebug-&gt;message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form-&gt;{
 file = /tmp/kivitendo-debug.log</pre><p>ist der Key <code class="varname">file</code> im Programm als
           <code class="varname">$::lx_office_conf-&gt;{debug}{file}</code>
           erreichbar.</p><div class="warning" title="Warnung" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warnung]" src="system/docbook-xsl/images/warning.png"></td><th align="left">Warnung</th></tr><tr><td align="left" valign="top"><p>Zugriff auf die Konfiguration erfolgt im Moment über
-            Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5708"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+            Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5739"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
               "<code class="classname">SL::InstanceConfiguration</code>"</p></li><li class="listitem"><p>wird pro Request neu erstellt</p></li></ul></div><p>Funktioniert wie <code class="varname">$::lx_office_conf</code>,
           speichert aber Daten die von der Instanz abhängig sind. Eine Instanz
           ist hier eine Mandantendatenbank. Beispielsweise überprüft
           </p><pre class="programlisting">$::instance_conf-&gt;get_inventory_system eq 'perpetual'</pre><p>
-          ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5729"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+          ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5760"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
               "<code class="varname">SL::Dispatcher</code>"</p></li><li class="listitem"><p>wird pro Serverprozess erstellt.</p></li><li class="listitem"><p>enthält Informationen über die technische Verbindung zum
               Server</p></li></ul></div><p>Der dritte Punkt ist auch der einzige Grund warum das Objekt
           global gespeichert wird. Wird vermutlich irgendwann in einem anderen
-          Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5747"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
+          Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5778"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
                   <code class="varname">$::request</code> ist ein generischer Platz um
           Daten "für den aktuellen Request" abzulegen. Sollte nicht für action
           at a distance benutzt werden, sondern um lokales memoizing zu
@@ -174,20 +174,20 @@ file = /tmp/kivitendo-debug.log</pre><p>ist der Key <code class="varname">file</
               <code class="varname">$::request</code>
                      </p></li><li class="listitem"><p>Muss ich von anderen Teilen des Programms lesend drauf
               zugreifen? Dann <code class="varname">$::request</code>, aber Zugriff über
-              Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5789"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
-        entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5794"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
+              Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e5820"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
+        entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5825"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
               Klassenfunktionen funktionieren</p></li><li class="listitem"><p>Aufruf als Klasse erzeugt Dummyobjekt was im
               Klassennamespace gehalten wird und über Requestgrenzen
               leaked</p></li><li class="listitem"><p>liegt jetzt unter
               <code class="varname">$::request-&gt;{cgi}</code>
-                     </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5810"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
+                     </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5841"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
               ein paar hundert mal pro Request eine Liste der Einheiten
               brauchen, und de als Parameter durch einen Riesenstack von
               Funktionen geschleift werden müssten.</p></li><li class="listitem"><p>Liegt jetzt unter
               <code class="varname">$::request-&gt;{cache}{all_units}</code>
                      </p></li><li class="listitem"><p>Wird nur in
               <code class="function">AM-&gt;retrieve_all_units()</code> gesetzt oder
-              gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5829"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
+              gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5860"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
               abzufangen.</p></li><li class="listitem"><p>Wurde entfernt, weil callsub nur einen Bruchteil der
               möglichen Rekursioenen darstellt, und da nie welche
               auftreten.</p></li><li class="listitem"><p>komplette recursion protection wurde entfernt.</p></li></ul></div></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s03.html">Zurück</a>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="ch04s02.html">Weiter</a></td></tr><tr><td width="40%" align="left" valign="top">3.3. Excel-Vorlagen&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="index.html">Zum Anfang</a></td><td width="40%" align="right" valign="top">&nbsp;4.2. Entwicklung unter FastCGI</td></tr></table></div></body></html>
\ No newline at end of file
index 627d461e0fc48c52463c4712b717b40d56efef92..c97f3d396ffce8beab17bb2fbdd43fbbbd3ec8e6 100644 (file)
@@ -3,7 +3,7 @@
    <title>kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><link rel="next" href="ch01.html" title="Kapitel 1. Aktuelle Hinweise"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</th></tr><tr><td width="20%" align="left">&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="ch01.html">Weiter</a></td></tr></table><hr></div><div lang="de" class="book" title="kivitendo 3.0.0: Installation, Konfiguration, Entwicklung"><div class="titlepage"><div><div><h1 class="title"><a name="kivitendo-documentation"></a>kivitendo 3.0.0: Installation, Konfiguration, Entwicklung</h1></div></div><hr></div><div class="toc"><p><b>Inhaltsverzeichnis</b></p><dl><dt><span class="chapter"><a href="ch01.html">1. Aktuelle Hinweise</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. Installation und Grundkonfiguration</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#Installation-%C3%9Cbersicht">2.1. Übersicht</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">2.2. Benötigte Software und Pakete</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#Betriebssystem">2.2.1. Betriebssystem</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#Pakete">2.2.2. Benötigte Perl-Pakete installieren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">2.3. Manuelle Installation des Programmpaketes</a></span></dt><dt><span class="sect1"><a href="ch02s04.html">2.4. kivitendo-Konfigurationsdatei</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s04.html#config.config-file.introduction">2.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.sections-parameters">2.4.2. Abschnitte und Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.prior-versions">2.4.3. Versionen vor 2.6.3</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s05.html">2.5. Anpassung der PostgreSQL-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s05.html#Zeichens%C3%A4tze-die-Verwendung-von-UTF-8">2.5.1. Zeichensätze/die Verwendung von Unicode/UTF-8</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#%C3%84nderungen-an-Konfigurationsdateien">2.5.2. Änderungen an Konfigurationsdateien</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-servergespeicherte-Prozeduren">2.5.3. Erweiterung für servergespeicherte Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Datenbankbenutzer-anlegen">2.5.4. Datenbankbenutzer anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s06.html">2.6. Webserver-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s06.html#d0e697">2.6.1. Grundkonfiguration mittels CGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#Apache-Konfiguration.FCGI">2.6.2. Konfiguration für FastCGI/FCGI</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s07.html">2.7. Der Task-Server</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-des-Task-Servers">2.7.1. Verfügbare und notwendige Konfigurationsoptionen</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Einbinden-in-den-Boot-Prozess">2.7.2. Automatisches Starten des Task-Servers beim Booten</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle">2.7.3. Wie der Task-Server gestartet und beendet wird</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle2">2.7.4. Task-Server mit mehreren Mandanten</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s08.html">2.8. Benutzerauthentifizierung und Administratorpasswort</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s08.html#Grundlagen-zur-Benutzerauthentifizierung">2.8.1. Grundlagen zur Benutzerauthentifizierung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Administratorpasswort">2.8.2. Administratorpasswort</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Authentifizierungsdatenbank">2.8.3. Authentifizierungsdatenbank</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Passwort%C3%BCberpr%C3%BCfung">2.8.4. Passwortüberprüfung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Name-des-Session-Cookies">2.8.5. Name des Session-Cookies</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Anlegen-der-Authentifizierungsdatenbank">2.8.6. Anlegen der Authentifizierungsdatenbank</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s09.html">2.9. Mandanten-, Benutzer- und Gruppenverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s09.html#Zusammenh%C3%A4nge">2.9.1. Zusammenhänge</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-Benutzer-Gruppen">2.9.2. Mandanten, Benutzer und Gruppen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Datenbanken-anlegen">2.9.3. Datenbanken anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppen-anlegen">2.9.4. Gruppen anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Benutzer-anlegen">2.9.5. Benutzer anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-anlegen">2.9.6. Mandanten anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s10.html">2.10. E-Mail-Versand aus kivitendo heraus</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s10.html#config.sending-email.sendmail">2.10.1. Versand über lokalen E-Mail-Server</a></span></dt><dt><span class="sect2"><a href="ch02s10.html#config.sending-email.smtp">2.10.2. Versand über einen SMTP-Server</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s11.html">2.11. Drucken mit kivitendo</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s11.html#Vorlagenverzeichnis-anlegen">2.11.1. Vorlagenverzeichnis anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#Vorlagen-Standard">2.11.2. Standard</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#f-tex">2.11.3. f-tex</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#Vorlagen-RB">2.11.4. RB</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#allgemeine-hinweise-zu-latex">2.11.5. Allgemeine Hinweise zu LaTeX Vorlagen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s12.html">2.12. OpenDocument-Vorlagen</a></span></dt><dt><span class="sect1"><a href="ch02s13.html">2.13. Konfiguration zur Einnahmenüberschussrechnung/Bilanzierung:
       EUR</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s13.html#config.eur.introduction">2.13.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.parameters">2.13.2. Konfigurationsparameter</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.setting-parameters">2.13.3. Festlegen der Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.inventory-system-perpetual">2.13.4. Bemerkungen zu Bestandsmethode</a></span></dt><dt><span class="sect2"><a href="ch02s13.html#config.eur.knonw-issues">2.13.5. Bekannte Probleme</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s14.html">2.14. SKR04 19% Umstellung für innergemeinschaftlichen Erwerb</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s14.html#config.skr04-update-3804.introduction">2.14.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s14.html#config.skr04-update-3804.create-chart">2.14.2. Konto 3804 manuell anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s15.html">2.15. Einstellungen pro Mandant</a></span></dt><dt><span class="sect1"><a href="ch02s16.html">2.16. kivitendo ERP verwenden</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch03.html">3. Features und Funktionen</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch03.html#features.periodic-invoices">3.1. Wiederkehrende Rechnungen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.introduction">3.1.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.configuration">3.1.2. Konfiguration</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.variables">3.1.3. Spezielle Variablen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.reports">3.1.4. Auflisten</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.task-server">3.1.5. Erzeugung der eigentlichen Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.create-for-current-month">3.1.6. Erste Rechnung für aktuellen Monat erstellen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s02.html">3.2. Dokumentenvorlagen und verfügbare Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.einf%C3%BChrung">3.2.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.variablen-ausgeben">3.2.2. Variablen ausgeben</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.verwendung-in-druckbefehlen">3.2.3. Verwendung in Druckbefehlen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.tag-style">3.2.4. Anfang und Ende der Tags verändern</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.zuordnung-dateinamen">3.2.5. Zuordnung von den Dateinamen zu den Funktionen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.dateinamen-erweitert">3.2.6. Sprache, Drucker und E-Mail</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.allgemeine-variablen">3.2.7. Allgemeine Variablen, die in allen Vorlagen vorhanden
         sind</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.invoice">3.2.8. Variablen in Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.dunning">3.2.9. Variablen in Mahnungen und Rechnungen über Mahngebühren</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.andere-vorlagen">3.2.10. Variablen in anderen Vorlagen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.bloecke">3.2.11. Blöcke, bedingte Anweisungen und Schleifen</a></span></dt><dt><span class="sect2"><a href="ch03s02.html#dokumentenvorlagen-und-variablen.markup">3.2.12. Markup-Code zur Textformatierung innerhalb von
-        Formularen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">3.3. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#excel-templates.summary">3.3.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.usage">3.3.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.syntax">3.3.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.limitations">3.3.4. Einschränkungen</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e5267">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5368">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5401">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5789">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format">4.3.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format-perl-files">4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.dbupgrade-tool">4.3.4. Hilfsscript dbupgrade2_tool.pl</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. Translations and languages</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#translations-languages.introduction">4.4.1. Introduction</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.character-set">4.4.2. Character set</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.file-structure">4.4.3. File structure</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s05.html">4.5. Die kivitendo-Test-Suite</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.intro">4.5.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.prerequisites">4.5.2. Voraussetzungen</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.execution">4.5.3. 
+        Formularen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">3.3. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#excel-templates.summary">3.3.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.usage">3.3.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.syntax">3.3.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#excel-templates.limitations">3.3.4. Einschränkungen</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e5298">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5399">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5432">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5820">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format">4.3.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.format-perl-files">4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#db-upgrade-files.dbupgrade-tool">4.3.4. Hilfsscript dbupgrade2_tool.pl</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. Translations and languages</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#translations-languages.introduction">4.4.1. Introduction</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.character-set">4.4.2. Character set</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#translations-languages.file-structure">4.4.3. File structure</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s05.html">4.5. Die kivitendo-Test-Suite</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.intro">4.5.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.prerequisites">4.5.2. Voraussetzungen</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.execution">4.5.3. 
           Existierende Tests ausführen
         </a></span></dt><dt><span class="sect2"><a href="ch04s05.html#devel.testsuite.meaning_of_scripts">4.5.4. 
           Bedeutung der verschiedenen Test-Scripte
index 8ab41566e87af081476ffbf0019dd5e9e92d8431..6b364908d355f288460a53b4fbfddd98a5d24d68 100644 (file)
Binary files a/doc/kivitendo-Dokumentation.pdf and b/doc/kivitendo-Dokumentation.pdf differ
index ea2c596354e029c361700da586133b6adbaee300..ecaa55c2d512bcda69f799ab424b31b51286e14c 100755 (executable)
@@ -194,7 +194,9 @@ $self->{texts} = {
   'Amended Advance Turnover Tax Return' => 'Berichtigte Anmeldung',
   'Amended Advance Turnover Tax Return (Nr. 10)' => 'Ist dies eine berichtigte Anmeldung? (Nr. 10/Zeile 15 Steuererklärung)',
   'Amount'                      => 'Betrag',
+  'Amount (for verification)'   => 'Betrag (zur Überprüfung)',
   'Amount Due'                  => 'Betrag fällig',
+  'Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.' => 'Betrag und Nettobetrag werden von kivitendo berechnet. "verify_amount" und "verify_netamount" können für Plausibilitätsprüfungen angegeben werden.',
   'Amount payable'              => 'Noch zu bezahlender Betrag',
   'Amount payable less discount' => 'Noch zu bezahlender Betrag abzüglich Skonto',
   'An exception occurred during execution.' => 'Während der Ausführung trat eine Ausnahme auf.',
@@ -219,6 +221,7 @@ $self->{texts} = {
   'Are you sure you want to delete Transaction' => 'Buchung wirklich löschen?',
   'Are you sure you want to delete this background job?' => 'Sind Sie sicher, dass Sie diesen Hintergrund-Job löschen möchten?',
   'Are you sure you want to delete this business?' => 'Sind Sie sicher, dass Sie diesen Kunden-/Lieferantentyp löschen wollen?',
+  'Are you sure you want to delete this delivery term?' => 'Wollen Sie diese Lieferbedingungen wirklich löschen?',
   'Are you sure you want to delete this department?' => 'Sind Sie sicher, dass Sie diese Abteilung löschen wollen?',
   'Are you sure you want to delete this payment term?' => 'Wollen Sie diese Zahlungsbedingungen wirklich löschen?',
   'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
@@ -359,6 +362,7 @@ $self->{texts} = {
   'CSV export -- options'       => 'CSV-Export -- Optionen',
   'CSV import: contacts'        => 'CSV-Import: Ansprechpersonen',
   'CSV import: customers and vendors' => 'CSV-Import: Kunden und Lieferanten',
+  'CSV import: orders'          => 'CSV-Import: Aufträge',
   'CSV import: parts and services' => 'CSV-Import: Waren und Dienstleistungen',
   'CSV import: projects'        => 'CSV-Import: Projekte',
   'CSV import: shipping addresses' => 'CSV-Import: Lieferadressen',
@@ -470,6 +474,8 @@ $self->{texts} = {
   'Confirmation'                => 'Auftragsbestätigung',
   'Contact'                     => 'Kontakt',
   'Contact Person'              => 'Ansprechperson',
+  'Contact Person (database ID)' => 'Ansprechperson (Datenbank-ID)',
+  'Contact Person (name)'       => 'Ansprechperson (Name)',
   'Contact deleted.'            => 'Ansprechperson gelöscht.',
   'Contact is in use and was flagged invalid.' => 'Die Ansprechperson ist noch in Verwendung und wurde deshalb nur als ungültig markiert.',
   'Contact person (surname)'    => 'Ansprechperson (Nachname)',
@@ -497,6 +503,7 @@ $self->{texts} = {
   'Create a new background job' => 'Einen neuen Hintergrund-Job anlegen',
   'Create a new business'       => 'Einen neuen Kunden-/Lieferantentyp erfassen',
   'Create a new client'         => 'Einen neuen Mandanten anlegen',
+  'Create a new delivery term'  => 'Neue Lieferbedingungen anlegen',
   'Create a new department'     => 'Eine neue Abteilung erfassen',
   'Create a new group'          => 'Neue Benutzergruppe erfassen',
   'Create a new payment term'   => 'Neue Zahlungsbedingungen anlegen',
@@ -526,6 +533,7 @@ $self->{texts} = {
   'Create new background job'   => 'Neuen Hintergrund-Job anlegen',
   'Create new business'         => 'Kunden-/Lieferantentyp erfassen',
   'Create new client #1'        => 'Neuen Mandanten #1 anlegen',
+  'Create new delivery term'    => 'Neue Lieferbedingungen anlegen',
   'Create new department'       => 'Neue Abteilung erfassen',
   'Create new payment term'     => 'Neue Zahlungsbedingung anlegen',
   'Create new templates from master templates' => 'Neue Druckvorlagen aus Vorlagensatz erstellen',
@@ -563,6 +571,7 @@ $self->{texts} = {
   'Custom Variables'            => 'Benutzerdefinierte Variablen',
   'Custom variables for module' => 'Benutzerdefinierte Variablen für Modul',
   'Customer'                    => 'Kunde',
+  'Customer (database ID)'      => 'Kunde (Datenbank-ID)',
   'Customer (name)'             => 'Kunde (Name)',
   'Customer Master Data'        => 'Kundenstammdaten',
   'Customer Name'               => 'Kundenname',
@@ -677,7 +686,13 @@ $self->{texts} = {
   'Delivery Orders'             => 'Lieferscheine',
   'Delivery Plan'               => 'Lieferplan',
   'Delivery Plan for currently outstanding sales orders' => 'Lieferplan für offene Verkaufsaufträge',
+  'Delivery Terms'              => 'Lieferbedingungen',
+  'Delivery terms'              => 'Lieferbedingungen',
+  'Delivery terms (database ID)' => 'Lieferbedingungen (Datenbank-ID)',
+  'Delivery terms (name)'       => 'Lieferbedingungen (Name)',
   'Department'                  => 'Abteilung',
+  'Department (database ID)'    => 'Abeilung (Datenbank-ID)',
+  'Department (description)'    => 'Abteilung (Beschreibung)',
   'Department 1'                => 'Abteilung (1)',
   'Department 2'                => 'Abteilung (2)',
   'Department Id'               => 'Reservierung',
@@ -719,6 +734,9 @@ $self->{texts} = {
   'Do you want to set the account number "#1" to "#2" and the name "#3" to "#4"?' => 'Soll die Kontonummer "#1" zu "#2" und den Name "#3" zu "#4" geändert werden?',
   'Do you want to store the existing onhand values into a new warehouse?' => 'M&ouml;chten Sie die vorhandenen Mengendaten in ein Lager &uuml;bertragen?',
   'Document'                    => 'Dokument',
+  'Document Project (database ID)' => 'Projektnummer des Belegs (Datenbank-ID)',
+  'Document Project (description)' => 'Projektnummer des Belegs (Beschreibung)',
+  'Document Project (number)'   => 'Projektnummer des Belegs',
   'Document Project Number'     => 'Projektnummer des Belegs',
   'Document Template'           => 'Dokumentvorlage',
   'Documentation'               => 'Dokumentation',
@@ -816,6 +834,7 @@ $self->{texts} = {
   'Edit bank account'           => 'Bankkonto bearbeiten',
   'Edit business'               => 'Kunden-/Lieferantentyp bearbeiten',
   'Edit custom variable'        => 'Benutzerdefinierte Variable bearbeiten',
+  'Edit delivery term'          => 'Lieferbedingungen bearbeiten',
   'Edit department'             => 'Abteilung bearbeiten',
   'Edit file'                   => 'Datei bearbeiten',
   'Edit greetings'              => 'Anreden bearbeiten',
@@ -839,6 +858,7 @@ $self->{texts} = {
   'Element disabled'            => 'Element deaktiviert',
   'Employee'                    => 'Bearbeiter',
   'Employee #1 saved!'          => 'Benutzer #1 gespeichert!',
+  'Employee (database ID)'      => 'Bearbeiter (Datenbank-ID)',
   'Employees'                   => 'Benutzer',
   'Empty selection for warehouse will not be added, even if the old bin is still visible (use back and forth to edit again).' => 'Leere Lager-Auswahl wird ignoriert, selbst wenn noch ein Lagerplatz ausgewählt ist. Alle Daten können durch zurück und vorwärts korrigiert werden.',
   'Empty transaction!'          => 'Buchung ist leer!',
@@ -859,17 +879,28 @@ $self->{texts} = {
   'Error when saving: #1'       => 'Fehler beim Speichern: #1',
   'Error!'                      => 'Fehler!',
   'Error: Buchungsgruppe missing or invalid' => 'Fehler: Buchungsgruppe fehlt oder ungültig',
+  'Error: Customer/vendor missing' => 'Fehler: Kunde/Lieferant fehlt',
   'Error: Customer/vendor not found' => 'Fehler: Kunde/Lieferant nicht gefunden',
   'Error: Gender (cp_gender) missing or invalid' => 'Fehler: Geschlecht (cp_gender) fehlt oder ungültig',
   'Error: Invalid business'     => 'Fehler: Kunden-/Lieferantentyp ungültig',
+  'Error: Invalid contact'      => 'Fehler: Ansprechperson ungültig',
   'Error: Invalid currency'     => 'Fehler: ungültige Währung',
+  'Error: Invalid delivery terms' => 'Fehler: Lieferbedingungen ungültig',
+  'Error: Invalid department'   => 'Fehler: Abteilung ungültig',
   'Error: Invalid language'     => 'Fehler: Sprache ungültig',
+  'Error: Invalid order for this order item' => 'Fehler: Auftrag für diese Position ungültig',
+  'Error: Invalid part'         => 'Fehler: Artikel ungültig',
   'Error: Invalid part type'    => 'Fehler: Artikeltyp ungültig',
   'Error: Invalid parts group'  => 'Fehler: Warengruppe ungültig',
   'Error: Invalid payment terms' => 'Fehler: Zahlungsbedingungen ungültig',
   'Error: Invalid price factor' => 'Fehler: Preisfaktor ungültig',
+  'Error: Invalid price group'  => 'Fehler: Preisgruppe ungültig',
+  'Error: Invalid project'      => 'Fehler: Projekt ungültig',
+  'Error: Invalid shipto'       => 'Fehler: Lieferadresse ungültig',
+  'Error: Invalid tax zone'     => 'Fehler: Steuerzone ungültig',
   'Error: Invalid vendor in column make_#1' => 'Fehler: Lieferant ungültig in Spalte make_#1',
   'Error: Name missing'         => 'Fehler: Name fehlt',
+  'Error: Part not found'       => 'Fehler: Artikel nicht gefunden',
   'Error: Unit missing or invalid' => 'Fehler: Einheit fehlt oder ungültig',
   'Errors'                      => 'Fehler',
   'Ertrag'                      => 'Ertrag',
@@ -1033,6 +1064,7 @@ $self->{texts} = {
   'II'                          => 'II',
   'III'                         => 'III',
   'IV'                          => 'IV',
+  'If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.' => 'Weichen die Beträge mehr als die "maximale Betragsabweichung" (siehe Einstellungen) ab, so wird diese Position als ungültig markiert.',
   'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => 'Falls angehakt wird der DATEV-Steuerschlüssel bei Buchungen auf dieses Konto nicht beim DATEV-Export mitexportiert, allerdings nur wenn zusätzlich der Konto-Steuerschlüssel vom Buchungs (Hauptbuch) Steuerschlüssel abweicht',
   'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => 'Falls konfiguriert, wird dieses Lager mit Lagerplatz für neu angelegte Waren vorausgewählt.',
   'If the article type is set to \'mixed\' then a column called \'type\' must be present.' => 'Falls der Artikeltyp auf \'gemischt\' gestellt wird, muss eine Spalte namens \'type\' vorhanden sein.',
@@ -1183,6 +1215,7 @@ $self->{texts} = {
   'Last Vendor Number'          => 'Letzte Lieferantennummer',
   'Last command output'         => 'Ausgabe des letzten Befehls',
   'Last run at'                 => 'Letzte Ausführung um',
+  'Lastcost'                    => 'Einkaufspreis',
   'Lastcost (with X being a number)' => 'Einkaufspreis (X ist eine fortlaufende Zahl)',
   'Lead'                        => 'Kundenquelle',
   'Leads'                       => 'Leads',
@@ -1251,6 +1284,7 @@ $self->{texts} = {
   'Master Data'                 => 'Stammdaten',
   'Master Data Bin Text Deleted' => 'Gelöschte Stammdaten Freitext-Lagerplätze',
   'Max. Dunning Level'          => 'höchste Mahnstufe',
+  'Maximal amount difference'   => 'maximale Betragsabweichung',
   'Maximum future booking interval' => 'Maximale Anzahl von Tagen an denen Buchungen in der Zukunft erlaubt sind.',
   'May'                         => 'Mai',
   'May '                        => 'Mai',
@@ -1296,6 +1330,7 @@ $self->{texts} = {
   'National Expenses'           => 'Aufwand Inland',
   'National Revenues'           => 'Erl&ouml;se Inland',
   'Net amount'                  => 'Nettobetrag',
+  'Net amount (for verification)' => 'Nettobetrag (zur Überprüfung)',
   'Netto Terms'                 => 'Zahlungsziel netto',
   'New Password'                => 'Neues Passwort',
   'New assembly'                => 'Neues Erzeugnis',
@@ -1332,6 +1367,7 @@ $self->{texts} = {
   'No customer has been selected yet.' => 'Es wurde noch kein Kunde ausgewählt.',
   'No data was found.'          => 'Es wurden keine Daten gefunden.',
   'No default currency'         => 'Keine Standardwährung',
+  'No delivery term has been created yet.' => 'Es wurden noch keine Lieferbedingungen angelegt',
   'No department has been created yet.' => 'Es wurde noch keine Abteilung erfasst.',
   'No dunnings have been selected for printing.' => 'Es wurden keine Mahnungen zum Drucken ausgew&auml;hlt.',
   'No file has been uploaded yet.' => 'Es wurde noch keine Datei hochgeladen.',
@@ -1418,7 +1454,10 @@ $self->{texts} = {
   'Order Number'                => 'Auftragsnummer',
   'Order Number missing!'       => 'Auftragsnummer fehlt!',
   'Order deleted!'              => 'Auftrag gelöscht!',
+  'Order/Item row name'         => 'Name der Auftrag-/Positions-Zeilen',
+  'OrderItem'                   => 'Position',
   'Ordered'                     => 'Von Kunden bestellt',
+  'Orders'                      => 'Aufträge',
   'Orders / Delivery Orders deleteable' => 'Aufträge / Lieferscheine löschbar',
   'Orientation'                 => 'Seitenformat',
   'Orphaned'                    => 'Nie benutzt',
@@ -1448,6 +1487,7 @@ $self->{texts} = {
   'Page #1/#2'                  => 'Seite #1/#2',
   'Paid'                        => 'bezahlt',
   'Part'                        => 'Ware',
+  'Part (database ID)'          => 'Artikel (Datenbank-ID)',
   'Part Description'            => 'Artikelbeschreibung',
   'Part Description missing!'   => 'Artikelbezeichnung fehlt!',
   'Part Notes'                  => 'Bemerkungen',
@@ -1468,7 +1508,7 @@ $self->{texts} = {
   'Password'                    => 'Passwort',
   'Payables'                    => 'Verbindlichkeiten',
   'Payment'                     => 'Zahlungsausgang',
-  'Payment Options'             => 'Zahlungsoptionen',
+  'Payment / Delivery Options'  => 'Zahlungs- und Lieferoptionen',
   'Payment Reminder'            => 'Zahlungserinnerung',
   'Payment Terms'               => 'Zahlungsbedingungen',
   'Payment Terms missing in row ' => 'Zahlungsfrist fehlt in Zeile ',
@@ -1566,6 +1606,8 @@ $self->{texts} = {
   'Price factor (name)'         => 'Preisfaktor (Name)',
   'Price factor deleted!'       => 'Preisfaktor gel&ouml;scht.',
   'Price factor saved!'         => 'Preisfaktor gespeichert.',
+  'Price group (database ID)'   => 'Preisgruppe (Datenbank-ID)',
+  'Price group (name)'          => 'Preisgruppe (Name) ',
   'Price information'           => 'Preisinformation',
   'Pricegroup'                  => 'Preisgruppe',
   'Pricegroup deleted!'         => 'Preisgruppe gelöscht!',
@@ -1595,6 +1637,9 @@ $self->{texts} = {
   'Proforma Invoice'            => 'Proformarechnung',
   'Program'                     => 'Programm',
   'Project'                     => 'Projekt',
+  'Project (database ID)'       => 'Projekt (Datenbank-ID)',
+  'Project (description)'       => 'Projekt (Beschreibung)',
+  'Project (number)'            => 'Projektnummer',
   'Project Description'         => 'Projektbeschreibung',
   'Project Number'              => 'Projektnummer',
   'Project Numbers'             => 'Projektnummern',
@@ -1739,6 +1784,7 @@ $self->{texts} = {
   'Sales price total'           => 'VK-Betrag',
   'Sales quotation'             => 'Angebot',
   'Salesman'                    => 'Verkäufer/in',
+  'Salesman (database ID)'      => 'Verkäufer (Datenbank-ID)',
   'Salesperson'                 => 'Verkäufer',
   'Same as the quote character' => 'Wie Anf&uuml;hrungszeichen',
   'Sat. Fax'                    => 'Sat. Fax',
@@ -1813,6 +1859,7 @@ $self->{texts} = {
   'Settings'                    => 'Einstellungen',
   'Setup Menu'                  => 'Menü-Variante',
   'Ship to'                     => 'Lieferadresse',
+  'Ship to (database ID)'       => 'Lieferadresse (Datenbank-ID)',
   'Ship via'                    => 'Transportmittel',
   'Shipping Address'            => 'Lieferadresse',
   'Shipping Point'              => 'Versandort',
@@ -1959,6 +2006,8 @@ $self->{texts} = {
   'Tax paid'                    => 'Vorsteuer',
   'Tax rate'                    => 'Steuersatz',
   'Tax saved!'                  => 'Steuer gespeichert!',
+  'Tax zone (database ID)'      => 'Steuerzone ((Datenbank-ID)',
+  'Tax zone (description)'      => 'Steuerzone (Beschreibung)',
   'Tax-O-Matic'                 => 'Steuer',
   'Tax-o-matic Account'         => 'Automatikbuchung auf Konto',
   'Taxaccount_coa'              => 'Automatikkonto',
@@ -2032,6 +2081,7 @@ $self->{texts} = {
   'The client has been created.' => 'Der Mandant wurde angelegt.',
   'The client has been deleted.' => 'Der Mandant wurde gelöscht.',
   'The client has been saved.'  => 'Der Mandant wurde gespeichert.',
+  'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => 'Die Spalte "datatype" muss vorhanden sein und sie muss die erste Spalte sein. Die Werte in dieser Spalte müssen die Namen der Auftrag-/Positions-Zeilen (siehe Einstellungen) sein.',
   'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => 'Die Spalte "make_X" can entweder die Datenbank-ID des Lieferanten, eine Lieferantennummer oder einen Lieferantennamen enthalten.',
   '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).' => 'Die Spalten-Dreiergruppen können mehrfach auftreten, sofern sie unterschiedliche Nummern "X" verwenden (z.B. "make_1", "model_1", "lastcost_1", "make_2", "model_2", "lastcost_2", "make_3", "model_3", "lastcost_3" etc).',
   'The columns &quot;Dunning Duedate&quot;, &quot;Total Fees&quot; and &quot;Interest&quot; show data for the previous dunning created for this invoice.' => 'Die Spalten &quot;Zahlbar bis&quot;, &quot;Kumulierte Geb&uuml;hren&quot; und &quot;Zinsen&quot; zeigen Daten der letzten f&uuml;r diese Rechnung erzeugten Mahnung.',
@@ -2060,6 +2110,10 @@ $self->{texts} = {
   'The deductible amount'       => 'Der abziehbare Skontobetrag',
   'The default value depends on the variable type:' => 'Die Bedeutung des Standardwertes h&auml;ngt vom Variablentypen ab:',
   'The delivery order has not been marked as delivered. The warehouse contents have not changed.' => 'Der Lieferschein wurde nicht als geliefert markiert. Der Lagerinhalt wurde nicht verändert.',
+  'The delivery term has been created.' => 'Die Lieferbedingungen wurden angelegt.',
+  'The delivery term has been deleted.' => 'Die Lieferbedingungen wurden gelöscht.',
+  'The delivery term has been saved.' => 'Die Lieferbedingungen wurden gespeichert.',
+  'The delivery term is in use and cannot be deleted.' => 'Die Lieferbedingungen werden bereits verwendet und können nicht gelöscht werden.',
   'The department has been created.' => 'Die Abteilung wurde angelegt.',
   'The department has been deleted.' => 'Die Abteiltung wurde gelöscht.',
   'The department has been saved.' => 'Die abteilung wurde gespeichert.',
@@ -2366,6 +2420,7 @@ $self->{texts} = {
   'Variable Description'        => 'Datenfeldbezeichnung',
   'Variable Name'               => 'Datenfeldname (intern)',
   'Vendor'                      => 'Lieferant',
+  'Vendor (database ID)'        => '(Datenbank-ID)',
   'Vendor (name)'               => 'Lieferant (Name)',
   'Vendor Invoice'              => 'Einkaufsrechnung',
   'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
index 38be9b36edcd449db0c7b531db7618ed8e9cd744..b5faacac26ca4a320a7d0571544ffae494e4893c 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 # -*- coding: utf-8; -*-
-# vim: fenc=UTF-8
+# vim: fenc=utf-8
 
 use utf8;
 
@@ -14,11 +14,13 @@ $self->{texts} = {
   ' Part Number missing!'       => '',
   ' missing!'                   => '',
   '#1 (custom variable)'        => '',
+  '#1 MD'                       => '',
+  '#1 h'                        => '',
   '#1 of #2 importable objects were imported.' => '',
   '#1 prices were updated.'     => '',
-  '* there are restrictions for the perpetual method, look at chapter "Bemerkungen zu Bestandsmethode"  in' => '',
-  '*) Since version 2.7 these parameters ares set in the client database and not in the lx-erp.conf / lx_office.conf file, details in chapter:' => '',
+  '(recommended) Insert the used currencies in the system. You can simply change the name of the currencies by editing the textfields above. Do not use a name of a currency that is already in use.' => '',
   '*/'                          => '',
+  ', if set'                    => '',
   '---please select---'         => '',
   '. Automatically generated.'  => '',
   '...after loggin in'          => '',
@@ -29,17 +31,18 @@ $self->{texts} = {
   '2. Quarter'                  => '',
   '3. Quarter'                  => '',
   '4. Quarter'                  => '',
+  '<b> I DO CARE!</b> Please check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '',
+  '<b> I DO CARE!</b> Please click back and cancel the update and come back after there has been at least one warehouse defined with bin(s).:' => '',
+  '<b> I DO NOT CARE</b> Please click continue and the following data (see list) will be deleted:' => '',
+  '<b>Automatically create new bins</b> in the following new warehouse ' => '',
+  '<b>Automatically create new bins</b> in the following warehouse if not selected in the list above' => '',
+  '<b>Default Bins Migration !READ CAREFULLY!</b>' => '',
   '<b>What</b> do you want to look for?' => '',
-  'A Buchungsgruppe consists of a descriptive name and the account numbers for the income and expense accounts for those four tax zones as well as the inventory account number.' => '',
   'A digit is required.'        => '',
-  'A group named &quot;Full Access&quot; has been created.' => '',
-  'A group with that name does already exist.' => '',
+  'A directory with the name for the new print templates exists already.' => '',
   'A lot of the usability of kivitendo has been enhanced with javascript. Although it is currently possible to use every aspect of kivitendo without javascript, we strongly recommend it. In a future version this may change and javascript may be necessary to access advanced features.' => '',
   'A lower-case character is required.' => '',
   'A special character is required (valid characters: #1).' => '',
-  'A temporary directory could not be created:' => '',
-  'A temporary file could not be created. Please verify that the directory "#1" is writeable by the webserver.' => '',
-  'A temporary file could not be created:' => '',
   'A unit with this name does already exist.' => '',
   'A valid taxkey is missing!'  => '',
   'A variable marked as \'editable\' can be changed in each quotation, order, invoice etc.' => '',
@@ -62,9 +65,12 @@ $self->{texts} = {
   'ASSETS'                      => '',
   'ATTENTION! If you enabled this feature you can not simply turn it off again without taking care that best_before fields are emptied in the database.' => '',
   'ATTENTION! You can not simply change it from periodic to perpetual once you started posting.' => '',
+  'AUTOMATICALLY MATCH BINS'    => '',
   'Abort'                       => '',
   'Abrechnungsnummer'           => '',
   'Abteilung'                   => '',
+  'Access rights'               => '',
+  'Access to clients'           => '',
   'Account'                     => '',
   'Account Category A'          => '',
   'Account Category C'          => '',
@@ -90,21 +96,23 @@ $self->{texts} = {
   'Account Link IC_taxpart'     => '',
   'Account Link IC_taxservice'  => '',
   'Account Number'              => '',
-  'Account Number already used!' => '',
   'Account Number missing!'     => '',
   'Account Nummer'              => '',
   'Account Type'                => '',
   'Account Type missing!'       => '',
+  'Account categories'          => '',
   'Account deleted!'            => '',
   'Account for fees'            => '',
   'Account for interest'        => '',
   'Account number'              => '',
   'Account number #1, bank code #2, #3' => '',
+  'Account number not unique!'  => '',
   'Account saved!'              => '',
   'Accounting Group deleted!'   => '',
   'Accounting Group saved!'     => '',
   'Accounting method'           => '',
   'Accrual'                     => '',
+  'Accrual accounting'          => '',
   'Active'                      => '',
   'Active?'                     => '',
   'Add'                         => '',
@@ -116,12 +124,11 @@ $self->{texts} = {
   'Add Accounts Receivables Transaction' => 'Add Sales Transaction',
   'Add Assembly'                => '',
   'Add Buchungsgruppe'          => '',
-  'Add Business'                => '',
+  'Add Client'                  => '',
   'Add Credit Note'             => '',
   'Add Customer'                => '',
   'Add Delivery Note'           => '',
   'Add Delivery Order'          => '',
-  'Add Department'              => '',
   'Add Dunning'                 => '',
   'Add Exchangerate'            => '',
   'Add Follow-Up'               => '',
@@ -132,7 +139,6 @@ $self->{texts} = {
   'Add Lead'                    => '',
   'Add Machine'                 => '',
   'Add Part'                    => '',
-  'Add Payment Terms'           => '',
   'Add Price Factor'            => '',
   'Add Pricegroup'              => '',
   'Add Printer'                 => '',
@@ -150,48 +156,54 @@ $self->{texts} = {
   'Add Storno Credit Note'      => '',
   'Add Transaction'             => '',
   'Add User'                    => '',
+  'Add User Group'              => '',
   'Add Vendor'                  => '',
   'Add Vendor Invoice'          => '',
   'Add Warehouse'               => '',
-  'Add a new group'             => '',
   'Add and edit units'          => '',
   'Add bank account'            => '',
   'Add custom variable'         => '',
+  'Add link: select records to link with' => '',
+  'Add linked record'           => '',
+  'Add links'                   => '',
+  'Add new currency'            => '',
+  'Add new custom variable'     => '',
   'Add note'                    => '',
   'Add unit'                    => '',
   'Address'                     => '',
+  'Admin'                       => '',
   'Administration'              => '',
-  'Administration (Used to access instance administration from user logins)' => '',
   'Administration area'         => '',
   'Advance turnover tax return' => '',
   'Aktion'                      => '',
   'All'                         => '',
   'All Accounts'                => '',
-  'All Datasets up to date!'    => '',
   'All changes in that file have been reverted.' => '',
-  'All database upgrades have been applied.' => '',
+  'All clients'                 => '',
   'All general ledger entries'  => '',
+  'All groups'                  => '',
   'All of the exports you have selected were already closed.' => '',
   'All reports'                 => '',
+  'All the other clients will start with an empty set of WebDAV folders.' => '',
   'All the selected exports have already been closed, or all of their items have already been executed.' => '',
   'All units have either no or exactly one base unit of which they are multiples.' => '',
   'All users'                   => '',
   'Allow access'                => '',
   'Allow the following users access to my follow-ups:' => '',
   'Alternatively you can create a new part which will then be selected.' => '',
-  'Alternatively you can skip this step and create groups yourself.' => '',
   'Amended Advance Turnover Tax Return' => '',
   'Amended Advance Turnover Tax Return (Nr. 10)' => '',
   'Amount'                      => '',
+  'Amount (for verification)'   => '',
   'Amount Due'                  => '',
-  'Amount has to be greater then zero! Wrong row number: ' => '',
+  'Amount and net amount are calculated by kivitendo. "verify_amount" and "verify_netamount" can be used for sanity checks.' => '',
   'Amount payable'              => '',
   'Amount payable less discount' => '',
+  'An exception occurred during execution.' => '',
   'An invalid character was used (invalid characters: #1).' => '',
   'An invalid character was used (valid characters: #1).' => '',
   'An upper-case character is required.' => '',
   'Annotations'                 => '',
-  'Another user with the login #1 does already exist.' => '',
   'Any stock contents containing a best before date will be impossible to stock out otherwise.' => '',
   'Ap aging on %s'              => '',
   'Application Error. No Format given' => '',
@@ -205,16 +217,16 @@ $self->{texts} = {
   'Apr'                         => '',
   'April'                       => '',
   'Ar aging on %s'              => '',
-  'Are you sure you want to delete Delivery Order Number #1?' => '',
   'Are you sure you want to delete Invoice Number' => '',
-  'Are you sure you want to delete Order Number' => '',
-  'Are you sure you want to delete Quotation Number' => '',
   'Are you sure you want to delete Transaction' => '',
+  'Are you sure you want to delete this background job?' => '',
   'Are you sure you want to delete this business?' => '',
+  'Are you sure you want to delete this delivery term?' => '',
   'Are you sure you want to delete this department?' => '',
   'Are you sure you want to delete this payment term?' => '',
   'Are you sure you want to remove the marked entries from the queue?' => '',
   'Are you sure you want to update the prices' => '',
+  'Are you sure?'               => '',
   'Article Code'                => '',
   'Article Code missing!'       => '',
   'Article type (see below)'    => '',
@@ -226,12 +238,10 @@ $self->{texts} = {
   'Assembly Number missing!'    => '',
   'Asset'                       => '',
   'Assets'                      => '',
-  'Assign new units'            => '',
-  'Assign units'                => '',
   'Assistant for general ledger corrections' => '',
   'Assume Tax Consultant Data in Tax Computation?' => '',
   'At least'                    => '',
-  'At least one Perl module that Lx-Office ERP requires for running is not installed on your system.' => '',
+  'At least one Perl module that kivitendo ERP requires for running is not installed on your system.' => '',
   '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.' => '',
   'At most'                     => '',
   'At the moment the transaction looks like this:' => '',
@@ -254,12 +264,12 @@ $self->{texts} = {
   'BWA'                         => '',
   'Back'                        => '',
   'Back to login'               => '',
-  'Back to the login page'      => '',
-  'Backup Dataset'              => '',
-  'Backup file'                 => '',
-  'Backup of dataset'           => '',
+  'Background job history'      => '',
+  'Background jobs'             => '',
+  'Background jobs and task server' => '',
   'Balance'                     => '',
   'Balance Sheet'               => '',
+  'Balancing'                   => '',
   'Bank'                        => '',
   'Bank Code'                   => '',
   'Bank Code (long)'            => '',
@@ -280,6 +290,8 @@ $self->{texts} = {
   'Basic Data'                  => '',
   'Batch Printing'              => '',
   'Bcc'                         => '',
+  'Bcc E-mail'                  => '',
+  'Because the useability gets worse if one partnumber is used for several parts (for example if you are searching a position for an invoice), partnumbers should be unique.' => '',
   'Belegnummer'                 => '',
   'Beratername'                 => '',
   'Beraternummer'               => '',
@@ -288,16 +300,19 @@ $self->{texts} = {
   'Bilanz'                      => '',
   'Billing Address'             => '',
   'Billing/shipping address (city)' => '',
+  'Billing/shipping address (country)' => '',
   'Billing/shipping address (street)' => '',
   'Billing/shipping address (zipcode)' => '',
   'Bin'                         => '',
   'Bin From'                    => '',
   'Bin List'                    => '',
   'Bin To'                      => '',
-  'Binding to the LDAP server as "#1" failed. Please check config/lx_office.conf.' => '',
+  'Binding to the LDAP server as "#1" failed. Please check config/kivitendo.conf.' => '',
   'Bins saved.'                 => '',
   'Bins that have been used in the past cannot be deleted anymore. For these bins there\'s no checkbox in the &quot;Delete&quot; column.' => '',
   'Birthday'                    => '',
+  'Birthday (after conversion)' => '',
+  'Birthday (before conversion)' => '',
   'Bis'                         => '',
   'Bis Konto: '                 => '',
   'Block'                       => '',
@@ -310,6 +325,7 @@ $self->{texts} = {
   'Both'                        => '',
   'Bottom'                      => '',
   'Bought'                      => '',
+  'Break up the update and contact a service provider.' => '',
   'Buchungsdatum'               => '',
   'Buchungsgruppe'              => '',
   'Buchungsgruppe (database ID)' => '',
@@ -327,6 +343,7 @@ $self->{texts} = {
   'CB Transaction'              => '',
   'CB Transactions'             => '',
   'CR'                          => '',
+  'CRM'                         => '',
   'CRM admin'                   => '',
   'CRM create customers, vendors and contacts' => '',
   'CRM follow up'               => '',
@@ -341,18 +358,20 @@ $self->{texts} = {
   'CRM status'                  => '',
   'CRM termin'                  => '',
   'CRM user'                    => '',
+  'CSS style for pictures'      => '',
   'CSV export -- options'       => '',
   'CSV import: contacts'        => '',
   'CSV import: customers and vendors' => '',
+  'CSV import: orders'          => '',
   'CSV import: parts and services' => '',
+  'CSV import: projects'        => '',
   'CSV import: shipping addresses' => '',
   'Calculate'                   => '',
-  'Calendar'                    => '',
   'Can not create that quantity with current stock' => '',
   'Cancel'                      => '',
   'Cancel Accounts Payables Transaction' => '',
   'Cancel Accounts Receivables Transaction' => '',
-  'Cannot create Lock!'         => '',
+  'Cannot check correct WebDAV folder' => '',
   'Cannot delete account!'      => '',
   'Cannot delete customer!'     => '',
   'Cannot delete default account!' => '',
@@ -372,6 +391,8 @@ $self->{texts} = {
   'Cannot post invoice!'        => '',
   'Cannot post payment for a closed period!' => '',
   'Cannot post payment!'        => '',
+  'Cannot post storno for a closed period!' => '',
+  'Cannot post transaction above the maximum future booking date!' => '',
   'Cannot post transaction for a closed period!' => '',
   'Cannot post transaction with a debit and credit entry for the same account!' => '',
   'Cannot post transaction!'    => '',
@@ -382,10 +403,16 @@ $self->{texts} = {
   'Cannot save preferences!'    => '',
   'Cannot save quotation!'      => '',
   'Cannot storno storno invoice!' => '',
+  'Cannot transfer. <br> Reason:<br>#1' => '',
   'Carry over shipping address' => '',
   'Cash'                        => '',
+  'Cash accounting'             => '',
+  'Cash basis accounting'       => '',
+  'Catalog'                     => '',
   'Cc'                          => '',
-  'Change Lx-Office installation settings (all menu entries beneath \'System\')' => '',
+  'Cc E-mail'                   => '',
+  'Change default bin for this parts' => '',
+  'Change kivitendo installation settings (most entries in the \'System\' menu)' => '',
   'Change representative to'    => '',
   'Changes in this block are only sensible if the account is NOT a summary account AND there exists one valid taxkey. To select both Receivables and Payables only make sense for Payment / Receipt (i.e. account cash).' => '',
   'Changes to Receivables and Payables are only possible if no transactions to this account are posted yet.' => '',
@@ -396,7 +423,6 @@ $self->{texts} = {
   'Chart Type'                  => '',
   'Chart balance'               => '',
   'Chart of Accounts'           => '',
-  'Chart of accounts'           => '',
   'Chartaccounts connected to this Tax:' => '',
   'Check'                       => 'Cheque',
   'Check Details'               => '',
@@ -414,15 +440,23 @@ $self->{texts} = {
   'City'                        => '',
   'Cleared Balance'             => '',
   'Clearing Tax Received (No 71)' => '',
-  'Click on login name to edit!' => '',
+  'Client'                      => '',
+  'Client #1'                   => '',
   'Client Configuration'        => '',
   'Client Configuration saved!' => '',
+  'Client administration: configuration, editing templates, task server control, background jobs (remaining entries in the \'System\' menu)' => '',
+  'Client list'                 => '',
+  'Client name'                 => '',
+  'Client to assign the existing WebDAV folders to' => '',
+  'Client to configure the printers for' => '',
+  'Clients this Group is valid for' => '',
+  'Clients this user has access to' => '',
   'Close'                       => '',
   'Close Books up to'           => '',
-  'Close Dialog'                => '',
   'Close Flash'                 => '',
   'Close SEPA exports'          => '',
   'Close Window'                => '',
+  'Close window'                => '',
   'Closed'                      => '',
   'Collective Orders only work for orders from one customer!' => '',
   'Column name'                 => '',
@@ -430,15 +464,18 @@ $self->{texts} = {
   'Comment'                     => '',
   'Company'                     => '',
   'Company Name'                => '',
+  'Company name'                => '',
+  'Company settings'            => '',
   'Compare to'                  => '',
   'Configuration'               => '',
   'Configuration of individual TODO items' => '',
   'Configure'                   => '',
-  'Confirm'                     => '',
   'Confirm!'                    => '',
   'Confirmation'                => '',
   'Contact'                     => '',
   'Contact Person'              => '',
+  'Contact Person (database ID)' => '',
+  'Contact Person (name)'       => '',
   'Contact deleted.'            => '',
   'Contact is in use and was flagged invalid.' => '',
   'Contact person (surname)'    => '',
@@ -446,31 +483,34 @@ $self->{texts} = {
   'Contacts'                    => '',
   'Continue'                    => '',
   'Contra'                      => '',
+  'Conversion of "birthday" contact person attribute' => '',
   'Copies'                      => '',
+  'Copy file from #1 to #2 failed: #3' => '',
   'Correct taxkey'              => '',
-  'Corrections'                 => '',
   'Costs'                       => '',
-  'Could not copy %s to %s. Reason: %s' => '',
   'Could not load class #1 (#2): "#3"' => '',
   'Could not load class #1, #2' => '',
   'Could not load employee'     => '',
-  'Could not open the file users/members.' => '',
-  'Could not open the old memberfile.' => '',
   'Could not print dunning.'    => '',
-  'Could not rename %s to %s. Reason: %s' => '',
   'Could not spawn ghostscript.' => '',
   'Could not spawn the printer command.' => '',
   'Could not update prices!'    => '',
   'Country'                     => '',
   'Create Assembly'             => '',
-  'Create Buchungsgruppen'      => '',
   'Create Chart of Accounts'    => '',
   'Create Dataset'              => '',
   'Create Date'                 => '',
+  'Create a new background job' => '',
   'Create a new business'       => '',
+  'Create a new client'         => '',
+  'Create a new delivery term'  => '',
   'Create a new department'     => '',
+  'Create a new group'          => '',
   'Create a new payment term'   => '',
-  'Create a standard group'     => '',
+  'Create a new printer'        => '',
+  'Create a new project'        => '',
+  'Create a new user'           => '',
+  'Create a new user group'     => '',
   'Create and edit RFQs'        => '',
   'Create and edit dunnings'    => '',
   'Create and edit invoices and credit notes' => '',
@@ -490,9 +530,12 @@ $self->{texts} = {
   'Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)' => '',
   'Create invoice?'             => '',
   'Create new'                  => '',
+  'Create new background job'   => '',
   'Create new business'         => '',
+  'Create new client #1'        => '',
   'Create new department'       => '',
   'Create new payment term'     => '',
+  'Create new templates from master templates' => '',
   'Create tables'               => '',
   'Created by'                  => '',
   'Created for'                 => '',
@@ -513,16 +556,23 @@ $self->{texts} = {
   'Curr'                        => '',
   'Currencies'                  => '',
   'Currency'                    => '',
+  'Currency (database ID)'      => '',
+  'Currency name'               => '',
+  'Currency names must be unique.' => '',
+  'Currency names must not be empty.' => '',
   'Current / Next Level'        => '',
   'Current Earnings'            => '',
   'Current assets account'      => '',
+  'Current filter'              => '',
   'Current profile'             => '',
-  'Current unit'                => '',
+  'Current status'              => '',
   'Current value:'              => '',
   'Custom Variables'            => '',
   'Custom variables for module' => '',
   'Customer'                    => '',
+  'Customer (database ID)'      => '',
   'Customer (name)'             => '',
+  'Customer Master Data'        => '',
   'Customer Name'               => '',
   'Customer Number'             => '',
   'Customer Order Number'       => '',
@@ -531,6 +581,7 @@ $self->{texts} = {
   'Customer missing!'           => '',
   'Customer not on file or locked!' => '',
   'Customer not on file!'       => '',
+  'Customer saved'              => '',
   'Customer saved!'             => '',
   'Customer type'               => '',
   'Customer variables'          => '',
@@ -551,22 +602,26 @@ $self->{texts} = {
   'DATEX - Export Assistent'    => '',
   'DELETED'                     => '',
   'DFV-Kennzeichen'             => '',
+  'DHL'                         => '',
   'DR'                          => '',
   'DUNNING STARTED'             => '',
+  'DUNS number'                 => '',
   'DUNS-Nr'                     => '',
-  'Database'                    => '',
+  'Data'                        => '',
   'Database Administration'     => '',
   'Database Connection Test'    => '',
   'Database Host'               => '',
+  'Database ID'                 => '',
+  'Database Management'         => '',
   'Database User'               => '',
-  'Database User missing!'      => '',
-  'Database backups and restorations are disabled in the configuration.' => '',
+  'Database host and port'      => '',
+  'Database login (#1)'         => '',
   'Database name'               => '',
+  'Database settings'           => '',
   'Database template'           => '',
   'Database update error:'      => '',
-  'Dataset'                     => '',
+  'Database user and password'  => '',
   'Dataset missing!'            => '',
-  'Dataset name'                => '',
   'Dataset upgrade'             => '',
   'Date'                        => '',
   'Date Format'                 => '',
@@ -590,22 +645,32 @@ $self->{texts} = {
   'Decrease'                    => '',
   'Default (no language selected)' => '',
   'Default Accounts'            => '',
+  'Default Bin'                 => '',
+  'Default Bin with ignoring onhand' => '',
+  'Default Client (unconfigured)' => '',
   'Default Customer/Vendor Language' => '',
+  'Default Transfer'            => '',
+  'Default Transfer Out always succeed. The current part onhand is ignored and the inventory can have negative stocks (not recommended).' => '',
+  'Default Transfer Out with negative inventory' => '',
+  'Default Transfer with Master Bin' => '',
+  'Default Warehouse'           => '',
+  'Default Warehouse with ignoring on hand' => '',
   'Default buchungsgruppe'      => '',
+  'Default client'              => '',
+  'Default currency'            => '',
+  'Default currency missing!'   => '',
   'Default output medium'       => '',
   'Default printer'             => '',
   'Default template format'     => '',
   'Default unit'                => '',
   'Default value'               => '',
-  'Defaults saved.'             => '',
   'Delete'                      => '',
   'Delete Account'              => '',
   'Delete Contact'              => '',
   'Delete Dataset'              => '',
   'Delete Shipto'               => '',
-  'Delete delivery order'       => '',
   'Delete drafts'               => '',
-  'Delete group'                => '',
+  'Delete links'                => '',
   'Delete profile'              => '',
   'Delete transaction'          => '',
   'Deleted'                     => '',
@@ -620,7 +685,13 @@ $self->{texts} = {
   'Delivery Orders'             => '',
   'Delivery Plan'               => '',
   'Delivery Plan for currently outstanding sales orders' => '',
+  'Delivery Terms'              => '',
+  'Delivery terms'              => '',
+  'Delivery terms (database ID)' => '',
+  'Delivery terms (name)'       => '',
   'Department'                  => '',
+  'Department (database ID)'    => '',
+  'Department (description)'    => '',
   'Department 1'                => '',
   'Department 2'                => '',
   'Department Id'               => '',
@@ -639,7 +710,6 @@ $self->{texts} = {
   'Destination warehouse and bin' => '',
   'Details (one letter abbreviation)' => '',
   'Difference'                  => '',
-  'Dimension unit'              => '',
   'Directory'                   => '',
   'Discard duplicate entries in CSV file' => '',
   'Discard entries with duplicates in database or CSV file' => '',
@@ -647,6 +717,7 @@ $self->{texts} = {
   'Display'                     => '',
   'Display file'                => '',
   'Display options'             => '',
+  'Do not change the tax rate of taxkey 0.' => '',
   'Do not check for duplicates' => '',
   'Do not set default buchungsgruppe' => '',
   '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.' => '',
@@ -654,25 +725,29 @@ $self->{texts} = {
   'Do you really want to delete AP transaction #1?' => '',
   'Do you really want to delete AR transaction #1?' => '',
   'Do you really want to delete GL transaction #1?' => '',
-  'Do you really want to delete this group?' => '',
+  'Do you really want to delete the selected links?' => '',
   'Do you really want to delete this object?' => '',
   'Do you really want to delete this warehouse?' => '',
-  'Do you want kivitendo to create a group for access to all functions?' => '',
   'Do you want to <b>limit</b> your search?' => '',
   'Do you want to carry this shipping address over to the new purchase order so that the vendor can deliver the goods directly to your customer?' => '',
+  'Do you want to set the account number "#1" to "#2" and the name "#3" to "#4"?' => '',
   'Do you want to store the existing onhand values into a new warehouse?' => '',
   'Document'                    => '',
+  'Document Project (database ID)' => '',
+  'Document Project (description)' => '',
+  'Document Project (number)'   => '',
+  'Document Project Number'     => '',
   'Document Template'           => '',
+  'Documentation'               => '',
+  'Documentation (in German)'   => '',
   'Documents'                   => '',
-  'Document Project Number'     => '',
   'Documents in the WebDAV repository' => '',
   'Done'                        => '',
+  'Double partnumbers'          => '',
   'Download SEPA XML export file' => '',
   'Download sample file'        => '',
-  'Download the backup'         => '',
   'Draft saved.'                => '',
   'Drawing'                     => '',
-  'Driver'                      => '',
   'Dropdown Limit'              => '',
   'Due'                         => '',
   'Due Date'                    => '',
@@ -695,7 +770,7 @@ $self->{texts} = {
   'Dunnings'                    => '',
   'Duplicate in CSV file'       => '',
   'Duplicate in database'       => '',
-  'During this user migration kivitendo can create such a group for you and grant all users access to all of kivitendo\'s functions.' => '',
+  'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => '',
   'E-mail'                      => '',
   'E-mail Statement to'         => '',
   'E-mail address missing!'     => '',
@@ -709,10 +784,7 @@ $self->{texts} = {
   'ELSTER Export nach Winston'  => '',
   'ELSTER Tax Number'           => '',
   'EQUITY'                      => '',
-  'EU with VAT ID'              => '',
-  'EU without VAT ID'           => '',
   'EUER'                        => '',
-  'EUR'                         => '',
   'Earlier versions of kivitendo contained bugs which might have led to wrong entries in the general ledger.' => '',
   'Edit'                        => '',
   'Edit Access Rights'          => '',
@@ -724,6 +796,7 @@ $self->{texts} = {
   'Edit Assembly'               => '',
   'Edit Bins'                   => '',
   'Edit Buchungsgruppe'         => '',
+  'Edit Client'                 => '',
   'Edit Credit Note'            => '',
   'Edit Customer'               => '',
   'Edit Dunning'                => '',
@@ -740,7 +813,6 @@ $self->{texts} = {
   'Edit Price Factor'           => '',
   'Edit Pricegroup'             => '',
   'Edit Printer'                => '',
-  'Edit Project'                => '',
   'Edit Purchase Delivery Order' => '',
   'Edit Purchase Order'         => '',
   'Edit Quotation'              => '',
@@ -753,28 +825,27 @@ $self->{texts} = {
   'Edit Storno Credit Note'     => '',
   'Edit Storno Invoice'         => '',
   'Edit User'                   => '',
+  'Edit User Group'             => '',
   'Edit Vendor'                 => '',
   'Edit Vendor Invoice'         => '',
   'Edit Warehouse'              => '',
-  'Edit and delete a group'     => '',
+  'Edit background job'         => '',
   'Edit bank account'           => '',
   'Edit business'               => '',
   'Edit custom variable'        => '',
+  'Edit delivery term'          => '',
   'Edit department'             => '',
   'Edit file'                   => '',
   'Edit greetings'              => '',
-  'Edit group '                 => '',
-  'Edit group membership'       => '',
-  'Edit groups'                 => '',
-  'Edit membership'             => '',
   'Edit note'                   => '',
   'Edit payment term'           => '',
   'Edit prices and discount (if not used, textfield is ONLY set readonly)' => '',
-  'Edit rights'                 => '',
+  'Edit project'                => '',
+  'Edit project #1'             => '',
   'Edit templates'              => 'Templates, edit',
   'Edit the Delivery Order'     => '',
   'Edit the configuration for periodic invoices' => '',
-  'Edit the membership of all users in all groups:' => '',
+  'Edit the currency names in order to rename them.' => '',
   'Edit the purchase_order'     => '',
   'Edit the request_quotation'  => '',
   'Edit the sales_order'        => '',
@@ -786,13 +857,16 @@ $self->{texts} = {
   'Element disabled'            => '',
   'Employee'                    => '',
   'Employee #1 saved!'          => '',
+  'Employee (database ID)'      => '',
   'Employees'                   => '',
+  'Empty selection for warehouse will not be added, even if the old bin is still visible (use back and forth to edit again).' => '',
   'Empty transaction!'          => '',
   'End date'                    => '',
   'Enter a description for this new draft.' => '',
   'Enter longdescription'       => '',
   'Enter the requested execution date or leave empty for the quickest possible execution:' => '',
-  'Enter up to 3 letters separated by a colon (i.e CAD:USD:EUR) for your native and foreign currencies' => '',
+  'Entries for which automatic conversion failed:' => '',
+  'Entries for which automatic conversion succeeded:' => '',
   'Equity'                      => '',
   'Error'                       => '',
   'Error in database control file \'%s\': %s' => '',
@@ -800,24 +874,39 @@ $self->{texts} = {
   'Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.' => '',
   'Error in row #1: The quantity you entered is bigger than the stocked quantity.' => '',
   'Error message from the database driver:' => '',
+  'Error message from the database: #1' => '',
   'Error when saving: #1'       => '',
   'Error!'                      => '',
   'Error: Buchungsgruppe missing or invalid' => '',
+  'Error: Customer/vendor missing' => '',
   'Error: Customer/vendor not found' => '',
   'Error: Gender (cp_gender) missing or invalid' => '',
   'Error: Invalid business'     => '',
+  'Error: Invalid contact'      => '',
+  'Error: Invalid currency'     => '',
+  'Error: Invalid delivery terms' => '',
+  'Error: Invalid department'   => '',
   'Error: Invalid language'     => '',
+  'Error: Invalid order for this order item' => '',
+  'Error: Invalid part'         => '',
   'Error: Invalid part type'    => '',
   'Error: Invalid parts group'  => '',
   'Error: Invalid payment terms' => '',
   'Error: Invalid price factor' => '',
+  'Error: Invalid price group'  => '',
+  'Error: Invalid project'      => '',
+  'Error: Invalid shipto'       => '',
+  'Error: Invalid tax zone'     => '',
   'Error: Invalid vendor in column make_#1' => '',
   'Error: Name missing'         => '',
+  'Error: Part not found'       => '',
   'Error: Unit missing or invalid' => '',
   'Errors'                      => '',
   'Ertrag'                      => '',
   'Ertrag prozentual'           => '',
   'Escape character'            => '',
+  'EuR'                         => '',
+  'Everyone can log in.'        => '',
   'Exact'                       => '',
   'Example: http://kivitendo.de' => '',
   'Excel'                       => '',
@@ -826,23 +915,28 @@ $self->{texts} = {
   'Exchangerate Difference'     => '',
   'Exchangerate for payment missing!' => '',
   'Exchangerate missing!'       => '',
+  'Execute now'                 => '',
   'Executed'                    => '',
   'Execution date'              => '',
   'Execution date from'         => '',
   'Execution date to'           => '',
-  'Existing Buchungsgruppen'    => '',
+  'Execution schedule'          => '',
+  'Execution status'            => '',
+  'Execution type'              => '',
   'Existing Datasets'           => '',
+  'Existing contacts (with column \'cp_id\')' => '',
+  'Existing customers/vendors with same customer/vendor number' => '',
   'Existing file on server'     => '',
   'Existing pending follow-ups for this item' => '',
   'Existing profiles'           => '',
   'Expected Tax'                => '',
   'Expense'                     => '',
   'Expense Account'             => '',
-  'Expense accno'               => '',
   'Expense/Asset'               => '',
   'Expenses EU with UStId'      => '',
   'Expenses EU without UStId'   => '',
   'Export Buchungsdaten'        => '',
+  'Export Number'               => '',
   'Export Stammdaten'           => '',
   'Export as CSV'               => '',
   'Export as PDF'               => '',
@@ -856,17 +950,18 @@ $self->{texts} = {
   'Factor missing!'             => '',
   'Falsches Datumsformat!'      => '',
   'Fax'                         => '',
+  'Features'                    => '',
   'Feb'                         => '',
   'February'                    => '',
   'Fee'                         => '',
   'Field'                       => '',
   'File'                        => '',
   'File name'                   => '',
-  'Files created by kivitendo\'s &quot;Backup Dataset&quot; function are such files.' => '',
   'Filter'                      => '',
   'Filter date by'              => '',
   'Filter for customer variables' => '',
   'Filter for item variables'   => '',
+  'Filter parts'                => '',
   'Finish'                      => '',
   'First 20 Lines'              => '',
   'Fix transaction'             => '',
@@ -885,7 +980,6 @@ $self->{texts} = {
   'Font size'                   => '',
   'For AP transactions it will replace the sales taxkeys with input taxkeys with the same tax rate.' => '',
   'For AR transactions it will replace the input taxkeys with sales taxkeys with the same tax rate.' => '',
-  'For each unit there\'s either no or exactly one base unit. If you chose a base unit then you also have to chose a factor. That way the new unit will be defined as a multiple of the base unit. The base unit must be the &quot;smaller&quot; one. A factor may not be less than 1. Therefore you may define &quot;kg&quot; with the base unit &quot;g&quot; and a factor of &quot;1&quot;, but not the other way round.' => '',
   'For further information read this: ' => '',
   'For type "customer" the perl module JSON is required. Please check this on system level: $ ./scripts/installation_check.pl' => '',
   'Foreign Exchange Gain'       => '',
@@ -901,11 +995,14 @@ $self->{texts} = {
   'Fristsetzung'                => '',
   'From'                        => '',
   'From Date'                   => '',
-  'From this version on the taxkey 0 is reserved for tax rate 0.' => '',
+  'From this version on a new feature is available.' => '',
+  'From this version on it is necessary to name a default value.' => '',
+  'From this version on the partnumber of services, articles and assemblies have to be unique.' => '',
+  'From this version on the taxkey 0 must have a tax rate of 0 (for DATEV compatibility).' => '',
   'Full Access'                 => '',
   'Full Preview'                => '',
   'Full access to all functions' => '',
-  'Furthermore you should define a taxkey for all accounts, because this update cannot be executed.' => '',
+  'Function/position'           => '',
   'Fwd'                         => 'Forward',
   'GL Transaction'              => '',
   'GL transactions changeable'  => '',
@@ -916,7 +1013,9 @@ $self->{texts} = {
   'General Ledger Transaction'  => '',
   'General ledger and cash'     => '',
   'General ledger corrections'  => '',
+  'General settings'            => '',
   'Generic Tax Report'          => '',
+  'Git revision: #1, #2 #3'     => '',
   'Given Name'                  => '',
   'Go one step back'            => '',
   'Go one step forward'         => '',
@@ -925,23 +1024,32 @@ $self->{texts} = {
   'Group'                       => '',
   'Group Invoices'              => '',
   'Group Items'                 => '',
+  'Group assignment'            => '',
   'Group deleted!'              => '',
+  'Group list'                  => '',
   'Group membership'            => '',
   'Group missing!'              => '',
   'Group saved!'                => '',
   'Groups'                      => '',
+  'Groups that are valid for this client for access rights' => '',
+  'Groups this user is a member in' => '',
+  'Groups valid for this client' => '',
   'HTML'                        => '',
   'HTML Templates'              => '',
+  'Handling of WebDAV'          => '',
   'Hardcopy'                    => '',
   'Has serial number'           => '',
   'Heading'                     => '',
-  'Help'                        => '',
   'Help Template Variables'     => '',
   'Help on column names'        => '',
+  'Here'                        => '',
+  'Here you only provide the credentials for logging into the database.' => '',
   'Here\'s an example command line:' => '',
   'Hide Filter'                 => '',
   'Hide by default'             => '',
   'Hide help text'              => '',
+  'Hide settings'               => '',
+  'Hints'                       => '',
   'History'                     => '',
   'History Search'              => '',
   'History Search Engine'       => '',
@@ -954,31 +1062,38 @@ $self->{texts} = {
   'ID-Nummer'                   => '',
   'II'                          => '',
   'III'                         => '',
-  'IMPORTANT NOTE: You cannot safely change currencies, IF you have already booking entries!' => '',
   'IV'                          => '',
+  'If amounts differ more than "Maximal amount difference" (see settings), this item is marked as invalid.' => '',
   'If checked the taxkey will not be exported in the DATEV Export, but only IF chart taxkeys differ from general ledger taxkeys' => '',
+  'If configured this bin will be preselected for all new parts. Also this bin will be used as the master default bin, if default transfer out with master bin is activated.' => '',
   'If the article type is set to \'mixed\' then a column called \'type\' must be present.' => '',
   'If the automatic creation of invoices for fees and interest is switched on for a dunning level then the following accounts will be used for the invoice.' => '',
   'If the database user listed above does not have the right to create a database then enter the name and password of the superuser below:' => '',
-  'If you chose to let Lx-Office do the migration then Lx-Office will also remove the old member file after creating a backup copy of it in the directory &quot;#1&quot;.' => '',
+  'If the default transfer out always succeed use this bin for negative stock quantity.' => '',
   'If you enter values for the part number and / or part description then only those bins containing parts whose part number or part description match your input will be shown.' => '',
+  'If you have not chosen for example the category revenue for a tax and you choose an revenue account to create a transfer in the general ledger, this tax will not be displayed in the tax dropdown.' => '',
+  'If you lock the system normal users won\'t be able to log in.' => '',
   'If you see this message, you most likely just setup your LX-Office and haven\'t added any entry types. If this is the case, the option is accessible for administrators in the System menu.' => '',
   'If you select a base unit then you also have to enter a factor.' => '',
-  'If you want to change any of these parameters then press the &quot;Back&quot; button, edit the file &quot;config/lx_office.conf&quot; and login into the admin module again.' => '',
-  'If you want to delete such a dataset you have to edit the user(s) that are using the dataset in question and have them use another dataset.' => '',
+  'If you want to change any of these parameters then press the "Back" button, edit the file "config/kivitendo.conf" and login into the admin module again.' => '',
+  'If you want to delete such a dataset you have to edit the client(s) that are using the dataset in question and have them use another dataset.' => '',
   'If you want to set up the authentication database yourself then log in to the administration panel. kivitendo will then create the database and tables for you.' => '',
-  'If you yourself want to upgrade the installation then please read the file &quot;doc/UPGRADE&quot; and follow the steps outlined in this file.' => '',
+  'If your old bins match exactly Bins in the Warehouse CLICK on <b>AUTOMATICALLY MATCH BINS</b>.' => '',
+  'Illegal characters have been removed from the following fields: #1' => '',
   'Image'                       => '',
   'Import'                      => '',
   'Import CSV'                  => '',
+  'Import Status'               => '',
   'Import file'                 => '',
+  'Import not started yet, please wait...' => '',
   'Import preview'              => '',
   'Import profiles'             => '',
   'Import result'               => '',
   'Import summary'              => '',
   'In order to do that hit the button "Delete transaction".' => '',
-  'In the latter case the tables needed by Lx-Office will be created in that database.' => '',
-  'In version 2.4.0 the administrator has to enter a list of units in the administrative section.' => '',
+  'In order to migrate the old folder structure into the new structure you have to chose which client the old structure will be assigned to.' => '',
+  'In order to use kivitendo you have to create at least a client, a user and a group.' => '',
+  'In the latter case the tables needed by kivitendo will be created in that database.' => '',
   'In-line'                     => '',
   'Inactive'                    => '',
   'Include Exchangerate Difference' => '',
@@ -988,29 +1103,31 @@ $self->{texts} = {
   'Include in drop-down menus'  => '',
   'Include invalid warehouses ' => '',
   'Includeable in reports'      => '',
+  'Included in reports by default' => '',
   'Including'                   => '',
   'Income Statement'            => '',
-  'Income accno'                => '',
   'Incoming Payments'           => '',
   'Incoming invoice number'     => '',
-  'Incorrect Password!'         => '',
-  'Incorrect password!.'        => '',
-  'Incorrect username or password!' => '',
+  'Inconsistency in database'   => '',
+  'Incorrect password!        => '',
+  'Incorrect username or password or no access to selected client!' => '',
   'Increase'                    => '',
   'Individual Items'            => '',
   'Information'                 => '',
+  'Insert with new customer/vendor number' => '',
+  'Insert with new database ID' => '',
   'Insert with new part number' => '',
   'Interest'                    => '',
   'Interest Rate'               => '',
   'Internal Notes'              => '',
-  'International'               => '',
   'Internet'                    => '',
-  'Introduction of Buchungsgruppen' => '',
-  'Introduction of units'       => '',
+  'Introduction of clients'     => '',
   'Inv. Duedate'                => '',
   'Invalid'                     => '',
   'Invalid follow-up ID.'       => '',
   'Invalid quantity.'           => '',
+  'Invalid request type \'#1\'' => '',
+  'Invalid transactions'        => '',
   'Invdate'                     => '',
   'Invdate from'                => '',
   'Inventory'                   => '',
@@ -1036,13 +1153,13 @@ $self->{texts} = {
   'Invoice total less discount' => '',
   'Invoice with Storno (abbreviation)' => '',
   'Invoices'                    => '',
+  'Invoices, Credit Notes & AR Transactions' => '',
   'Is Searchable'               => '',
   'Is this a summary account to record' => '',
+  'It can be changed later but must be unique within the installation.' => '',
+  'It is not allowed that a summary account occurs in a drop-down menu!' => '',
   'It is possible that even after such a correction there is something wrong with this transaction (e.g. taxes that don\'t match the selected taxkey). Therefore you should re-run the general ledger analysis.' => '',
-  'It is possible to do this automatically for some Buchungsgruppen, but not for all.' => '',
-  'It is possible to do this automatically for some units, but for others the user has to chose the new unit.' => '',
   'It is possible to make a quick DATEV export everytime you post a record to ensure things work nicely with their data requirements. This will result in a slight overhead though you can enable this for each type of record independantly.' => '',
-  'It may optionally be compressed with &quot;gzip&quot;.' => '',
   'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => '',
   'Item deleted!'               => '',
   'Item mode'                   => '',
@@ -1053,6 +1170,7 @@ $self->{texts} = {
   'Jan'                         => '',
   'January'                     => '',
   'Journal'                     => '',
+  'Journal of Last 10 Transfers' => '',
   'Jul'                         => '',
   'July'                        => '',
   'Jump to'                     => '',
@@ -1061,14 +1179,13 @@ $self->{texts} = {
   'KNE-Export erfolgreich!'     => '',
   'KNr. beim Kunden'            => '',
   'Keine Suchergebnisse gefunden!' => '',
-  'kivitendo needs to update the authentication database before you can proceed.' => '',
-  'kivitendo will then update the database automatically.' => '',
+  'Knowledge'                   => '',
   'Konten'                      => '',
   'L'                           => '',
   'LIABILITIES'                 => '',
   'LP'                          => '',
-  'Label'                       => '',
   'LaTeX Templates'             => '',
+  'Label'                       => '',
   'Landscape'                   => '',
   'Language'                    => '',
   'Language (database ID)'      => '',
@@ -1077,9 +1194,11 @@ $self->{texts} = {
   'Language deleted!'           => '',
   'Language missing!'           => '',
   'Language saved!'             => '',
+  'Language settings'           => '',
   'Languages'                   => '',
-  'Last Action'                 => '',
+  'Languages and translations'  => '',
   'Last Article Number'         => '',
+  'Last Assembly Number'        => '',
   'Last Cost'                   => '',
   'Last Credit Note Number'     => '',
   'Last Customer Number'        => '',
@@ -1093,29 +1212,28 @@ $self->{texts} = {
   'Last Service Number'         => '',
   'Last Transaction'            => '',
   'Last Vendor Number'          => '',
+  'Last command output'         => '',
+  'Last run at'                 => '',
+  'Lastcost'                    => '',
+  'Lastcost (with X being a number)' => '',
   'Lead'                        => '',
-  'Leave host and port field empty unless you want to make a remote connection.' => '',
+  'Leads'                       => '',
   'Left'                        => '',
   'Liability'                   => '',
   'Limit part selection'        => '',
   'Line Total'                  => '',
   'Line and column'             => '',
   'Line endings'                => '',
-  'List'                        => '',
-  'List Accounting Groups'      => '',
+  'Link direction'              => '',
+  'Link to'                     => '',
+  'Linked Records'              => '',
   'List Accounts'               => '',
-  'List Businesses'             => '',
-  'List Departments'            => '',
-  'List Groups'                 => '',
   'List Languages'              => '',
-  'List Lead'                   => '',
-  'List Payment Terms'          => '',
   'List Price'                  => '',
-  'List Price Factors'          => '',
-  'List Pricegroups'            => '',
+  'List Printers'               => '',
   'List Transactions'           => '',
-  'List Warehouses'             => '',
-  'List bank accounts'          => '',
+  'List Users, Clients and User Groups' => '',
+  'List current background jobs' => '',
   'List export'                 => '',
   'List of bank accounts'       => '',
   'List of bank collections'    => '',
@@ -1124,30 +1242,33 @@ $self->{texts} = {
   'List open SEPA exports'      => '',
   'Load draft'                  => '',
   'Load profile'                => '',
+  'Loading...'                  => '',
   'Local Tax Office Preferences' => '',
   'Lock System'                 => '',
+  'Lock and unlock installation' => '',
+  'Lock file handling failed. Please verify that the directory "#1" is writeable by the webserver.' => '',
   'Lockfile created!'           => '',
   'Lockfile removed!'           => '',
   'Login'                       => '',
   'Login Name'                  => '',
-  'Login name missing!'         => '',
   'Login of User'               => '',
   'Logout'                      => '',
   'Logout now'                  => '',
   'Long Dates'                  => '',
   'Long Description'            => '',
   'MAILED'                      => '',
+  'MD'                          => '',
   'Machine'                     => '',
-  'MSG_BROWSER_DOES_NOT_SUPPORT_IFRAMES' => '',
   'Main Preferences'            => '',
   'Main sorting'                => '',
   'Make'                        => '',
-  'Make (with X being a number)' => '',
+  'Make (vendor\'s database ID, number or name; with X being a number)' => '',
   'Make compatible for import'  => '',
   'Make default profile'        => '',
   'Manage Custom Variables'     => '',
   'Mandantennummer'             => '',
   'Mandatory Departments'       => '',
+  'Map'                         => '',
   'Mar'                         => '',
   'March'                       => '',
   'Margepercent'                => '',
@@ -1160,7 +1281,10 @@ $self->{texts} = {
   'Marked as paid'              => '',
   'Marked entries printed!'     => '',
   'Master Data'                 => '',
+  'Master Data Bin Text Deleted' => '',
   'Max. Dunning Level'          => '',
+  'Maximal amount difference'   => '',
+  'Maximum future booking interval' => '',
   'May'                         => '',
   'May '                        => '',
   'May set the BCC field when sending emails' => '',
@@ -1180,9 +1304,8 @@ $self->{texts} = {
   'Missing amount'              => '',
   'Missing parameter #1 in call to sub #2.' => '',
   'Missing parameter (at least one of #1) in call to sub #2.' => '',
-  'Missing qty'                 => '',
+  'Missing parameter for WebDAV file copy' => '',
   'Missing taxkeys in invoices with taxes.' => '',
-  'Missing user id!'            => '',
   'Mitarbeiter'                 => '',
   'Mixed (requires column "type")' => '',
   'Mobile'                      => '',
@@ -1199,71 +1322,75 @@ $self->{texts} = {
   'More than one #1 found matching, please be more specific.' => '',
   'More than one control file with the tag \'%s\' exist.' => '',
   'Multi mode not supported.'   => '',
-  'Multibyte Encoding'          => '',
   'MwSt. inkl.'                 => '',
   'Name'                        => '',
+  'Name and Street'             => '',
   'Name missing!'               => '',
-  'National'                    => '',
   'National Expenses'           => '',
   'National Revenues'           => '',
   'Net amount'                  => '',
+  'Net amount (for verification)' => '',
   'Netto Terms'                 => '',
-  'New Buchungsgruppe #1'       => '',
-  'New Templates'               => '',
-  'New Win/Tab'                 => '',
+  'New Password'                => '',
   'New assembly'                => '',
   'New bank account'            => '',
+  'New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.' => '',
+  'New client #1: The name must be unique and not empty.' => '',
   'New contact'                 => '',
   'New customer'                => '',
+  'New filter for tax accounts' => '',
   'New invoice'                 => '',
+  'New name'                    => '',
   'New part'                    => '',
   'New sales order'             => '',
   'New service'                 => '',
   'New shipto'                  => '',
-  'New unit'                    => '',
   'New vendor'                  => '',
+  'New window/tab'              => '',
   'Next Dunning Level'          => '',
+  'Next run at'                 => '',
   'No'                          => '',
   'No %s was found matching the search parameters.' => '',
   'No Company Address given'    => '',
   'No Company Name given'       => '',
   'No Customer was found matching the search parameters.' => '',
-  'No Database Drivers available!' => '',
-  'No Dataset selected!'        => '',
   'No Vendor was found matching the search parameters.' => '',
   'No action defined.'          => '',
-  'No backup file has been uploaded.' => '',
+  'No background job has been created yet.' => '',
   'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => '',
   'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => '',
   'No bins have been added to this warehouse yet.' => '',
   'No business has been created yet.' => '',
+  'No clients have been created yet.' => '',
   'No contact selected to delete' => '',
   'No customer has been selected yet.' => '',
   'No data was found.'          => '',
-  'No databases have been found on this server.' => '',
-  'No datasets have been selected.' => '',
+  'No default currency'         => '',
+  'No delivery term has been created yet.' => '',
   'No department has been created yet.' => '',
   'No dunnings have been selected for printing.' => '',
-  'No entries were found which had no unit assigned to them.' => '',
   'No file has been uploaded yet.' => '',
-  'No group has been selected, or the group does not exist anymore.' => '',
-  'No groups have been added yet.' => '',
-  'No or an unknown authenticantion module specified in "config/lx_office.conf".' => '',
+  'No groups have been created yet.' => '',
+  'No or an unknown authenticantion module specified in "config/kivitendo.conf".' => '',
   'No part was found matching the search parameters.' => '',
   'No payment term has been created yet.' => '',
   'No prices will be updated because no prices have been entered.' => '',
+  'No print templates have been created for this client yet. Please do so in the client configuration.' => '',
+  'No printers have been created yet.' => '',
   'No problems were recognized.' => '',
+  'No report with id #1'        => '',
   'No shipto selected to delete' => '',
+  'No summary account'          => '',
   'No transaction selected!'    => '',
+  'No transactions yet.'        => '',
   'No transfers were executed in this export.' => '',
-  'No unknown units where found.' => '',
+  'No users have been created yet.' => '',
   'No valid number entered for pricegroup "#1".' => '',
   'No vendor has been selected yet.' => '',
   'No warehouse has been created yet or the quantity of the bins is not configured yet.' => '',
   'No.'                         => '',
-  'Non-taxable Purchases'       => '',
-  'Non-taxable Sales'           => '',
   'None'                        => '',
+  'Normal users cannot log in.' => '',
   'Not Discountable'            => '',
   'Not delivered'               => '',
   'Not done yet'                => '',
@@ -1273,13 +1400,15 @@ $self->{texts} = {
   'Notes'                       => '',
   'Notes (translation for #1)'  => '',
   'Notes (will appear on hard copy)' => '',
+  'Notes for customer'          => '',
+  'Notes for vendor'            => '',
   'Nothing has been selected for removal.' => '',
   'Nothing has been selected for transfer.' => '',
   'Nothing selected!'           => '',
-  'Nothing to delete!'          => '',
+  'Nothing stocked yet.'        => '',
+  'Nothing will be created or deleted at this stage!' => '',
   'Nov'                         => '',
   'November'                    => '',
-  'Now the user must select a single Buchungsgruppe for each part instead of three distinct accounts.' => '',
   'Number'                      => '',
   'Number Format'               => '',
   'Number missing in Row'       => '',
@@ -1303,18 +1432,19 @@ $self->{texts} = {
   'One or more Perl modules missing' => '',
   'Only Warnings and Errors'    => '',
   'Only due follow-ups'         => '',
+  'Only groups that have been configured for the client the user logs in to will be considered.' => '',
   'Only shown in item mode'     => '',
-  'Oops. No valid action found to dispatch. Please report this case to the Lx-Office team.' => '',
+  'Oops. No valid action found to dispatch. Please report this case to the kivitendo team.' => '',
   'Open'                        => '',
   'Open Amount'                 => '',
-  'Open a further kivitendo Window or Tab' => '',
+  'Open a further kivitendo window or tab' => '',
   'Open amount'                 => '',
   'Open in new window'          => '',
   'Open this Website'           => '',
   'OpenDocument/OASIS'          => '',
   'Openings'                    => '',
-  'Optional comment'            => '',
   'Opportunity'                 => '',
+  'Optional comment'            => '',
   'Options'                     => '',
   'Or download the whole Installation Documentation as PDF (350kB) for off-line study (currently in German Language): ' => '',
   'Order'                       => '',
@@ -1323,15 +1453,20 @@ $self->{texts} = {
   'Order Number'                => '',
   'Order Number missing!'       => '',
   'Order deleted!'              => '',
+  'Order/Item row name'         => '',
+  'OrderItem'                   => '',
   'Ordered'                     => '',
+  'Orders'                      => '',
   'Orders / Delivery Orders deleteable' => '',
   'Orientation'                 => '',
   'Orphaned'                    => '',
+  'Orphaned currencies'         => '',
+  'Other'                       => '',
   'Other users\' follow-ups'    => '',
   'Other values are ignored.'   => '',
   'Others'                      => '',
-  'Otherwise all users will only have access to their own settings.' => '',
   'Otherwise the variable is only available for printing.' => '',
+  'Otherwise you can simply check create warehouse and bins and define a name for the warehouse (Bins will be created automatically) and then continue' => '',
   'Out of balance transaction!' => '',
   'Out of balance!'             => '',
   'Output Number Format'        => '',
@@ -1345,20 +1480,25 @@ $self->{texts} = {
   'POSTED'                      => '',
   'POSTED AS NEW'               => '',
   'PRINTED'                     => '',
+  'Package name'                => '',
   'Packing Lists'               => '',
   'Page'                        => '',
   'Page #1/#2'                  => '',
   'Paid'                        => '',
   'Part'                        => '',
+  'Part (database ID)'          => '',
   'Part Description'            => '',
   'Part Description missing!'   => '',
   'Part Notes'                  => '',
   'Part Number'                 => '',
   'Part Number missing!'        => '',
+  'Part picker'                 => '',
+  'Partnumber'                  => '',
   'Partnumber must not be set to empty!' => '',
   'Partnumber not unique!'      => '',
   'Parts'                       => '',
   'Parts Inventory'             => '',
+  'Parts Master Data'           => '',
   'Parts must have an entry type.' => '',
   'Parts with existing part numbers' => '',
   'Parts, services and assemblies' => '',
@@ -1367,6 +1507,7 @@ $self->{texts} = {
   'Password'                    => '',
   'Payables'                    => '',
   'Payment'                     => '',
+  'Payment / Delivery Options'  => '',
   'Payment Reminder'            => '',
   'Payment Terms'               => '',
   'Payment Terms missing in row ' => '',
@@ -1389,25 +1530,36 @@ $self->{texts} = {
   'Period'                      => '',
   'Period:'                     => '',
   'Periodic Invoices'           => '',
+  'Periodic inventory'          => '',
   'Periodic invoices active'    => '',
   'Periodic invoices inactive'  => '',
   'Periodicity'                 => '',
+  'Perpetual inventory'         => '',
+  'Person'                      => '',
   'Personal settings'           => '',
-  'Pg Database Administration'  => '',
   'Phone'                       => '',
   'Phone1'                      => '',
   'Phone2'                      => '',
   'Pick List'                   => '',
+  'Pictures for parts'          => '',
+  'Pictures for search parts'   => '',
   'Please Check the bank information for each customer:' => '',
   'Please Check the bank information for each vendor:' => '',
   'Please ask your administrator to create warehouses and bins.' => '',
+  'Please change the partnumber of the following parts and run the update again:' => '',
+  'Please choose for which categories the taxes should be displayed (otherwise remove the ticks):' => '',
+  'Please contact your administrator or a service provider.' => '',
   'Please contact your administrator.' => '',
+  'Please correct the settings and try again or deactivate that client.' => '',
+  'Please define a taxkey for the following taxes and run the update again:' => '',
+  'Please do so in the administration area.' => '',
   'Please enter a profile name.' => '',
-  'Please enter the login for the new user.' => '',
+  'Please enter the currency you are working with.' => '',
+  'Please enter the name for the new client.' => '',
+  'Please enter the name for the new group.' => '',
   'Please enter the name of the database that will be used as the template for the new database:' => '',
-  'Please enter the name of the dataset you want to restore the backup in.' => '',
   'Please enter the sales tax identification number.' => '',
-  'Please enter the taxnumber in the administration menu user preferences' => '',
+  'Please enter the taxnumber in the client configuration.' => '',
   'Please enter values'         => '',
   'Please insert object dimensions below.' => '',
   'Please insert your language values below' => '',
@@ -1418,15 +1570,13 @@ $self->{texts} = {
   'Please read the file'        => '',
   'Please select a customer from the list below.' => '',
   'Please select a part from the list below.' => '',
-  'Please select a user'        => '',
   'Please select a vendor from the list below.' => '',
-  'Please select the chart of accounts this installation is using from the list below.' => '',
-  'Please select the database you want to backup' => '',
+  'Please select the dataset you want to delete:' => '',
   'Please select the destination bank account for the collections:' => '',
   'Please select the source bank account for the transfers:' => '',
-  'Please seletct the dataset you want to delete:' => '',
+  'Please select which client configurations you want to create.' => '',
+  'Please set another taxnumber for the following taxes and run the update again:' => '',
   'Please specify a description for the warehouse designated for these goods.' => '',
-  'Please wait...'              => '',
   'Plural'                      => '',
   'Port'                        => '',
   'Portrait'                    => '',
@@ -1455,6 +1605,8 @@ $self->{texts} = {
   'Price factor (name)'         => '',
   'Price factor deleted!'       => '',
   'Price factor saved!'         => '',
+  'Price group (database ID)'   => '',
+  'Price group (name)'          => '',
   'Price information'           => '',
   'Pricegroup'                  => '',
   'Pricegroup deleted!'         => '',
@@ -1467,14 +1619,14 @@ $self->{texts} = {
   'Print dunnings'              => '',
   'Print list'                  => '',
   'Print options'               => '',
+  'Print templates'             => '',
+  'Print templates to use'      => '',
   'Printer'                     => '',
   'Printer Command'             => '',
-  'Printer Command missing!'    => '',
+  'Printer Description'         => '',
   'Printer Management'          => '',
-  'Printers are created for a user database. Please select a user. The associated database will be edited.' => '',
+  'Printer management'          => '',
   'Printing ... '               => '',
-  'Prior to version v2.4.0 the user could enter arbitrary strings as units for parts, services and in invoices, sales quotations etc.' => '',
-  'Prior to version v2.4.0 the user had to chose the accounts for each part and service.' => '',
   'Private E-mail'              => '',
   'Private Phone'               => '',
   'Problem'                     => '',
@@ -1484,19 +1636,20 @@ $self->{texts} = {
   'Proforma Invoice'            => '',
   'Program'                     => '',
   'Project'                     => '',
+  'Project (database ID)'       => '',
+  'Project (description)'       => '',
+  'Project (number)'            => '',
   'Project Description'         => '',
   'Project Number'              => '',
-  'Project Number missing!'     => '',
   'Project Numbers'             => '',
   'Project Transactions'        => '',
-  'Project deleted!'            => '',
-  'Project not on file!'        => '',
-  'Project saved!'              => '',
   'Projects'                    => '',
   'Projecttransactions'         => '',
   'Prozentual/Absolut'          => '',
+  'Purchase Delivery Orders'    => '',
   'Purchase Delivery Orders deleteable' => '',
   'Purchase Invoice'            => '',
+  'Purchase Invoices'           => '',
   'Purchase Order'              => '',
   'Purchase Orders'             => '',
   'Purchase Orders deleteable'  => '',
@@ -1536,7 +1689,6 @@ $self->{texts} = {
   'RFQs'                        => '',
   'ROP'                         => '',
   'Ranges of numbers'           => '',
-  'Ranges of numbers and default accounts' => '',
   'Re-run analysis'             => '',
   'Receipt'                     => '',
   'Receipt posted!'             => '',
@@ -1551,6 +1703,7 @@ $self->{texts} = {
   'Recorded taxkey'             => '',
   'Reference'                   => '',
   'Reference / Invoice Number'  => '',
+  'Reference day'               => '',
   'Reference missing!'          => '',
   'Release From Stock'          => '',
   'Remaining'                   => '',
@@ -1564,7 +1717,7 @@ $self->{texts} = {
   'Remove draft when posting'   => '',
   'Removed spoolfiles!'         => '',
   'Removing marked entries from queue ...' => '',
-  'Rename the group'            => '',
+  'Replace the orphaned currencies by other not orphaned currencies. To do so, please delete the currency in the textfields above and replace it by another currency. You could loose or change unintentionally exchangerates. Go on very carefully since you could destroy transactions.' => '',
   'Report Positions'            => '',
   'Report about warehouse contents' => '',
   'Report about warehouse transactions' => '',
@@ -1572,8 +1725,11 @@ $self->{texts} = {
   'Report for'                  => '',
   'Reports'                     => '',
   'Representative'              => '',
+  'Representative for Customer' => '',
   'Reqdate'                     => '',
+  'Request Quotations'          => '',
   'Request for Quotation'       => '',
+  'Request for Quotation Number' => '',
   'Request for Quotations'      => '',
   'Request quotation'           => '',
   'Requested execution date'    => '',
@@ -1581,13 +1737,18 @@ $self->{texts} = {
   'Requested execution date to' => '',
   'Required by'                 => '',
   'Reset'                       => '',
-  'Restore Dataset'             => '',
+  'Result'                      => '',
   'Revenue'                     => '',
   'Revenue Account'             => '',
   'Revenues EU with UStId'      => '',
   'Revenues EU without UStId'   => '',
   'Review of Aging list'        => '',
   'Right'                       => '',
+  'Row #1: amount has to be different from zero.' => '',
+  'Row number'                  => '',
+  'Row was created from current record' => '',
+  'Row was source for current record' => '',
+  'Run at'                      => '',
   'SAVED'                       => '',
   'SAVED FOR DUNNING'           => '',
   'SCREENED'                    => '',
@@ -1600,6 +1761,7 @@ $self->{texts} = {
   'Saldo neu'                   => '',
   'Saldo per'                   => '',
   'Sale Prices'                 => '',
+  'Sales Delivery Orders'       => '',
   'Sales Delivery Orders deleteable' => '',
   'Sales Invoice'               => '',
   'Sales Invoices'              => '',
@@ -1607,6 +1769,7 @@ $self->{texts} = {
   'Sales Orders'                => '',
   'Sales Orders deleteable'     => '',
   'Sales Price information'     => '',
+  'Sales Quotations'            => '',
   'Sales Report'                => '',
   'Sales and purchase invoices with inventory transactions with taxkeys' => '',
   'Sales delivery order'        => '',
@@ -1620,6 +1783,7 @@ $self->{texts} = {
   'Sales price total'           => '',
   'Sales quotation'             => '',
   'Salesman'                    => '',
+  'Salesman (database ID)'      => '',
   'Salesperson'                 => '',
   'Same as the quote character' => '',
   'Sat. Fax'                    => '',
@@ -1627,7 +1791,6 @@ $self->{texts} = {
   'Satz %'                      => '',
   'Save'                        => '',
   'Save Draft'                  => '',
-  'Save account first to insert taxkeys' => '',
   'Save and AP Transaction'     => '',
   'Save and AR Transaction'     => '',
   'Save and Close'              => '',
@@ -1636,18 +1799,23 @@ $self->{texts} = {
   'Save and Quotation'          => '',
   'Save and RFQ'                => '',
   'Save and close'              => '',
+  'Save and execute'            => '',
   'Save as new'                 => '',
+  'Save document in WebDAV repository' => '',
   'Save draft'                  => '',
   'Save profile'                => '',
   'Save settings as'            => '',
   'Saving the file \'%s\' failed. OS error message: %s' => '',
   'Screen'                      => '',
+  'Search'                      => '',
   'Search AP Aging'             => '',
   'Search AR Aging'             => '',
   'Search contacts'             => '',
+  'Search projects'             => '',
   'Search term'                 => '',
   'Searchable'                  => '',
   'Secondary sorting'           => '',
+  'Section "#1"'                => '',
   'Select'                      => '',
   'Select a Customer'           => '',
   'Select a customer'           => '',
@@ -1662,8 +1830,6 @@ $self->{texts} = {
   'Select from one of the projects below' => '',
   'Select postscript or PDF!'   => '',
   'Select tax office...'        => '',
-  'Select the chart of accounts in use' => '',
-  'Select the checkboxes that match users to the groups they should belong to.' => '',
   'Select type of removal'      => '',
   'Select type of transfer'     => '',
   'Selected'                    => '',
@@ -1675,7 +1841,6 @@ $self->{texts} = {
   'Sellprice for price group \'#1\'' => '',
   'Sellprice significant places' => '',
   'Semicolon'                   => '',
-  'Send the backup via Email'   => '',
   'Sep'                         => '',
   'Separator'                   => '',
   'Separator chararacter'       => '',
@@ -1686,14 +1851,14 @@ $self->{texts} = {
   'Service Contract'            => '',
   'Service Items'               => '',
   'Service Number missing!'     => '',
-  'Service unit'                => '',
+  'Service, assembly or part'   => '',
   'Services'                    => '',
   'Set Language Values'         => '',
   'Set eMail text'              => '',
   'Settings'                    => '',
   'Setup Menu'                  => '',
-  'Setup Templates'             => '',
   'Ship to'                     => '',
+  'Ship to (database ID)'       => '',
   'Ship via'                    => '',
   'Shipping Address'            => '',
   'Shipping Point'              => '',
@@ -1717,10 +1882,15 @@ $self->{texts} = {
   'Show "mark as paid" in ar transactions' => '',
   'Show "mark as paid" in purchase invoices' => '',
   'Show "mark as paid" in sales invoices' => '',
+  'Show AP transactions as part of AP invoice report' => '',
+  'Show AR transactions as part of AR invoice report' => '',
   'Show Bestbefore'             => '',
   'Show Filter'                 => '',
   'Show Salesman'               => '',
   'Show TODO list'              => '',
+  'Show Transfer via default'   => '',
+  'Show administration link'    => '',
+  'Show all parts'              => '',
   'Show by default'             => '',
   'Show custom variable search inputs' => '',
   'Show delete button in purchase delivery orders?' => '',
@@ -1728,20 +1898,30 @@ $self->{texts} = {
   'Show delete button in sales delivery orders?' => '',
   'Show delete button in sales orders?' => '',
   'Show details'                => '',
+  'Show details and reports of parts, services, assemblies' => '',
   'Show fields used for the best before date?' => '',
   'Show follow ups...'          => '',
   'Show help text'              => '',
   'Show items from invoices individually' => '',
   'Show old dunnings'           => '',
   'Show overdue sales quotations and requests for quotations...' => '',
+  'Show parts'                  => '',
+  'Show settings'               => '',
+  'Show the picture in the part form' => '',
+  'Show the pictures in the result for search parts' => '',
+  'Show the weights of articles and the total weight in orders, invoices and delivery notes?' => '',
+  'Show weights'                => '',
   'Show your TODO list after loggin in' => '',
   'Signature'                   => '',
   'Since bin is not enforced in the parts data, please specify a bin where goods without a specified bin will be put.' => '',
   'Single quotes'               => '',
   'Single values in item mode, cumulated values in invoice mode' => '',
   'Skip'                        => '',
+  'Skip entry'                  => '',
+  'Skipping due to existing entry in database' => '',
   'Skonto'                      => '',
   'Skonto Terms'                => '',
+  'So far you could use one partnumber for severel parts, for example a service and an article.' => '',
   'Sold'                        => '',
   'Solution'                    => '',
   'Sort By'                     => '',
@@ -1751,34 +1931,36 @@ $self->{texts} = {
   'Source bank account'         => '',
   'Source bin'                  => '',
   'Space'                       => '',
-  'Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. Due to known problems involving accounting software Lx-Office does not allow these.' => '',
+  'Split entry detected. The values you have entered will result in an entry with more than one position on both debit and credit. Due to known problems involving accounting software kivitendo does not allow these.' => '',
   'Spoolfile'                   => '',
   'Start Dunning Process'       => '',
   'Start analysis'              => '',
   'Start date'                  => '',
+  'Start task server'           => '',
   'Start the correction assistant' => '',
   'Startdate_coa'               => '',
   'Starting Balance'            => '',
+  'Starting the task server failed.' => '',
   'Starting with version 2.6.3 the configuration files in "config" have been consolidated.' => '',
   'Statement'                   => '',
   'Statement Balance'           => '',
   'Statement sent to'           => '',
   'Statements sent to printer!' => '',
   'Status'                      => '',
-  'Step 1 of 3: Parts'          => '',
   'Step 2'                      => '',
-  'Step 2 of 3: Services'       => '',
-  'Step 3 of 3: Assemblies'     => '',
-  'Step 3 of 3: Default units'  => '',
   'Steuersatz'                  => '',
   'Stock'                       => '',
   'Stock Qty for Date'          => '',
+  'Stock for part #1'           => '',
   'Stock value'                 => '',
   'Stocked Qty'                 => '',
+  'Stop task server'            => '',
+  'Stopping the task server failed. Output:' => '',
   'Storno'                      => '',
   'Storno (one letter abbreviation)' => '',
   'Storno Invoice'              => '',
   'Street'                      => '',
+  'Style the picture with the following CSS code' => '',
   'Stylesheet'                  => '',
   'Subject'                     => '',
   'Subject:'                    => '',
@@ -1802,8 +1984,12 @@ $self->{texts} = {
   'Tab'                         => '',
   'Target bank account'         => '',
   'Target table'                => '',
+  'Task Server is not running, starting it now. If this does not change, please check your task server config' => '',
+  'Task server control'         => '',
+  'Task server status'          => '',
   'Tax'                         => '',
   'Tax Consultant'              => '',
+  'Tax ID number'               => '',
   'Tax Included'                => '',
   'Tax Number'                  => '',
   'Tax Number / SSN'            => '',
@@ -1816,7 +2002,10 @@ $self->{texts} = {
   'Tax deleted!'                => '',
   'Tax number'                  => '',
   'Tax paid'                    => '',
+  'Tax rate'                    => '',
   'Tax saved!'                  => '',
+  'Tax zone (database ID)'      => '',
+  'Tax zone (description)'      => '',
   'Tax-O-Matic'                 => '',
   'Tax-o-matic Account'         => '',
   'Taxaccount_coa'              => '',
@@ -1842,104 +2031,137 @@ $self->{texts} = {
   'Templates'                   => '',
   'Terms missing in row '       => '',
   'Test and preview'            => '',
-  'Test connection'             => '',
+  'Test database connectivity'  => '',
   'Text field'                  => '',
   'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => '',
   'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => '',
   'Text, text field and number variables: The default value will be used as-is.' => '',
   'That export does not exist.' => '',
+  'That is why kivitendo could not find a default currency.' => '',
+  'The \'name\' is the field shown to the user during login.' => '',
   'The \'tag\' field must only consist of alphanumeric characters or the carachters - _ ( )' => '',
   'The AP transaction #1 has been deleted.' => '',
   'The AR transaction #1 has been deleted.' => '',
+  'The Bins in Inventory were only a information text field.' => '',
+  'The Bins in master data were only a information text field.' => '',
   'The GL transaction #1 has been deleted.' => '',
-  'The LDAP server "#1:#2" is unreachable. Please check config/lx_office.conf.' => '',
+  'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => '',
   'The SEPA export has been created.' => '',
   'The SEPA strings have been saved.' => '',
+  'The WebDAV feature has been used.' => '',
+  'The access rights a user has within a client instance is still governed by his group membership.' => '',
   'The access rights have been saved.' => '',
   'The account 3804 already exists, the update will be skipped.' => '',
   'The account 3804 will not be added automatically.' => '',
+  'The action you\'ve chosen has not been executed because the document does not contain any item yet.' => '',
+  'The administration area is always accessible.' => '',
   'The application "#1" was not found on the system.' => '',
   'The assembly has been created.' => '',
   'The assistant could not find anything wrong with #1. Maybe the problem has been solved in the meantime.' => '',
-  'The authentication configuration file &quot;config/lx_office.conf&quot; does not exist. This kivitendo installation has probably not been updated correctly yet. Please contact your administrator.' => '',
   'The authentication database is not reachable at the moment. Either it hasn\'t been set up yet or the database server might be down. Please contact your administrator.' => '',
   'The available options depend on the varibale type:' => '',
-  'The backup you upload here has to be a file created with &quot;pg_dump -o -Ft&quot;.' => '',
+  'The background job could not be destroyed.' => '',
+  'The background job has been created.' => '',
+  'The background job has been deleted.' => '',
+  'The background job has been saved.' => '',
+  'The background job was executed successfully.' => '',
   'The bank information must not be empty.' => '',
   'The base unit does not exist or it is about to be deleted in row %d.' => '',
   'The base unit does not exist.' => '',
   'The base unit relations must not contain loops (e.g. by saying that unit A\'s base unit is B, B\'s base unit is C and C\'s base unit is A) in row %d.' => '',
+  'The basic client tables have not been created for this client\'s database yet.' => '',
   'The business has been created.' => '',
   'The business has been deleted.' => '',
   'The business has been saved.' => '',
   'The business is in use and cannot be deleted.' => '',
   'The changing of tax-o-matic account is NOT recommended, but if you do so please also (re)configure buchungsgruppen and reconfigure ALL charts which point to this tax-o-matic account. ' => '',
+  'The client could not be deleted.' => '',
+  'The client has been created.' => '',
+  'The client has been deleted.' => '',
+  'The client has been saved.'  => '',
+  'The column "datatype" must be present and must be the first column. The values must be the row names (see settings) for order and item data respectively.' => '',
+  'The column "make_X" can contain either a vendor\'s database ID, a vendor number or a vendor\'s name.' => '',
+  '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).' => '',
   'The columns &quot;Dunning Duedate&quot;, &quot;Total Fees&quot; and &quot;Interest&quot; show data for the previous dunning created for this invoice.' => '',
-  'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/lx_office.conf.' => '',
+  'The combination of database host, port and name is not unique.' => '',
+  'The command is missing.'     => '',
+  'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.' => '',
   'The connection to the authentication database failed:' => '',
+  'The connection to the configured client database "#1" on host "#2:#3" failed.' => '',
   'The connection to the database could not be established.' => '',
   'The connection to the template database failed:' => '',
   'The connection was established successfully.' => '',
+  'The contact person attribute "birthday" is converted from a free-form text field into a date field.' => '',
   'The creation of the authentication database failed:' => '',
+  'The custom variable has been created.' => '',
   'The custom variable has been deleted.' => '',
   'The custom variable has been saved.' => '',
-  'The database #1 has been successfully deleted.' => '',
+  'The custom variable is in use and cannot be deleted.' => '',
   'The database for user management and authentication does not exist. You can create let kivitendo create it with the following parameters:' => '',
+  'The database host is missing.' => '',
+  'The database name is missing.' => '',
+  'The database port is missing.' => '',
   'The database update/creation did not succeed. The file #1 contained the following error:' => '',
-  'The database upgrade for the introduction of Buchungsgruppen is now complete.' => '',
-  'The database upgrade for the introduction of units is now complete.' => '',
-  'The dataset #1 has been successfully created.' => '',
-  'The dataset backup has been sent via email to #1.' => '',
-  'The dataset has to exist before a restoration can be started.' => '',
-  'The dataset name is missing.' => '',
+  'The database user is missing.' => '',
+  'The dataset #1 has been created.' => '',
+  'The dataset #1 has been deleted.' => '',
   'The deductible amount'       => '',
   'The default value depends on the variable type:' => '',
   'The delivery order has not been marked as delivered. The warehouse contents have not changed.' => '',
+  'The delivery term has been created.' => '',
+  'The delivery term has been deleted.' => '',
+  'The delivery term has been saved.' => '',
+  'The delivery term is in use and cannot be deleted.' => '',
   'The department has been created.' => '',
   'The department has been deleted.' => '',
   'The department has been saved.' => '',
   'The department is in use and cannot be deleted.' => '',
   'The description is missing.' => '',
+  'The description is not unique.' => '',
   'The description is shown on the form. Chose something short and descriptive.' => '',
-  'The directory "%s" could not be created:\n%s' => '',
   'The directory %s does not exist.' => '',
   'The discount in percent'     => '',
   'The discount must be less than 100%.' => '',
   'The discount must not be negative.' => '',
   'The dunning process started' => '',
   'The dunnings have been printed.' => '',
-  'The email address is missing.' => '',
   'The end date is the last day for which invoices will possibly be created.' => '',
+  'The execution schedule is invalid.' => '',
+  'The execution type is invalid.' => '',
+  'The existing record has been created from the link target to add.' => '',
   'The factor is missing in row %d.' => '',
   'The factor is missing.'      => '',
   'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => '',
   'The follow-up date is missing.' => '',
-  'The following Buchungsgruppen have already been created:' => '',
-  'The following Datasets need to be updated' => '',
+  'The following currencies have been used, but they are not defined:' => '',
   'The following drafts have been saved and can be loaded.' => '',
-  'The following old files whose settings have to be merged manually into the new configuration file "config/lx_office.conf" still exist:' => '',
+  'The following groups are valid for this client' => '',
+  'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => '',
+  'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => '',
   'The following transaction contains wrong taxes:' => '',
   'The following transaction contains wrong taxkeys:' => '',
-  'The following units are unknown.' => '',
-  'The following units exist already:' => '',
-  'The following users have been migrated into the authentication database:' => '',
-  'The following warnings occured during an upgrade to the document templates:' => '',
+  'The following transactions are concerned:' => '',
+  'The following users are a member of this group' => '',
+  'The following users will have access to this client' => '',
   'The formula needs the following syntax:<br>For regular article:<br>Variablename= Variable Unit;<br>Variablename2= Variable2 Unit2;<br>...<br>###<br>Variable + ( Variable2 / Variable )<br><b>Please be beware of the spaces in the formula</b><br>' => '',
   'The greetings have been saved.' => '',
-  'The group has been added.'   => '',
-  'The group has been deleted.' => '',
-  'The group has been saved.'   => '',
-  'The group memberships have been saved.' => '',
-  'The group name is missing.'  => '',
+  'The installation is currently locked.' => '',
+  'The installation is currently unlocked.' => '',
+  'The items are imported accoring do their number "X" regardless of the column order inside the file.' => '',
+  'The link target to add has been created from the existing record.' => '',
   'The list has been printed.'  => '',
+  'The login is missing.'       => '',
+  'The login is not unique.'    => '',
   'The long description is missing.' => '',
+  'The master templates where not found.' => '',
   'The name in row %d has already been used before.' => '',
   'The name is missing in row %d.' => '',
   'The name is missing.'        => '',
+  'The name is not unique.'     => '',
   'The name must only consist of letters, numbers and underscores and start with a letter.' => '',
   'The number of days for full payment' => '',
-  'The old file containing the user information is still present (&quot;#1&quot;). Do you want to migrate these users into the database? If not then you will not be able to log in with any of the users present in the old file.' => '',
   'The option field is empty.'  => '',
+  'The package name is invalid.' => '',
   'The parts for this delivery order have already been transferred in.' => '',
   'The parts for this delivery order have already been transferred out.' => '',
   'The parts have been removed.' => '',
@@ -1953,25 +2175,29 @@ $self->{texts} = {
   'The payment term has been saved.' => '',
   'The payment term is in use and cannot be deleted.' => '',
   'The payments have been posted.' => '',
-  'The pg_dump process could not be started.' => '',
-  'The pg_restore process could not be started.' => '',
   'The preferred one is to install packages provided by your operating system distribution (e.g. Debian or RPM packages).' => '',
+  'The printer could not be deleted.' => '',
+  'The printer has been created.' => '',
+  'The printer has been deleted.' => '',
+  'The printer has been saved.' => '',
   'The profile \'#1\' has been deleted.' => '',
   'The profile has been saved under the name \'#1\'.' => '',
-  'The program\'s exit code was #1 (&quot;0&quot; usually means that everything went OK).' => '',
-  'The project has been added.' => '',
+  'The project has been created.' => '',
+  'The project has been deleted.' => '',
   'The project has been saved.' => '',
-  'The restoration process has started. Here\'s the output of the &quot;pg_restore&quot; command:' => '',
-  'The restoration process is complete. Please review &quot;pg_restore&quot;\'s output to find out if the restoration was successful.' => '',
+  'The project is in use and cannot be deleted.' => '',
+  'The project number is already in use.' => '',
+  'The project number is missing.' => '',
   'The second reason is that kivitendo allowed the user to enter the tax amount manually regardless of the taxkey used.' => '',
   'The second way is to use Perl\'s CPAN module and let it download and install the module for you.' => '',
-  'The selected  PostgreSQL installation uses UTF-8 as its encoding. Therefore you have to configure Lx-Office to use UTF-8 as well.' => '',
   'The selected bank account does not exist anymore.' => '',
   'The selected bin does not exist.' => '',
   'The selected currency'       => '',
+  'The selected database is still configured for client "#1". If you delete the database that client will stop working until you re-configure it. Do you still want to delete the database?' => '',
   'The selected exports have been closed.' => '',
   'The selected warehouse does not exist.' => '',
   'The selected warehouse is empty, or no stocked items where found that match the filter settings.' => '',
+  'The session has expired. Please log in again.' => '',
   'The session is invalid or has expired.' => '',
   'The settings were saved, but the password was not changed.' => '',
   'The source warehouse does not contain any bins.' => '',
@@ -1979,14 +2205,29 @@ $self->{texts} = {
   'The subject is missing.'     => '',
   'The tables for user management and authentication do not exist. They will be created in the next step in the following database:' => '',
   'The tabulator character'     => '',
+  'The task server does not appear to be running.' => '',
+  'The task server is already running.' => '',
+  'The task server is not running at the moment but needed for this module' => '',
+  'The task server is not running.' => '',
+  'The task server was started successfully.' => '',
+  'The task server was stopped successfully.' => '',
   'The third way is to download the module from the above mentioned URL and to install the module manually following the installations instructions contained in the source archive.' => '',
+  '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.' => '',
   'The transaction is shown below in its current state.' => '',
+  'The type is missing.'        => '',
   'The unit has been saved.'    => '',
   'The unit in row %d has been deleted in the meantime.' => '',
   'The unit in row %d has been used in the meantime and cannot be changed anymore.' => '',
   'The units have been saved.'  => '',
-  'The user is a member in the following group(s):' => '',
-  'The user migration process is complete.' => '',
+  'The user can chose which client to connect to during login.' => '',
+  'The user could not be deleted.' => '',
+  'The user group could not be deleted.' => '',
+  'The user group has been created.' => '',
+  'The user group has been deleted.' => '',
+  'The user group has been saved.' => '',
+  'The user has been created.'  => '',
+  'The user has been deleted.'  => '',
+  'The user has been saved.'    => '',
   'The variable name must only consist of letters, numbers and underscores. It must begin with a letter. Example: send_christmas_present' => '',
   'The warehouse could not be deleted because it has already been used.' => '',
   'The warehouse does not contain any bins.' => '',
@@ -1994,18 +2235,23 @@ $self->{texts} = {
   'The wrong taxkeys for AP and AR transactions have been fixed.' => '',
   'The wrong taxkeys for inventory transactions for sales and purchase invoices have been fixed.' => '',
   'The wrong taxkeys have been fixed.' => '',
+  'Then go to the database administration and chose "create database".' => '',
   'There are #1 more open invoices for this customer with other currencies.' => '',
   'There are #1 more open invoices from this vendor with other currencies.' => '',
   'There are #1 unfinished follow-ups of which #2 are due.' => '',
+  'There are Bins defined in your Inventory.' => '',
+  'There are Bins defined in your master data.' => '',
   'There are bookings to the account 3803 after 01.01.2007. If you didn\'t change this account manually to 19% the bookings are probably incorrect.' => '',
+  'There are double partnumbers in your database.' => '',
   'There are entries in tax where taxkey is NULL.' => '',
-  'There are four tax zones.'   => '',
   'There are invalid taxnumbers in use.' => '',
+  'There are invalid transactions in your database.' => '',
   'There are no entries in the background job history.' => '',
   'There are no items in stock.' => '',
   'There are no items on your TODO list at the moment.' => '',
-  'There are still entries in the database for which no unit has been assigned.' => '',
+  'There are several options you can handle this problem, please select one:' => '',
   'There are still transfers not matching the qty of the delivery order. Stock operations can not be changed later. Do you really want to proceed?' => '',
+  'There are undefined currencies in your system.' => '',
   'There are usually three ways to install Perl modules.' => '',
   'There is already a taxkey 0 with tax rate not 0.' => '',
   'There is an inconsistancy in your database.' => '',
@@ -2014,49 +2260,59 @@ $self->{texts} = {
   'There is not enough available of \'#1\' at warehouse \'#2\', bin \'#3\', #4, #5, for the transfer of #6.' => '',
   'There is not enough available of \'#1\' at warehouse \'#2\', bin \'#3\', #4, for the transfer of #5.' => '',
   'There is not enough left of \'#1\' in bin \'#2\' for the removal of #3.' => '',
-  'There is nothing to do in this step.' => '',
+  'There was an error executing the background job.' => '',
+  'There was an error parsing the csv file: #1 in line #2.' => '',
+  'There you can let kivitendo create the basic tables for you, even in an already existing database.' => '',
+  'Therefore several settings that had to be made for each user in the past have been consolidated into the client configuration.' => '',
   'Therefore the definition of "kg" with the base unit "g" and a factor of 1000 is valid while defining "g" with a base unit of "kg" and a factor of "0.001" is not.' => '',
-  'Therefore there\'s no need to create the same article more than once if it is sold or bought in/from another tax zone.' => '',
-  'These units can be based on other units so that kivitendo can convert prices when the user switches from one unit to another.' => '',
   'These wrong entries cannot be fixed automatically.' => '',
   'This can be done with the following query:' => '',
-  'This corresponds to kivitendo\'s behavior prior to version 2.4.4.' => '',
   'This could have happened for two reasons:' => '',
   'This customer number is already in use.' => '',
-  'This group will be called &quot;Full Access&quot;.' => '',
-  'This installation uses an unknown chart of accounts (&quot;#1&quot;). This database upgrade cannot create standard buchungsgruppen automatically.' => '',
-  'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => '',
+  'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => '',
+  'This group is valid for the following clients' => '',
+  'This has been changed in this version, therefore please change the "old" bins to some real warehouse bins.' => '',
+  'This has been changed in this version.' => '',
+  'This is a very critical problem.' => '',
+  'This is the client to be selected by default on the login screen.' => '',
+  'This is the default bin for ignoring onhand' => '',
+  'This is the default bin for parts' => '',
   'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' => '',
   'This means that the user has created an AP transaction and chosen a taxkey for sales taxes, or that he has created an AR transaction and chosen a taxkey for input taxes.' => '',
   'This module can help you identify and correct such entries by analyzing the general ledger and presenting you likely solutions but also allowing you to fix problems yourself.' => '',
   'This option controls the inventory system.' => '',
   'This option controls the method used for profit determination.' => '',
   'This option controls the posting and calculation behavior for the accounting method.' => '',
+  'This partnumber is not unique. You should change it.' => '',
+  'This requires you to manually correct entries for which an automatic conversion failed and to check those for which it succeeded.' => '',
   'This transaction has to be split into several transactions manually.' => '',
   'This update will change the nature the onhand of goods is tracked.' => '',
-  'This upgrade script tries to map all existing parts in the database to the newly created Buchungsgruppen.' => '',
-  'This upgrade script tries to map all existing units in the database to the newly created units.' => '',
+  'This user is a member in the following groups' => '',
+  'This user will have access to the following clients' => '',
   'This vendor number is already in use.' => '',
+  'Three Options:'              => '',
+  'Time Format'                 => '',
+  'Time Tracking'               => '',
   'Time period for the analysis:' => '',
-  'Time Track'                  => '',
   'Timestamp'                   => '',
   'Title'                       => '',
   'To'                          => '',
   'To (email)'                  => '',
   'To (time)'                   => '',
   'To Date'                     => '',
-  'To add a user to a group edit a name, change the login name and save.  A new user with the same variables will then be saved under the new login name.' => '',
+  'To continue please change the taxkey 0 to another value.' => '',
+  'To user login'               => '',
   'Top'                         => '',
   'Top (CSS)'                   => '',
-  'Top (CSS) new'               => '',
   'Top (Javascript)'            => '',
   'Top 100'                     => '',
   'Top 100 hinzufuegen'         => '',
-  'Top Level'                   => '',
+  'Top Level Designation only'  => '',
   'Total'                       => '',
   'Total Fees'                  => '',
   'Total stock value'           => '',
   'Total sum'                   => '',
+  'Total weight'                => '',
   'Totals'                      => '',
   'Trade Discount'              => '',
   'Trans Id'                    => '',
@@ -2070,6 +2326,8 @@ $self->{texts} = {
   'Transaction has already been cancelled!' => '',
   'Transaction has been split on both the credit and the debit side' => '',
   'Transaction posted!'         => '',
+  'Transactions without account:' => '',
+  'Transactions without reference:' => '',
   'Transactions, AR transactions, AP transactions' => '',
   'Transdate'                   => '',
   'Transfer'                    => '',
@@ -2077,8 +2335,11 @@ $self->{texts} = {
   'Transfer To Stock'           => '',
   'Transfer from warehouse'     => '',
   'Transfer in'                 => '',
+  'Transfer in via default'     => '',
   'Transfer out'                => '',
+  'Transfer out via default'    => '',
   'Transfer qty'                => '',
+  'Transfer successful'         => '',
   'Translation'                 => '',
   'Trial Balance'               => '',
   'Trial balance between %s and %s' => '',
@@ -2088,6 +2349,7 @@ $self->{texts} = {
   'Type of Business'            => '',
   'Type of Customer'            => '',
   'Type of Vendor'              => '',
+  'Types of Business'           => '',
   'USTVA'                       => '',
   'USTVA 2004'                  => '',
   'USTVA 2005'                  => '',
@@ -2104,6 +2366,7 @@ $self->{texts} = {
   'Unbalanced Ledger'           => '',
   'Unchecked custom variables will not appear in orders and invoices.' => '',
   'Unfinished follow-ups'       => '',
+  'Unfortunately you have no warehouse defined.' => '',
   'Unit'                        => '',
   'Unit (if missing or empty default unit will be used)' => '',
   'Unit missing.'               => '',
@@ -2112,46 +2375,53 @@ $self->{texts} = {
   'Units that have already been used (e.g. for parts and services or in invoices or warehouse transactions) cannot be changed.' => '',
   'Unknown Category'            => '',
   'Unknown Link'                => '',
-  'Unknown chart of accounts'   => '',
   'Unknown dependency \'%s\'.'  => '',
   'Unknown problem type.'       => '',
   'Unlock System'               => '',
   'Until'                       => '',
   'Update'                      => '',
-  'Update Dataset'              => '',
   'Update Prices'               => '',
   'Update SKR04: new tax account 3804 (19%)' => '',
-  'Update complete'             => '',
   'Update prices'               => '',
   'Update prices of existing entries' => '',
-  'Update?'                     => '',
+  'Update properties of existing entries' => '',
   'Updated'                     => '',
+  'Updating existing entry in database' => '',
   'Updating prices of existing entry in database' => '',
+  'Updating the client fields in the database "#1" on host "#2:#3" failed.' => '',
   'Uploaded on #1, size #2 kB'  => '',
-  'Use As Template'             => '',
-  'Use Templates'               => '',
+  'Use As New'                  => '',
+  'Use WebDAV Repository'       => '',
+  'Use existing templates'      => '',
+  'Use master default bin for Default Transfer, if no default bin for the part is configured' => '',
   'User'                        => '',
   'User Config'                 => '',
   'User Groups'                 => '',
-  'User Login'                  => '',
-  'User data migration'         => '',
-  'User deleted!'               => '',
-  'User migration complete'     => '',
+  'User Preferences'            => '',
+  'User access'                 => '',
+  'User list'                   => '',
+  'User login'                  => '',
   'User name'                   => '',
-  'User saved!'                 => '',
   'Username'                    => '',
-  'Users in this group'         => '',
-  'Ust-IDNr'                    => '',
+  'Users that are a member in this group' => '',
+  'Users that have access to this client' => '',
+  'Users with access'           => '',
+  'Users with access to this client' => '',
+  'Users, Clients and User Groups' => '',
+  'VAT ID'                      => '',
+  'Valid'                       => '',
   'Valid from'                  => '',
   'Valid until'                 => '',
+  'Valid/Obsolete'              => '',
   'Value'                       => '',
   'Variable'                    => '',
   'Variable Description'        => '',
   'Variable Name'               => '',
   'Vendor'                      => '',
+  'Vendor (database ID)'        => '',
   'Vendor (name)'               => '',
   'Vendor Invoice'              => '',
-  'Vendor Invoices'             => '',
+  'Vendor Invoices & AP Transactions' => '',
   'Vendor Name'                 => '',
   'Vendor Number'               => '',
   'Vendor Order Number'         => '',
@@ -2160,13 +2430,16 @@ $self->{texts} = {
   'Vendor missing!'             => '',
   'Vendor not on file or locked!' => '',
   'Vendor not on file!'         => '',
+  'Vendor saved'                => '',
   'Vendor saved!'               => '',
   'Vendor type'                 => '',
   'Vendors'                     => '',
   'Verrechnungseinheit'         => '',
   'Version'                     => '',
-  'Version 2.4.0 introduces two new concepts: tax zones and Buchungsgruppen.' => '',
   'View SEPA export'            => '',
+  'View background job execution result' => '',
+  'View background job history' => '',
+  'View background jobs'        => '',
   'View warehouse content'      => '',
   'View/edit all employees sales documents' => '',
   'Von Konto: '                 => '',
@@ -2176,13 +2449,16 @@ $self->{texts} = {
   'Warehouse Migration'         => '',
   'Warehouse To'                => '',
   'Warehouse content'           => '',
+  'Warehouse correction'        => '',
   'Warehouse deleted.'          => '',
+  'Warehouse list'              => '',
   'Warehouse management'        => '',
   'Warehouse saved.'            => '',
   'Warehouses'                  => '',
   'Warning'                     => '',
-  'Warnings during template upgrade' => '',
+  'WebDAV'                      => '',
   'WebDAV link'                 => '',
+  'WebDAV save documents'       => '',
   'Webserver interface'         => '',
   'Weight'                      => '',
   'Weight unit'                 => '',
@@ -2190,13 +2466,14 @@ $self->{texts} = {
   'What type of item is this?'  => '',
   'Which is located at doc/kivitendo-Dokumentation.pdf. Click here: ' => '',
   'With Extension Of Time'      => '',
+  'With the introduction of clients each client gets its own WebDAV folder.' => '',
   'Workflow Delivery Order'     => '',
   'Workflow purchase_order'     => '',
   'Workflow request_quotation'  => '',
   'Workflow sales_order'        => '',
   'Workflow sales_quotation'    => '',
+  'Write bin to default bin in part?' => '',
   'Wrong Period'                => '',
-  'Wrong date format!'          => '',
   'Wrong tax keys recorded'     => '',
   'Wrong taxes recorded'        => '',
   'YYYY'                        => '',
@@ -2207,51 +2484,50 @@ $self->{texts} = {
   'Yes, included by default'    => '',
   'Yes/No (Checkbox)'           => '',
   'You are logged out!'         => '',
-  'You can also create new units now.' => '',
   'You can also delete this transaction and re-enter it manually.' => '',
+  'You can choose account categories for taxes. Depending on these categories taxes will be displayed for transfers in the general ledger or not.' => '',
   'You can correct this transaction by chosing the correct taxkeys from the drop down boxes and hitting the button "Fix transaction" afterwards.' => '',
-  'You can create a missing dataset by going back and chosing &quot;Create Dataset&quot;.' => '',
   'You can create warehouses and bins via the menu "System -> Warehouses".' => '',
   'You can declare different translations for singular and plural for each unit (e.g. &quot;day&quot; and &quot;days).' => '',
   'You can either create a new database or chose an existing database.' => '',
   'You can find information on the migration in the upgrade chapter of the documentation.' => '',
   'You can only delete datasets that are not in use.' => '',
+  'You can update existing contacts by providing the \'cp_id\' column with their database IDs. Otherwise: ' => '',
   'You can use the following strings in the long description and all translations. They will be replaced by their actual values by kivitendo before they\'re output.' => '',
   'You cannot adjust the price for pricegroup "#1" by a negative percentage.' => '',
   'You cannot continue before all required modules are installed.' => '',
-  'You cannot continue until all unknown units have been mapped to known ones.' => '',
   'You cannot create an invoice for delivery orders for different customers.' => '',
   'You cannot create an invoice for delivery orders from different vendors.' => '',
-  'You did not enter a name!'   => '',
   'You do not have the permissions to access this function.' => '',
   'You have entered or selected the following shipping address for this customer:' => '',
+  'You have never worked with currencies.' => '',
   'You have not added bank accounts yet.' => '',
   'You have not selected any delivery order.' => '',
   'You have not selected any export.' => '',
   'You have not selected any item.' => '',
   'You have selected none of the invoices.' => '',
-  'You have to chose a dimension unit and a service unit which will then be assigned to those entries.' => '',
-  'You have to chose which unit to save for each of them.' => '',
-  'You have to create at least one group, grant it access to kivitendo\'s functions and assign users to it.' => '',
-  'You have to create new Buchungsgruppen for all the combinations of inventory, income and expense accounts that have been used already.' => '',
   'You have to define a unit as a multiple of a smaller unit.' => '',
-  'You have to enter a company name in your user preferences (see the "Program" menu, "Preferences").' => '',
-  'You have to enter the SEPA creditor ID in your user preferences (see the "Program" menu, "Preferences").' => '',
+  'You have to enter a company name in the client configuration.' => '',
+  'You have to enter the SEPA creditor ID in the client configuration.' => '',
   'You have to fill in at least an account number, the bank code, the IBAN and the BIC.' => '',
+  'You have to grant users access to one or more clients.' => '',
   'You have to specify a department.' => '',
   'You have to specify an execution date for each antry.' => '',
   'You must chose a user.'      => '',
+  'You must enter a name for your new print templates.' => '',
+  'You must select existing print templates or create a new set.' => '',
   'You should create a backup of the database before proceeding because the backup might not be reversible.' => '',
-  'You will now be forwarded to the administration panel.' => '',
   'You\'re not editing a file.' => '',
   'You\'ve already chosen the following limitations:' => '',
-  'Your PostgreSQL installationen uses UTF-8 as its encoding. Therefore you have to configure Lx-Office to use UTF-8 as well.' => '',
+  'Your PostgreSQL installationen does not use Unicode as its encoding. This is not supported anymore.' => '',
   'Your TODO list'              => '',
   'Your account number'         => '',
   'Your bank'                   => '',
   'Your bank code'              => '',
   'Your browser does not currently support Javascript.' => '',
   'Your download does not exist anymore. Please re-run the DATEV export assistant.' => '',
+  'Your import is beig processed.' => '',
+  'ZM'                          => '',
   'Zeitpunkt'                   => '',
   'Zeitraum'                    => '',
   'Zero amount posting!'        => '',
@@ -2265,9 +2541,11 @@ $self->{texts} = {
   'action= not defined!'        => '',
   'active'                      => '',
   'all entries'                 => '',
+  'and'                         => '',
   'ap_aging_list'               => '',
   'ar_aging_list'               => '',
   'as at'                       => '',
+  'assembly'                    => '',
   'assembly_list'               => '',
   'averaged values, in invoice mode only useful when filtered by a part' => '',
   'back'                        => '',
@@ -2279,7 +2557,8 @@ $self->{texts} = {
   'bestbefore #1'               => '',
   'bin_list'                    => '',
   'bis'                         => '',
-  'button'                      => '',
+  'building data'               => '',
+  'building report'             => '',
   'cash'                        => '',
   'chargenumber #1'             => '',
   'chart_of_accounts'           => '',
@@ -2289,10 +2568,10 @@ $self->{texts} = {
   'close'                       => '',
   'closed'                      => '',
   'companylogo_subtitle'        => '',
-  'config/lx_office.conf: Key "DB_config" is missing.' => '',
-  'config/lx_office.conf: Key "authentication/ldap" is missing.' => '',
-  'config/lx_office.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".' => '',
-  'config/lx_office.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".' => '',
+  'config/kivitendo.conf: Key "DB_config" is missing.' => '',
+  'config/kivitendo.conf: Key "authentication/ldap" is missing.' => '',
+  'config/kivitendo.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".' => '',
+  'config/kivitendo.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".' => '',
   'contact_list'                => '',
   'continue'                    => '',
   'correction'                  => '',
@@ -2309,20 +2588,25 @@ $self->{texts} = {
   'done'                        => '',
   'down'                        => '',
   'dunning_list'                => '',
+  'eBayImporter'                => '',
   'eMail Send?'                 => '',
   'eMail?'                      => '',
   'ea'                          => '',
   'emailed to'                  => '',
   'empty'                       => '',
+  'every third month'           => '',
   'every time'                  => '',
   'executed'                    => '',
+  'failed'                      => '',
   'female'                      => '',
   'follow_up_list'              => '',
   'for'                         => '',
   'for Period'                  => '',
+  'for date'                    => '',
   'found'                       => '',
   'from (time)'                 => '',
   'general_ledger_list'         => '',
+  'h'                           => '',
   'history'                     => '',
   'history search engine'       => '',
   'inactive'                    => '',
@@ -2333,18 +2617,21 @@ $self->{texts} = {
   'kivitendo'                   => '',
   'kivitendo Homepage'          => '',
   'kivitendo can fix these problems automatically.' => '',
-  'kivitendo has been switched to group-based access restrictions.' => '',
+  'kivitendo has been extended to handle multiple clients within a single installation.' => '',
   'kivitendo has found one or more problems in the general ledger.' => '',
   'kivitendo is about to update the database [ #1 ].' => '',
   'kivitendo is now able to manage warehouses instead of just tracking the amount of goods in your system.' => '',
-  'Knowledge'                   => '',
+  'kivitendo needs to update the authentication database before you can proceed.' => '',
+  'kivitendo v#1'               => '',
+  'kivitendo v#1 administration' => '',
+  'kivitendo website (external)' => '',
+  'kivitendo will then update the database automatically.' => '',
   'lead deleted!'               => '',
   'lead saved!'                 => '',
   'list'                        => '',
   'list_of_payments'            => '',
   'list_of_receipts'            => '',
   'list_of_transactions'        => '',
-  'loading'                     => '',
   'logout'                      => '',
   'male'                        => '',
   'mark as paid'                => '',
@@ -2361,17 +2648,22 @@ $self->{texts} = {
   'not configured'              => '',
   'not delivered'               => '',
   'not executed'                => '',
-  'not logged in'               => '',
+  'not running'                 => '',
+  'not set'                     => '',
+  'not shipped'                 => '',
   'not transferred in yet'      => '',
   'not transferred out yet'     => '',
   'not yet executed'            => '',
   'number'                      => '',
   'oe.pl::search called with unknown type' => '',
   'on the same day'             => '',
+  'one-time execution'          => '',
   'only OB Transactions'        => '',
   'open'                        => '',
   'order'                       => '',
   'our vendor number at customer' => '',
+  'parsing csv'                 => '',
+  'part'                        => '',
   'part_list'                   => '',
   'percental'                   => '',
   'periodic'                    => '',
@@ -2386,21 +2678,21 @@ $self->{texts} = {
   'prev'                        => '',
   'print'                       => '',
   'proforma'                    => '',
-  'project_list'                => '',
   'purchase_delivery_order_list' => '',
   'purchase_order'              => '',
   'purchase_order_list'         => '',
   'quarter'                     => '',
-  'quarterly'                   => '',
   'quotation_list'              => '',
   'release_material'            => '',
   'reorder item'                => '',
+  'repeated execution'          => '',
   'report_generator_dispatch_to is not defined.' => '',
   'report_generator_nextsub is not defined.' => '',
   'request_quotation'           => '',
   'reset'                       => '',
   'return_material'             => '',
   'rfq_list'                    => '',
+  'running'                     => '',
   'sales tax identification number' => '',
   'sales_delivery_order_list'   => '',
   'sales_order'                 => '',
@@ -2408,19 +2700,25 @@ $self->{texts} = {
   'sales_quotation'             => '',
   'saved'                       => '',
   'saved!'                      => '',
+  'saving data'                 => '',
   'sent'                        => '',
   'sent to printer'             => '',
+  'service'                     => '',
   'service_list'                => '',
   'shipped'                     => '',
   'singular first char'         => '',
   'soldtotal'                   => '',
   'stock'                       => '',
   'submit'                      => '',
+  'succeeded'                   => '',
   'tax_chartaccno'              => '',
   'tax_percent'                 => '',
   'tax_rate'                    => '',
   'tax_taxdescription'          => '',
   'tax_taxkey'                  => '',
+  'taxincluded checked'         => '',
+  'taxkey 0 is already allocated.' => '',
+  'taxkey 0 with taxrate 0 was created.' => '',
   'taxnumber'                   => '',
   'terminated'                  => '',
   'to (date)'                   => '',
@@ -2429,8 +2727,10 @@ $self->{texts} = {
   'transferred in'              => '',
   'transferred out'             => '',
   'trial_balance'               => '',
+  'unconfigured'                => '',
   'up'                          => '',
   'use program settings'        => '',
+  'use user config'             => '',
   'used'                        => '',
   'valid from'                  => '',
   'vendor'                      => '',
@@ -2441,6 +2741,7 @@ $self->{texts} = {
   'wrongformat'                 => '',
   'yearly'                      => '',
   'yes'                         => '',
+  'you can find professional help.' => '',
 };
 
 1;
index b0bff6139632df560f821c53775114edff50ca9b..b49a851a54484fa8d256ede43c428fd230a736b1 100644 (file)
@@ -597,6 +597,10 @@ action=edit_sepa_strings
 module=controller.pl
 action=PaymentTerm/list
 
+[System--Delivery Terms]
+module=controller.pl
+action=DeliveryTerm/list
+
 [System--Manage Custom Variables]
 module=controller.pl
 action=CustomVariableConfig/list
@@ -636,6 +640,11 @@ module=controller.pl
 action=CsvImport/new
 profile.type=projects
 
+[System--Import CSV--Orders]
+module=controller.pl
+action=CsvImport/new
+profile.type=orders
+
 [System--Templates]
 ACCESS=admin
 module=menu.pl
index 113344d45ac525dd8c2e4e3c3eb0b7235066e0ab..81bf5d3e431ce4dfb5f536c86d41dcc2fac9a37f 100644 (file)
@@ -596,7 +596,7 @@ sub table {
         if ($remaining_header_rows) {
           $remaining_header_rows--;
         } else {
-          $row_cnt++;
+          $row_cnt++ unless $do_leftovers;
         }
       }# End of while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg)
 
index 09f49611c1a002190e2c8a423eee4626ba7e3ace..bb246f86f82b9a79dd62c465ef0a36434e1b07f5 100755 (executable)
@@ -53,6 +53,7 @@ our %foreign_key_name_map = (
   orderitems           => { parts => 'part', trans => 'order', },
   delivery_order_items => { parts => 'part' },
   invoice              => { parts => 'part' },
+  follow_ups           => { 'employee_obj' => 'created_for' },
 
   periodic_invoices_configs => { oe => 'order' },
 );
diff --git a/sql/Pg-upgrade2/csv_import_reports_add_numheaders.sql b/sql/Pg-upgrade2/csv_import_reports_add_numheaders.sql
new file mode 100644 (file)
index 0000000..b68b668
--- /dev/null
@@ -0,0 +1,8 @@
+-- @tag: csv_import_reports_add_numheaders
+-- @description: Anzahl der Header-Zeilen in Csv Import Report speichern
+-- @depends: csv_import_report_cache
+-- @encoding: utf-8
+
+ALTER TABLE csv_import_reports ADD COLUMN numheaders INTEGER;
+UPDATE csv_import_reports SET numheaders = 1;
+ALTER TABLE csv_import_reports ALTER COLUMN numheaders SET NOT NULL;
diff --git a/sql/Pg-upgrade2/delivery_terms.sql b/sql/Pg-upgrade2/delivery_terms.sql
new file mode 100644 (file)
index 0000000..fa7400c
--- /dev/null
@@ -0,0 +1,39 @@
+-- @tag: delivery_terms
+-- @description: Neue Tabelle und Spalten für Lieferbedingungen
+-- @depends: release_3_0_0
+-- @encoding: utf-8
+
+CREATE TABLE delivery_terms (
+       id                        integer        NOT NULL DEFAULT nextval('id'),
+       description               text,
+       description_long          text,
+       sortkey                   integer        NOT NULL,
+       itime                     timestamp      DEFAULT now(),
+       mtime                     timestamp,
+
+       PRIMARY KEY (id)
+);
+
+CREATE TRIGGER mtime_delivery_terms
+    BEFORE UPDATE ON delivery_terms
+    FOR EACH ROW
+    EXECUTE PROCEDURE set_mtime();
+
+
+ALTER TABLE oe                ADD COLUMN delivery_term_id integer;
+ALTER TABLE oe                ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE delivery_orders   ADD COLUMN delivery_term_id integer;
+ALTER TABLE delivery_orders   ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE ar                ADD COLUMN delivery_term_id integer;
+ALTER TABLE ar                ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE ap                ADD COLUMN delivery_term_id integer;
+ALTER TABLE ap                ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE customer          ADD COLUMN delivery_term_id integer;
+ALTER TABLE customer          ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
+
+ALTER TABLE vendor            ADD COLUMN delivery_term_id integer;
+ALTER TABLE vendor            ADD FOREIGN KEY (delivery_term_id) REFERENCES delivery_terms(id);
diff --git a/sql/Pg-upgrade2/gl_add_employee_foreign_key.sql b/sql/Pg-upgrade2/gl_add_employee_foreign_key.sql
new file mode 100644 (file)
index 0000000..7d96a4d
--- /dev/null
@@ -0,0 +1,5 @@
+-- @tag: gl_add_employee_foreign_key
+-- @description: Dialogbuchungen mit Bearbeiter verknüpfen
+-- @depends: release_3_0_0
+-- @ignore: 0
+ALTER TABLE gl  ADD FOREIGN KEY (employee_id) REFERENCES employee(id);
index 088ed68ab3b447339e6c9a368c9903bb1d4d8197..809dfb11a10763a09b738a335bb8b2d0ec86e4b6 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 47;
+use Test::More tests => 71;
 
 use lib 't';
 use utf8;
@@ -11,9 +11,9 @@ use_ok 'SL::Helper::Csv';
 Support::TestSetup::login();
 
 my $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee\n",
-  header => [ 'description' ],
-  class  => 'SL::DB::Part',
+  file    => \"Kaffee\n",       # " # make emacs happy
+  header  => [ 'description' ],
+  profile => [{ class  => 'SL::DB::Part', }],
 );
 
 isa_ok $csv->_csv, 'Text::CSV_XS';
@@ -28,10 +28,10 @@ $::myconfig{numberformat} = '1.000,00';
 $::myconfig{dateformat} = 'dd.mm.yyyy';
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee;0.12;12,2;1,5234\n",
-  header => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
-  profile => { listprice => 'listprice_as_number' },
-  class  => 'SL::DB::Part',
+  file    => \"Kaffee;0.12;12,2;1,5234\n",            # " # make emacs happy
+  header  => [ 'description', 'sellprice', 'lastcost_as_number', 'listprice' ],
+  profile => [{profile => { listprice => 'listprice_as_number' },
+               class   => 'SL::DB::Part',}],
 );
 $csv->parse;
 
@@ -49,8 +49,8 @@ Kaffee,0.12,'12,2','1,5234'
 EOL
   sep_char => ',',
   quote_char => "'",
-  profile => { listprice => 'listprice_as_number' },
-  class  => 'SL::DB::Part',
+  profile => [{profile => { listprice => 'listprice_as_number' },
+               class   => 'SL::DB::Part',}]
 );
 $csv->parse;
 is scalar @{ $csv->get_objects }, 1, 'auto header works';
@@ -64,7 +64,7 @@ $csv = SL::Helper::Csv->new(
 ;;description;sellprice;lastcost_as_number;
 #####;Puppy;Kaffee;0.12;12,2;1,5234
 EOL
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is scalar @{ $csv->get_objects }, 1, 'bozo header doesn\'t blow things up';
@@ -77,7 +77,7 @@ description;partnumber;sellprice;lastcost_as_number;
 Kaffee;;0.12;12,2;1,5234
 Beer;1123245;0.12;12,2;1,5234
 EOL
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is scalar @{ $csv->get_objects }, 2, 'multiple objects work';
@@ -93,7 +93,7 @@ Kaffee;;0.12;1,221.52
 Beer;1123245;0.12;1.5234
 EOL
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is $csv->get_objects->[0]->lastcost, '1221.52', 'formatnumber';
@@ -106,8 +106,9 @@ $csv = SL::Helper::Csv->new(
 Kaffee;;0.12;1,221.52
 Beer;1123245;0.12;1.5234
 EOL
+# " # make emacs happy
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 is $csv->parse, undef, 'broken csv header won\'t get parsed';
 
@@ -119,8 +120,9 @@ description;partnumber;sellprice;lastcost_as_number;
 "Kaf"fee";;0.12;1,221.52
 Beer;1123245;0.12;1.5234
 EOL
+# " # make emacs happy
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 is $csv->parse, undef, 'broken csv content won\'t get parsed';
 is_deeply $csv->errors, [ '"Kaf"fee";;0.12;1,221.52'."\n", 2023, 'EIQ - QUO character not allowed', 5, 2 ], 'error';
@@ -136,7 +138,7 @@ Beer;1123245;0.12;1.5234;nein kein wieder
 EOL
   numberformat => '1,000.00',
   ignore_unknown_columns => 1,
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is $csv->get_objects->[0]->lastcost, '1221.52', 'ignore_unkown_columns works';
@@ -150,10 +152,10 @@ Kaffee;;0.12;1,221.52;Standard 7%
 Beer;1123245;0.12;1.5234;16 %
 EOL
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
-  profile => {
-    buchungsgruppe => "buchungsgruppen.description",
-  }
+  profile => [{
+    profile => {buchungsgruppe => "buchungsgruppen.description"},
+    class  => 'SL::DB::Part',
+  }]
 );
 $csv->parse;
 isa_ok $csv->get_objects->[0]->buchungsgruppe, 'SL::DB::Buchungsgruppe', 'deep dispatch auto vivify works';
@@ -169,11 +171,13 @@ description;partnumber;sellprice;lastcost_as_number;make_1;model_1;
 Beer;1123245;0.12;1.5234;
 EOL
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
-  profile => {
-    make_1 => "makemodels.0.make",
-    model_1 => "makemodels.0.model",
-  }
+  profile => [{
+    profile => {
+      make_1 => "makemodels.0.make",
+      model_1 => "makemodels.0.model",
+    },
+    class  => 'SL::DB::Part',
+  }],
 );
 $csv->parse;
 my @mm = $csv->get_objects->[0]->makemodel;
@@ -189,13 +193,15 @@ description;partnumber;sellprice;lastcost_as_number;make_1;model_1;make_2;model_
  Kaffee;;0.12;1,221.52;213;Chair 0815;523;Table 15
 EOL
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
-  profile => {
-    make_1 => "makemodels.0.make",
-    model_1 => "makemodels.0.model",
-    make_2 => "makemodels.1.make",
-    model_2 => "makemodels.1.model",
-  }
+  profile => [{
+    profile => {
+      make_1 => "makemodels.0.make",
+      model_1 => "makemodels.0.model",
+      make_2 => "makemodels.1.make",
+      model_2 => "makemodels.1.model",
+    },
+    class  => 'SL::DB::Part',
+  }]
 );
 $csv->parse;
 
@@ -215,10 +221,10 @@ $csv = SL::Helper::Csv->new(
 description;partnumber;sellprice;lastcost_as_number;buchungsgruppe;
 EOL
   numberformat => '1,000.00',
-  class  => 'SL::DB::Part',
-  profile => {
-    buchungsgruppe => "buchungsgruppen.1.description",
-  }
+  profile => [{
+    profile => {buchungsgruppe => "buchungsgruppen.1.description"},
+    class  => 'SL::DB::Part',
+  }]
 );
 is $csv->parse, undef, 'wrong profile gets rejected';
 is_deeply $csv->errors, [ 'buchungsgruppen.1.description', undef, "Profile path error. Indexed relationship is not OneToMany around here: 'buchungsgruppen.1'", undef ,0 ], 'error indicates wrong header';
@@ -235,10 +241,10 @@ EOL
   numberformat => '1,000.00',
   ignore_unknown_columns => 1,
   strict_profile => 1,
-  class  => 'SL::DB::Part',
-  profile => {
-    lastcost => 'lastcost_as_number',
-  }
+  profile => [{
+    profile => {lastcost => 'lastcost_as_number'},
+    class  => 'SL::DB::Part',
+  }]
 );
 $csv->parse;
 is $csv->get_objects->[0]->lastcost, '1221.52', 'strict_profile with ignore';
@@ -254,10 +260,10 @@ Beer;1123245;0.12;1.5234;nein kein wieder
 EOL
   numberformat => '1,000.00',
   strict_profile => 1,
-  class  => 'SL::DB::Part',
-  profile => {
-    lastcost => 'lastcost_as_number',
-  }
+  profile => [{
+    profile => {lastcost => 'lastcost_as_number'},
+    class  => 'SL::DB::Part',
+  }]
 );
 $csv->parse;
 
@@ -266,9 +272,9 @@ is_deeply( ($csv->errors)[0], [ 'description', undef, 'header field \'descriptio
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee",
-  header => [ 'description' ],
-  class  => 'SL::DB::Part',
+  file   => \"Kaffee",       # " # make emacs happy
+  header =>  [ 'description' ],
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'eol bug at the end of files';
@@ -276,10 +282,12 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'eol bug at the end o
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Description\nKaffee",
-  class  => 'SL::DB::Part',
+  file   => \"Description\nKaffee",        # " # make emacs happy
   case_insensitive_header => 1,
-  profile => { description => 'description' },
+  profile => [{
+    profile => { description => 'description' },
+    class  => 'SL::DB::Part'
+  }],
 );
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header from csv works';
@@ -287,11 +295,13 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive hea
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee",
-  header => [ 'Description' ],
-  class  => 'SL::DB::Part',
+  file   => \"Kaffee",          # " # make emacs happy
+  header =>  [ 'Description' ],
   case_insensitive_header => 1,
-  profile => { description => 'description' },
+  profile => [{
+    profile => { description => 'description' },
+    class  => 'SL::DB::Part'
+  }],
 );
 $csv->parse;
 is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive header as param works';
@@ -299,8 +309,8 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'case insensitive hea
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"\x{EF}\x{BB}\x{BF}description\nKaffee",
-  class  => 'SL::DB::Part',
+  file   => \"\x{EF}\x{BB}\x{BF}description\nKaffee",           # " # make emacs happy
+  profile => [{class  => 'SL::DB::Part'}],
   encoding => 'utf8',
 );
 $csv->parse;
@@ -309,9 +319,9 @@ is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'utf8 BOM works (bug
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee",
+  file   => \"Kaffee",            # " # make emacs happy
   header => [ 'Description' ],
-  class  => 'SL::DB::Part',
+  profile => [{class  => 'SL::DB::Part'}],
 );
 $csv->parse;
 is_deeply $csv->get_data, undef, 'case insensitive header without flag ignores';
@@ -319,10 +329,12 @@ is_deeply $csv->get_data, undef, 'case insensitive header without flag ignores';
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee",
+  file   => \"Kaffee",            # " # make emacs happy
   header => [ 'foo' ],
-  class  => 'SL::DB::Part',
-  profile => { foo => '' },
+  profile => [{
+    profile => { foo => '' },
+    class  => 'SL::DB::Part',
+  }],
 );
 $csv->parse;
 
@@ -332,11 +344,13 @@ ok $csv->get_objects->[0], 'empty path gets ignored in object creation';
 #####
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Kaffee",
+  file   => \"Kaffee",            # " # make emacs happy
   header => [ 'foo' ],
-  class  => 'SL::DB::Part',
   strict_profile => 1,
-  profile => { foo => '' },
+  profile => [{
+    profile => { foo => '' },
+    class  => 'SL::DB::Part',
+  }],
 );
 $csv->parse;
 
@@ -344,16 +358,329 @@ is_deeply $csv->get_data, [ { foo => 'Kaffee' } ], 'empty path still gets parsed
 ok $csv->get_objects->[0], 'empty path gets ignored in object creation (strict profile)';
 
 $csv = SL::Helper::Csv->new(
-  file   => \"Phil",
+  file   => \"Phil",            # " # make emacs happy
   header => [ 'CVAR_grOUnDHog' ],
-  class  => 'SL::DB::Part',
   strict_profile => 1,
   case_insensitive_header => 1,
-  profile => { cvar_Groundhog => '' },
+  profile => [{
+    profile => { cvar_Groundhog => '' },
+    class  => 'SL::DB::Part',
+  }],
+
 );
 $csv->parse;
 
 is_deeply $csv->get_data, [ { cvar_Groundhog => 'Phil' } ], 'using empty path to get cvars working';
 ok $csv->get_objects->[0], '...and not destorying the objects';
 
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"description\nKaffee",            # " # make emacs happy
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { description => 'Kaffee' } ], 'without profile and class works';
+
+#####
+$csv = SL::Helper::Csv->new(
+  file    => \"Kaffee;1,50\nSchoke;0,89\n",
+  header  => [
+    [ 'datatype', 'description', 'sellprice' ],
+  ],
+  profile => [
+    { profile   => { sellprice => 'sellprice_as_number' },
+      class     => 'SL::DB::Part',}
+  ],
+);
+
+ok $csv->_check_multiplexed, 'multiplex check works on not-multiplexed data';
+ok !$csv->is_multiplexed, 'not-multiplexed data is recognized';
+
+#####
+$csv = SL::Helper::Csv->new(
+  file    => \"P;Kaffee;1,50\nC;Meier\n",
+  header  => [
+    [ 'datatype', 'description', 'listprice' ],
+    [ 'datatype', 'name' ],
+  ],
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P' },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+);
+
+ok $csv->_check_multiplexed, 'multiplex check works on multiplexed data';
+ok $csv->is_multiplexed, 'multiplexed data is recognized';
+
+#####
+$csv = SL::Helper::Csv->new(
+  file    => \"P;Kaffee;1,50\nC;Meier\n",
+  header  => [
+    [ 'datatype', 'description', 'listprice' ],
+    [ 'datatype', 'name' ],
+  ],
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      class     => 'SL::DB::Part', },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing row_ident';
+
+#####
+$csv = SL::Helper::Csv->new(
+  file    => \"P;Kaffee;1,50\nC;Meier\n",
+  header  => [
+    [ 'datatype', 'description', 'listprice' ],
+    [ 'datatype', 'name' ],
+  ],
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      row_ident => 'P' },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing class';
+
+#####
+$csv = SL::Helper::Csv->new(
+  file    => \"P;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
+  header  => [
+    [ 'datatype', 'description', 'listprice' ],
+  ],
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P' },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+);
+
+ok !$csv->_check_multiplexed, 'multiplex check works on multiplexed data and detects missing header';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file    => \"P;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
+  header  => [
+    [ 'datatype', 'description', 'listprice' ],
+    [ 'datatype', 'name' ],
+  ],
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P' },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+  ignore_unknown_columns => 1,
+);
+
+$csv->parse;
+is_deeply $csv->get_data,
+    [ { datatype => 'P', description => 'Kaffee', listprice => '1,50' }, { datatype => 'C', name => 'Meier' } ],
+    'multiplex: simple case works';
+is scalar @{ $csv->get_objects }, 2, 'multiplex: multiple objects work';
+is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: first object';
+is $csv->get_objects->[1]->name,        'Meier',  'multiplex: second object';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file    => \"datatype;description;listprice\ndatatype;name\nP;Kaffee;1,50\nC;Meier\n",  # " # make emacs happy
+  profile => [
+    { profile   => { listprice => 'listprice_as_number' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P' },
+    { class  => 'SL::DB::Customer',
+      row_ident => 'C' }
+  ],
+  ignore_unknown_columns => 1,
+);
+
+$csv->parse;
+is scalar @{ $csv->get_objects }, 2, 'multiplex: auto header works';
+is $csv->get_objects->[0]->description, 'Kaffee', 'multiplex: auto header first object';
+is $csv->get_objects->[1]->name,        'Meier',  'multiplex: auto header second object';
+
+######
+
+$csv = SL::Helper::Csv->new(
+  file   => \<<EOL,
+datatype;description
+"datatype;name
+P;Kaffee
+C;Meier
+P;Beer
+EOL
+# " # make emacs happy
+  profile => [
+              {class  => 'SL::DB::Part',     row_ident => 'P'},
+              {class  => 'SL::DB::Customer', row_ident => 'C'},
+             ],
+  ignore_unknown_columns => 1,
+);
+is $csv->parse, undef, 'multiplex: broken csv header won\'t get parsed';
+
+######
+
+$csv = SL::Helper::Csv->new(
+  file   => \<<EOL,
+datatype;description
+P;Kaffee
+C;Meier
+P;Beer
+EOL
+# " # make emacs happy
+  profile => [
+              {class  => 'SL::DB::Part',     row_ident => 'P'},
+              {class  => 'SL::DB::Customer', row_ident => 'C'},
+             ],
+  header  => [ [], ['name'] ],
+  ignore_unknown_columns => 1,
+);
+ok !$csv->_check_multiplexed, 'multiplex check detects empty header';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"Datatype;Description\nDatatype;Name\nP;Kaffee\nC;Meier",        # " # make emacs happy
+  case_insensitive_header => 1,
+  ignore_unknown_columns => 1,
+  profile => [
+    {
+      profile   => { datatype => 'datatype', description => 'description' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P'
+    },
+    {
+      profile   => { datatype => 'datatype', name => 'name' },
+      class     => 'SL::DB::Customer',
+      row_ident => 'C'
+    }
+  ],
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
+                            { datatype => 'C', name => 'Meier'} ],
+                          'multiplex: case insensitive header from csv works';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"P;Kaffee\nC;Meier",          # " # make emacs happy
+  header =>  [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
+  case_insensitive_header => 1,
+  ignore_unknown_columns => 1,
+  profile => [
+    {
+      profile   => { datatype => 'datatype', description => 'description' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P'
+    },
+    {
+      profile => { datatype => 'datatype', name => 'name' },
+      class  => 'SL::DB::Customer',
+      row_ident => 'C'
+    }
+  ],
+);
+$csv->parse;
+is_deeply $csv->get_data, [ { datatype => 'P', description => 'Kaffee' },
+                            { datatype => 'C', name => 'Meier' } ],
+                          'multiplex: case insensitive header as param works';
+
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \"P;Kaffee\nC;Meier",          # " # make emacs happy
+  header =>  [[ 'Datatype', 'Description' ], [ 'Datatype', 'Name']],
+  profile => [
+    {
+      profile   => { datatype => 'datatype', description => 'description' },
+      class     => 'SL::DB::Part',
+      row_ident => 'P'
+    },
+    {
+      profile => { datatype => 'datatype', name => 'name' },
+      class  => 'SL::DB::Customer',
+      row_ident => 'C'
+    }
+  ],
+);
+$csv->parse;
+is_deeply $csv->get_data, undef, 'multiplex: case insensitive header without flag ignores';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \<<EOL,
+P;Kaffee;lecker
+C;Meier;froh
+EOL
+# " # make emacs happy
+  header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
+  profile => [{
+    profile   => { datatype => '', Afoo => '', Abar => '' },
+    class     => 'SL::DB::Part',
+    row_ident => 'P'
+  },
+  {
+    profile   => { datatype => '', Bfoo => '', Bbar => '' },
+    class     => 'SL::DB::Customer',
+    row_ident => 'C'
+  }],
+);
+$csv->parse;
+
+is_deeply $csv->get_data,
+    [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
+    'multiplex: empty path still gets parsed into data';
+ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation';
+
+#####
+
+$csv = SL::Helper::Csv->new(
+  file   => \<<EOL,
+P;Kaffee;lecker
+C;Meier;froh
+EOL
+# " # make emacs happy
+  header => [[ 'datatype', 'Afoo', 'Abar' ], [ 'datatype', 'Bfoo', 'Bbar']],
+  strict_profile => 1,
+  profile => [{
+    profile   => { datatype => '', Afoo => '', Abar => '' },
+    class     => 'SL::DB::Part',
+    row_ident => 'P'
+  },
+  {
+    profile   => { datatype => '', Bfoo => '', Bbar => '' },
+    class     => 'SL::DB::Customer',
+    row_ident => 'C'
+  }],
+);
+$csv->parse;
+
+is_deeply $csv->get_data,
+    [ { datatype => 'P', Afoo => 'Kaffee', Abar => 'lecker' }, { datatype => 'C', Bfoo => 'Meier', Bbar => 'froh' } ],
+    'multiplex: empty path still gets parsed into data (strict profile)';
+ok $csv->get_objects->[0], 'multiplex: empty path gets ignored in object creation (strict profile)';
+
+#####
+
+
 # vim: ft=perl
+# set emacs to perl mode
+# Local Variables:
+# mode: perl
+# End:
+
index 94270209393b8c2932cb35d09d171d7e80618a0b..75bee25b483aab7388c39ebc84a594b5b5f09945 100644 (file)
@@ -40,6 +40,7 @@
 \newcommand{\weiteraufnaechsterseite} {weiter auf der nächsten Seite ...}
 
 \newcommand{\zahlung} {Zahlungsbedingungen:}
+\newcommand{\lieferung} {Lieferbedingungen:}
 \newcommand{\textTelefon} {Tel.:}
 \newcommand{\textFax} {Fax:}
 
index efed0a7d8f2ead1fd218c6a86bafeaa54dafb0b6..2812389610b0f89c58e005ef2de2d12cc5c35841 100644 (file)
@@ -39,6 +39,7 @@
 \newcommand{\weiteraufnaechsterseite} {to be continued on next page  ..}
 
 \newcommand{\zahlung} {Payment terms:}
+\newcommand{\lieferung} {Delivery terms:}
 \newcommand{\textTelefon} {Tel.:}
 \newcommand{\textFax} {Fax:}
 
index 097f90da5d9104fe6515786dad356697d5ce0bd4..9959c452f53b86f3ce67f746fb750035362678c2 100644 (file)
   \zahlung ~<%payment_terms%>\\
 <%end payment_terms%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 <%if ustid%>\ihreustid ~<%ustid%>.\\<%end if%>
 
 \ifthenelse{\equal{<%taxzone_id%>}{1}}
index 5f4a674e4cbca0aaef724e95df93dc8f832822e1..09833b88e7b91b37856d1752db00909855f9a942 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 \end{document}
 
index e1a737ab25b00c53ddad882826a0a3d0d5b989c8..bd820eed677a66f3f9dc139c40aa8fc3704392f8 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 \gruesse \\ \\ \\
   <%employee_name%>
 
index ceb554b5cd3af195ac46642da76960e0a19d804a..5e0268272bf357592f49c87227581c3879f3e5e6 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 <%if reqdate%>
 \anfrageBenoetigtBis~<%reqdate%>.
 <%end if%>
index 690fd137f1e46e7fcc22c31b3b76f69a40c8cb84..2f18eef274aa442ad53f68ba31bbd591057838c2 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 \end{document}
 
index 0e15a1aae4386c4e86ec069b977be72bc5836542..b86f1594d6cf938337a020b74bfce8aa30623e3c 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 <%if reqdate%>
 \lieferungErfolgtAm ~<%reqdate%>. \\
 <%end if%>
index 277eb076192f36883a106313a9ffe4e60e5a4e2b..8ef6b5ba7b984fa068b3e7d13cee72fc50aeb5f8 100644 (file)
         \vspace{5mm}
 <%end if%>
 
+<%if delivery_term%>
+  \lieferung ~<%delivery_term.description_long%>\\
+<%end delivery_term%>
+
 \angebotdanke\\
 <%if reqdate%>
 \angebotgueltig~<%reqdate%>.
index e0555d33cd67848a6aad33c403b780547fcadb96..e2c6e672f4ad50f84c0ad3bdfb4cd62623716643 100644 (file)
 
   <tr>
    <td align="right">[% 'Discount' | $T8 %]</td>
-   <td>[% HTML.escape(discount) %]%</td>
+   <td>[% HTML.escape(discount_as_percent) %]%</td>
   </tr>
 
   [% IF is_customer %]
    <td>[% HTML.escape(payment_terms) %]</td>
   </tr>
 
+  <tr>
+   <td align="right">[% 'Delivery Terms' | $T8 %]</td>
+   <td>[% HTML.escape(delivery_terms) %]</td>
+  </tr>
+
   <tr>
    <td align="right">[% 'Tax Number' | $T8 %]</td>
    <td>[% HTML.escape(taxnumber) %]</td>
diff --git a/templates/webpages/csv_import/_form_orders.html b/templates/webpages/csv_import/_form_orders.html
new file mode 100644 (file)
index 0000000..e02b930
--- /dev/null
@@ -0,0 +1,16 @@
+[% USE LxERP %]
+[% USE L %]
+<tr>
+ <th align="right">[%- LxERP.t8('Order/Item row name') %]:</th>
+ <td colspan="10">
+  [% L.input_tag('settings.order_column', SELF.profile.get('order_column'), size => "10") %]
+  [% L.input_tag('settings.item_column',  SELF.profile.get('item_column'),  size => "10") %]
+ </td>
+</tr>
+
+<tr>
+ <th align="right">[%- LxERP.t8('Maximal amount difference') %]:</th>
+ <td colspan="10">
+  [% L.input_tag('settings.max_amount_diff', LxERP.format_amount(SELF.profile.get('max_amount_diff')), size => "5") %]
+ </td>
+</tr>
index 9fd30ac1d953bc09272b571b805ff64c16215239..5b189ec77a013bbcd7e09d72436600e15e5054bf 100644 (file)
   <div class="help_toggle" style="display:none">
    <p><a href="#" onClick="javascript:$('.help_toggle').toggle()">[% LxERP.t8("Hide help text") %]</a></p>
 
-   <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 %]
-   </table>
+   [%- IF SELF.worker.is_multiplexed %]
+     <table>
+       <tr class="listheading">
+         [%- FOREACH p = SELF.worker.profile %]
+           <th>[%- p.row_ident %]</th>
+         [%- END %]
+       </tr>
+       <tr class="listrow[% loop.count % 2 %]">
+         [%- 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 %]
+           </table>
+         </td>
+         [%- END %]
+       </tr>
+     </table>
+   [%- ELSE %]
+     <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 %]
+     </table>
+   [%- END %]
 
 [%- IF SELF.type == 'contacts' %]
    <p>
     [% 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>
+
+[%- ELSIF SELF.type == 'orders' %]
+   <p>
+    [1]:
+    [% LxERP.t8('The column "datatype" must be present and must be the first column. 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 %]
 
    <p>
  [%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
 [%- ELSIF SELF.type == 'contacts' %]
  [%- INCLUDE 'csv_import/_form_contacts.html' %]
+[%- ELSIF SELF.type == 'orders' %]
+ [%- INCLUDE 'csv_import/_form_orders.html' %]
 [%- END %]
 
    <tr>
index 51d4eb23c7577f84389440828379562f403ce793..539ce5a1f0e9a5a6ad3b16da4eb71f365fcc9a07 100644 (file)
@@ -6,7 +6,7 @@
 [%- PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
  <table>
 [%- FOREACH rownum = SELF.display_rows %]
- [%- IF loop.first %]
+ [%- IF rownum < SELF.report_numheaders %]
   <tr class="listheading">
   [%- FOREACH value = SELF.report_rows.${rownum} %]
    <th>[% value | html %]</th>
@@ -21,7 +21,7 @@
   [%- END %]
    <td>
     [%- FOREACH error = csv_import_report_errors %][%- error | html %][% UNLESS loop.last %]<br>[%- END %][%- END %]
-    [%- FOREACH info  = SELF.report_status.${rownum}.information %][% IF !loop.first || csv_import_report_errors.size %]<br>[%- END %][%- info | html %][%- 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 %]
diff --git a/templates/webpages/ct/_contact.html b/templates/webpages/ct/_contact.html
deleted file mode 100644 (file)
index a7d395e..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-[% USE L %][% USE HTML %][% USE T8 %][% USE LxERP %]
-    <table>
-     <input type="hidden" name="cp_id" value="[% HTML.escape(cp_id) %]">
-     <tr>
-      <th align="left">[% 'Contacts' | $T8 %]</th>
-      <td>
-       [%- L.select_tag('cp_id', CONTACTS, default = cp_id, with_empty = 1, empty_title = LxERP.t8('New contact'), value_key = 'cp_id', title_sub = \contacts_label,
-                        onchange = "\$('#contacts').load('ct.pl?action=get_contact&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&cp_id=' + \$('#cp_id').val())") %]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Gender' | $T8 %]</th>
-      <td>
-       <select id="cp_gender" name="cp_gender">
-        <option value="m"[% IF cp_gender == 'm' %] selected[% END %]>[% 'male' | $T8 %]</option>
-        <option value="f"[% IF cp_gender == 'f' %] selected[% END %]>[% 'female' | $T8 %]</option>
-       </select>
-      </td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Title' | $T8 %]</th>
-      <td>
-       <input id="cp_title" name="cp_title" size="40" maxlength="75" value="[% HTML.escape(cp_title) %]">&nbsp;
-       [% L.select_tag('selected_cp_title', TITLES, with_empty = 1) %]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Department' | $T8 %]</th>
-      <td>
-       <input id="cp_abteilung" name="cp_abteilung" size="40" value="[% HTML.escape(cp_abteilung) %]">&nbsp;
-       [% L.select_tag('selected_cp_abteilung', DEPARTMENT, with_empty = 1) %]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Function/position' | $T8 %]</th>
-      <td>[% L.input_tag('cp_position', cp_position, size=40, maxlength=75) %]</td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Given Name' | $T8 %]</th>
-      <td><input id="cp_givenname" name="cp_givenname" size="40" maxlength="75" value="[% HTML.escape(cp_givenname) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Name' | $T8 %]</th>
-      <td><input id="cp_name" name="cp_name" size="40" maxlength="75" value="[% HTML.escape(cp_name) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'E-mail' | $T8 %]</th>
-      <td><input id="cp_email" name="cp_email" size="40" value="[% HTML.escape(cp_email) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Phone1' | $T8 %]</th>
-      <td><input id="cp_phone1" name="cp_phone1" size="40" maxlength="75" value="[% HTML.escape(cp_phone1) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Phone2' | $T8 %]</th>
-      <td><input id="cp_phone2" name="cp_phone2" size="40" maxlength="75" value="[% HTML.escape(cp_phone2) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Fax' | $T8 %]</th>
-      <td><input id="cp_fax" name="cp_fax" size="40" value="[% HTML.escape(cp_fax) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Mobile1' | $T8 %]</th>
-      <td><input id="cp_mobile1" name="cp_mobile1" size="40" value="[% HTML.escape(cp_mobile1) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Mobile2' | $T8 %]</th>
-      <td><input id="cp_mobile2" name="cp_mobile2" size="40" value="[% HTML.escape(cp_mobile2) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Sat. Phone' | $T8 %]</th>
-      <td><input id="cp_satphone" name="cp_satphone" size="40" value="[% HTML.escape(cp_satphone) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Sat. Fax' | $T8 %]</th>
-      <td><input id="cp_satfax" name="cp_satfax" size="40" value="[% HTML.escape(cp_satfax) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Project' | $T8 %]</th>
-      <td><input id="cp_project" name="cp_project" size="40" value="[% HTML.escape(cp_project) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Street' | $T8 %]</th>
-      <td><input id="cp_street" name="cp_street" size="40" maxlength="75" value="[% HTML.escape(cp_street) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Zip, City' | $T8 %]</th>
-      <td>
-       <input id="cp_zipcode" name="cp_zipcode" size="5" maxlength="10" value="[% HTML.escape(cp_zipcode) %]">
-       <input id="cp_city" name="cp_city" size="25" maxlength="75" value="[% HTML.escape(cp_city) %]">
-      </td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Private Phone' | $T8 %]</th>
-      <td><input id="cp_privatphone" name="cp_privatphone" size="40" value="[% HTML.escape(cp_privatphone) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Private E-mail' | $T8 %]</th>
-      <td><input id="cp_privatemail" name="cp_privatemail" size="40" value="[% HTML.escape(cp_privatemail) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Birthday' | $T8 %]</th>
-      <td>
-        [% L.date_tag('cp_birthday', cp_birthday) %]
-      </td>
-     </tr>
-
-     [% IF CUSTOM_VARIABLES.Contacts.size %]
-     <tr>
-      <td colspan="2"><hr></td>
-     </tr>
-
-     [%- FOREACH var = CUSTOM_VARIABLES.Contacts %]
-     <tr>
-      <th align="left" valign="top" nowrap>[% HTML.escape(var.description) %]</th>
-      <td valign="top">[% var.HTML_CODE %]</td>
-     </tr>
-     [%- END %]
-     [%- END %]
-
-    </table>
-
-    [% IF cp_id %]
-      <input type="button" id="delete_contact" onclick="submitInputButton(this);" name="action" value="[% 'Delete Contact' | $T8 %]">
-    [% END %]
diff --git a/templates/webpages/ct/_shipto.html b/templates/webpages/ct/_shipto.html
deleted file mode 100644 (file)
index 47eacee..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-[% USE L %][% USE HTML %][% USE T8 %][% USE LxERP %]
-    <table width="100%" id="shipto_table">
-     <tr>
-      <th align="right">[% 'Shipping Address' | $T8 %]</th>
-      <td>
-       [% L.select_tag('shipto_id', SHIPTO, default = shipto_id, value_key = 'shipto_id', title_sub = \shipto_label, with_empty = 1, empty_title = LxERP.t8('New shipto'),
-                       onchange = "\$('#shipto').load('ct.pl?action=get_shipto&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&shipto_id=' + this.value)") %]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Name' | $T8 %]</th>
-      <td><input id="shiptoname" name="shiptoname" size="35" maxlength="75" value="[% HTML.escape(shiptoname) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Abteilung' | $T8 %]</th>
-      <td>
-       <input id="shiptodepartment_1" name="shiptodepartment_1" size="16" maxlength="75" value="[% HTML.escape(shiptodepartment_1) %]">
-       <input id="shiptodepartment_2" name="shiptodepartment_2" size="16" maxlength="75" value="[% HTML.escape(shiptodepartment_2) %]">
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Street' | $T8 %]</th>
-      <td><input id="shiptostreet" name="shiptostreet" size="35" maxlength="75" value="[% HTML.escape(shiptostreet) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Zipcode' | $T8 %]/[% 'City' | $T8 %]</th>
-      <td>
-       <input id="shiptozipcode" name="shiptozipcode" size="5" maxlength="75" value="[% HTML.escape(shiptozipcode) %]">
-       <input id="shiptocity" name="shiptocity" size="30" maxlength="75" value="[% HTML.escape(shiptocity) %]">
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Country' | $T8 %]</th>
-      <td><input id="shiptocountry" name="shiptocountry" size="35" maxlength="75" value="[% HTML.escape(shiptocountry) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Contact' | $T8 %]</th>
-      <td><input id="shiptocontact" name="shiptocontact" size="30" maxlength="75" value="[% HTML.escape(shiptocontact) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Phone' | $T8 %]</th>
-      <td><input id="shiptophone" name="shiptophone" size="30" maxlength="30" value="[% HTML.escape(shiptophone) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Fax' | $T8 %]</th>
-      <td><input id="shiptofax" name="shiptofax" size="30" maxlength="30" value="[% HTML.escape(shiptofax) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'E-mail' | $T8 %]</th>
-      <td><input id="shiptoemail" name="shiptoemail" size="45" value="[% HTML.escape(shiptoemail) %]"></td>
-     </tr>
-    </table>
-
-[%- IF shipto_id %]
-    <input type="submit" id="delete_shipto" name="action" value="[% 'Delete Shipto' | $T8 %]">
-[%- END %]
diff --git a/templates/webpages/ct/ajax_autocomplete.html b/templates/webpages/ct/ajax_autocomplete.html
deleted file mode 100644 (file)
index 25ab8c3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[%- USE HTML %]
-[%- FOREACH vc = CT %]
-[%- IF loop.count < limit %]
-[% vc.$column %]
-[%- END %]
-[%- END %]
diff --git a/templates/webpages/ct/form_footer.html b/templates/webpages/ct/form_footer.html
deleted file mode 100644 (file)
index 8df9c7e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-[%- USE T8 %]
-[% USE HTML %]
-[% USE LxERP %]
-[% USE L %]
-<input name="id" type="hidden" id="cvid" value="[% HTML.escape(id) %]">
-<input name="business_save" type="hidden" value="[% HTML.escape(selectbusiness) %]">
-<input name="title_save" type="hidden" value="[% HTML.escape(title) %]">
-
-<input type="hidden" name="callback" value="[% HTML.escape(callback) %]">
-<input type="hidden" name="db" id="db" value="[% HTML.escape(db) %]">
-
-<br>
-  <input class="submit" type="submit" name="action" accesskey="s" value="[% 'Save' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  <input class="submit" type="submit" name="action" accesskey="s" value="[% 'Save and Close' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- IF is_customer %]
-  <input class="submit" type="submit" name="action" value="[% 'Save and AR Transaction' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- ELSE %]
-  <input class="submit" type="submit" name="action" value="[% 'Save and AP Transaction' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- END %]
-  <input class="submit" type="submit" name="action" value="[% 'Save and Invoice' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  <input class="submit" type="submit" name="action" value="[% 'Save and Order' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- IF is_customer %]
-  <input class="submit" type="submit" name="action" value="[% 'Save and Quotation' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- ELSE %]
-  <input class="submit" type="submit" name="action" value="[% 'Save and RFQ' | $T8 %]" onclick="return check_taxzone_and_ustid()">
-  [%- END %]
-[%- IF id AND is_orphaned %]
-  [% L.submit_tag('action', LxERP.t8('Delete'), id => 'action_delete', confirm => LxERP.t8('Do you really want to delete this object?')) %]
-[%- END %]
-[%- IF id %]
-  <input type="button" class="submit" onclick="set_history_window([% HTML.escape(id) %]);" name="history" id="history" value="[% 'history' | $T8 %]">
-[%- END %]
-
-  </form>
-
- <script type="text/javascript">
-  <!--
-      function enable_delete_shipto(used) { var s=document.getElementById('delete_shipto');  if (s) s.disabled = (used > 0 ? true : false); }
-      function enable_delete_contact(used){ var s=document.getElementById('delete_contact'); if (s) s.disabled = (used > 0 ? true : false); }
-
-      function submitInputButton(button)
-      {
-        var hidden = document.createElement("input");
-        hidden.setAttribute("type", "hidden");
-
-        if( button.hasAttribute("name") )
-          hidden.setAttribute("name", button.getAttribute("name"));
-
-        if( button.hasAttribute("value") )
-          hidden.setAttribute("value", button.getAttribute("value"));
-
-
-        button.form.appendChild(hidden);
-
-        button.disabled = true;
-
-        button.form.submit();
-      }
-
-      function check_taxzone_and_ustid() {
-        if (($('#taxzone_id').val() == '1') && ($('#ustid').val() == '')) {
-          alert('[% LxERP.t8('Please enter the sales tax identification number.') %]');
-          return false;
-        }
-        return true;
-      }
-
-    -->
- </script>
diff --git a/templates/webpages/ct/form_header.html b/templates/webpages/ct/form_header.html
deleted file mode 100644 (file)
index cd5f610..0000000
+++ /dev/null
@@ -1,462 +0,0 @@
-[%- USE T8 %]
-[% USE HTML %][% USE LxERP %]
-[% USE L %]
-
- <h1>[% title %]</h1>
-
- [% PROCESS 'common/flash.html' %]
-
- <form method="post" name="ct" action="ct.pl" >
-
-  <div class="tabwidget">
-   <ul>
-    <li><a href="#billing">[% 'Billing Address' | $T8 %]</a></li>
-    <li><a href="#shipto">[% 'Shipping Address' | $T8 %]</a></li>
-    <li><a href="#contacts">[% 'Contacts' | $T8 %]</a></li>
-[%- IF id %]
-    <li><a href="#deliveries">[% 'Supplies' | $T8 %]</a></li>
-[%- END %]
-    <li><a href="#vcnotes">[% 'Notes' | $T8 %]</a></li>
-[%- IF CUSTOM_VARIABLES.CT.size %]
-    <li><a href="#custom_variables">[% 'Custom Variables' | $T8 %]</a></li>
-[%- END %]
-   </ul>
-
-   <div id="billing">
-
-    <table width="100%">
-     <tr height="5"></tr>
-     [% IF INSTANCE_CONF.get_vertreter %]
-      <tr>
-       <th align="right">[% IF is_customer %][% 'Type of Customer' | $T8 %][%- ELSE %][% 'Type of Vendor' | $T8 %][%- END %]</th>
-       <td>
-        [%- INCLUDE generic/multibox.html
-              name       = 'business',
-              DATA       = all_business,
-              show_empty = 1,
-              id_key     = 'id',
-              label_key  = 'description',
-        -%]
-       </td>
-      </tr>
-      [%- IF id %]
-      <tr>
-       <th align="right">[% 'Representative' | $T8 %]</th>
-       <td>
-        <input type="hidden" name="salesman_id" value="[%- HTML.escape(salesman_id) %]"%>
-        [%- HTML.escape(salesman) %]
-       </td>
-      </tr>
-      [%- END %]
-      <tr>
-       <th align="right">
-        [%- IF !id %]
-         [% 'Representative' | $T8 %]
-        [%- ELSE %]
-         [% 'Change representative to' | $T8 %]
-        [%- END %]
-       </th>
-       <td>
-        [%- INCLUDE generic/multibox.html
-              name       = 'new_salesman_id',
-              DATA       = ALL_SALESMAN_CUSTOMERS,
-              id_key     = 'id',
-              label_key  = 'name',
-              show_empty = id,
-        -%]
-       </td>
-      </tr>
-     [%- END %]
-     <tr>
-      [%- IF is_customer %]
-      <th align="right" nowrap>[% 'Customer Number' | $T8 %]</th>
-      <td><input name="customernumber" size="35" value="[% HTML.escape(customernumber) %]"></td>
-      [%- ELSE %]
-      <th align="right" nowrap>[% 'Vendor Number' | $T8 %]</th>
-      <td><input name="vendornumber" size="35" value="[% HTML.escape(vendornumber) %]"></td>
-      [%- END %]
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Greeting' | $T8 %]</th>
-      <td>
-       <input id="greeting" name="greeting" size="30" value="[% HTML.escape(greeting) %]" class="initial_focus">&nbsp;
-       [%- INCLUDE generic/multibox.html
-             name       = 'selected_company_greeting',
-             DATA       = MB_COMPANY_GREETINGS,
-             show_empty = 1,
-             id_key     = 'id',
-             label_key  = 'description',
-       -%]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% IF is_customer %][% 'Customer Name' | $T8 %][%- ELSE %][% 'Vendor Name' | $T8 %][%- END %]</th>
-      <td><input name="name" size="35" maxlength="75" value="[% HTML.escape(name) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Department' | $T8 %]</th>
-      <td>
-       <input name="department_1" size="16" maxlength="75" value="[% HTML.escape(department_1) %]">
-       <input name="department_2" size="16" maxlength="75" value="[% HTML.escape(department_2) %]">
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Street' | $T8 %]</th>
-      <td>
-        <input name="street" size="35" maxlength="75" value="[% HTML.escape(street) %]">
-        <a
-          href="#"
-          onclick="window.open('https://maps.google.com/maps?q='+ encodeURIComponent($('#billing input[name=street]').val() +', '+ $('#billing input[name=zipcode]').val() +' '+ $('#billing input[name=city]').val() +', '+ $('#billing input[name=country]').val()), '_blank'); window.focus();"
-          title="[% 'Map' | $T8 %]"
-          >
-            <img src="image/map.png" alt="[% 'Map' | $T8 %]" />
-          </a>
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Zipcode' | $T8 %]/[% 'City' | $T8 %]</th>
-      <td>
-       <input name="zipcode" size="5" maxlength="10" value="[% HTML.escape(zipcode) %]">
-       <input name="city" size="30" maxlength="75" value="[% HTML.escape(city) %]">
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Country' | $T8 %]</th>
-      <td><input name="country" size="35" maxlength="75" value="[% HTML.escape(country) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Contact' | $T8 %]</th>
-      <td><input name="contact" size="28" maxlength="75" value="[% HTML.escape(contact) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Phone' | $T8 %]</th>
-      <td><input name="phone" size="30" value="[% HTML.escape(phone) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Fax' | $T8 %]</th>
-      <td><input name="fax" size="30" maxlength="30" value="[% HTML.escape(fax) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'E-mail' | $T8 %]</th>
-      <td><input name="email" size="45" value="[% HTML.escape(email) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Cc E-mail' | $T8 %]</th>
-      <td><input name="cc" size="45" value="[% HTML.escape(cc) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Bcc E-mail' | $T8 %]</th>
-      <td><input name="bcc" size="45" value="[% HTML.escape(bcc) %]"></td>
-     </tr>
-
-
-     <tr>
-      <th align="right" nowrap>
-      [% IF homepage %]<a href="[% HTML.escape(homepage) %]" title="[% 'Open this Website' | $T8 %]" target="_blank">[% 'Homepage' | $T8 %]</a>
-      [% ELSE %][% 'Homepage' | $T8 %]
-      [% END %]
-      </th>
-      <td><input name="homepage" size="45" title="[% 'Example: http://kivitendo.de' | $T8 %]" value="[% HTML.escape(homepage) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Username' | $T8 %]</th>
-      <td><input name="username" size="45" value="[% HTML.escape(username) %]"></td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'Password' | $T8 %]</th>
-      <td><input name="user_password" size="45" value="[% HTML.escape(user_password) %]"></td>
-     </tr>
-    </table>
-
-    <table>
-     <tr>
-      <th align="right">[% 'Credit Limit' | $T8 %]</th>
-      <td><input name="creditlimit" size="9" value="[% LxERP.format_amount(creditlimit, 0) %]"></td>
-      <input type="hidden" name="terms" value="[% HTML.escape(terms) %]">
-      <th align="right">[% 'Payment Terms' | $T8 %]</th>
-      <td>
-       [%- INCLUDE generic/multibox.html
-             name       = 'payment_id',
-             DATA       = payment_terms,
-             show_empty = 1,
-             id_key     = 'id',
-             label_key  = 'description',
-       -%]
-      </td>
-
-      <th align="right">[% 'Discount' | $T8 %]</th>
-      <td><input name="discount" size="4" value="[% LxERP.format_amount(discount) %]"> %</td>
-     </tr>
-
-     <tr>
-      <th align="right">[% 'Tax Number / SSN' | $T8 %]</th>
-      <td><input name="taxnumber" size="20" value="[% HTML.escape(taxnumber) %]"></td>
-      <!-- Anm.: R&B 15.11.2008     VAT Reg No ist Ust-ID in GB, aber generell sollte es laut Richardson die sales tax id sein -->
-      <th align="right">[% 'sales tax identification number' | $T8 %]</th>
-      <td>[% L.input_tag('ustid', ustid, maxlength=14, size=30) %]</td>
-      [%- IF is_customer %]
-      <th align="right">[% 'our vendor number at customer' | $T8 %]</th>
-      <td>[% L.input_tag('c_vendor_id', c_vendor_id, size=30) %]</td>
-      [%- ELSE %]
-      <th align="right">[% 'Customer Number' | $T8 %]</th>
-      <td>[% L.input_tag('v_customer_id', v_customer_id, size=30) %]</td>
-      [%- END %]
-     </tr>
-
-     <tr>
-      <th align="right">[% 'Account Number' | $T8 %]</th>
-      <td>[% L.input_tag('account_number', account_number, size=30) %]</td>
-      <th align="right">[% 'Bank Code Number' | $T8 %]</th>
-      <td>[% L.input_tag('bank_code', bank_code, size=30) %]</td>
-      <th align="right">[% 'Bank' | $T8 %]</th>
-      <td>[% L.input_tag('bank', bank, size=30) %]</td>
-     </tr>
-
-     <tr>
-      <th align="right">[% 'IBAN' | $T8 %]</th>
-      <td>[% L.input_tag('iban', iban, maxlength=100, size=30) %]</td>
-      <th align="right">[% 'BIC' | $T8 %]</th>
-      <td>[% L.input_tag('bic', bic, maxlength=100, size=30) %]</td>
-      [%- IF ALL_CURRENCIES.size %]
-        <th align="right">[% 'Currency' | $T8 %]</th>
-        <td>[% L.select_tag('currency', ALL_CURRENCIES, default = currency) %]</td>
-      [%- END %]
-     </tr>
-
-     <tr>
-      [% UNLESS INSTANCE_CONF.get_vertreter %]
-       <th align="right">[% IF is_customer %][% 'Type of Customer' | $T8 %][% ELSE %][% 'Type of Vendor' | $T8 %][%- END %]</th>
-       <td>
-        [%- INCLUDE generic/multibox.html
-              name       = 'business',
-              DATA       = all_business,
-              show_empty = 1,
-              id_key     = 'id',
-              label_key  = 'description',
-        -%]
-       </td>
-      [%- END %]
-      <th align="right">[% 'Language' | $T8 %]</th>
-      <td>
-       [%- INCLUDE generic/multibox.html
-             name       = 'language_id',
-             default    = default_language_id,
-             DATA       = languages,
-             show_empty = 1,
-             id_key     = 'id',
-             label_key  = 'description',
-       -%]
-      </td>
-
-      [%- IF is_customer %]
-      <th align="right">[% 'Preisklasse' | $T8 %]</th>
-      <td>
-       [%- INCLUDE generic/multibox.html
-             name       = 'klass',
-             DATA       = all_pricegroup,
-             show_empty = 1,
-             id_key     = 'id',
-             label_key  = 'pricegroup',
-       -%]
-      </td>
-      [%- END  %]
-     </tr>
-
-     <tr>
-      <td align="right"><label for="obsolete">[% 'Obsolete' | $T8 %]</label></td>
-      <td><input name="obsolete" id="obsolete" class="checkbox" type="checkbox" value="1" [% IF obsolete %]checked[% END %]></td>
-      <td align="right"><label for="direct_debit">[% 'direct debit' | $T8 %]</label></td>
-      <td><input name="direct_debit" id="direct_debit" class="checkbox" type="checkbox" value="1" [% IF direct_debit %]checked[% END %]></td>
-     </tr>
-
-     <tr>
-      <th align="right">[% 'Steuersatz' | $T8 %]</th>
-      <td>
-       [%- INCLUDE generic/multibox.html
-             name       = 'taxzone_id',
-             id         = 'taxzone_id',
-             DATA       = ALL_TAXZONES,
-             show_empty = 0,
-             id_key     = 'id',
-             label_key  = 'description',
-       -%]
-      </td>
-      [%- IF is_customer && !INSTANCE_CONF.get_vertreter %]
-      <th align="right">[% 'Salesman' | $T8 %]</th>
-      <td>[% L.select_tag('salesman_id', ALL_SALESMEN, default = salesman_id, with_empty = 1, title_key = 'safe_name') %]</td>
-      [%- END %]
-
-      <td>[% 'taxincluded checked' | $T8 %]</td>
-      <td>[% L.select_tag('taxincluded_checked', [[undef, LxERP.t8('use user config')], ['1', LxERP.t8('Yes')], ['0', LxERP.t8('No')]], default = taxincluded_checked) %]</td>
-     </tr>
-    </table>
-
-    <table>
-     <tr>
-      <th align="left" nowrap>[% 'Internal Notes' | $T8 %]</th>
-     </tr>
-
-     <tr>
-      <td><textarea name="notes" rows="3" cols="60" wrap="soft">[% HTML.escape(notes) %]</textarea></td>
-     </tr>
-    </table>
-   </div>
-
-   <div id="shipto">
-    [% INCLUDE 'ct/_shipto.html' %]
-   </div>
-
-   <div id="contacts">
-    [% INCLUDE 'ct/_contact.html' %]
-   </div>
-
-   <div id="deliveries">
-    <table>
-     <tr>
-      <th align="right">[% 'Shipping Address' | $T8 %]</th>
-      <td colspan="3">
-       [% L.select_tag('delivery_id', SHIPTO_ALL, value_key = 'shipto_id', title_sub = \shipto_label, with_empty = 1,
-                       onchange = "\$('#delivery').load('ct.pl?action=get_delivery&id=' + \$('#cvid').val() + '&db=' + \$('#db').val() + '&shipto_id=' + this.value)") %]
-      </td>
-     </tr>
-
-     <tr>
-      <th align="right" nowrap>[% 'From' | $T8 %]</th>
-      <td>
-        [% L.date_tag('from',
-                      from,
-                      onchange => "\$('#delivery').load('ct.pl?action=get_delivery&shipto_id='+ \$('#delivery_id').val() +'&from='+ \$('#from').val() +'&to='+ \$('#to').val() +'&id='+ \$('#cvid').val() +'&db='+ \$('#db').val())")
-        %]
-      </td>
-      <th align="right" nowrap>[% 'To (time)' | $T8 %]</th>
-      <td>
-       [% L.date_tag('to',
-                      to,
-                      onchange => "\$('#delivery').load('ct.pl?action=get_delivery&shipto_id='+ \$('#delivery_id').val() +'&from='+ \$('#from').val() +'&to='+ \$('#to').val() +'&id='+ \$('#cvid').val() +'&db='+ \$('#db').val())")
-       %]
-      </td>
-     </tr>
-
-     <tr>
-      <td colspan="4">
-       <div id="delivery">
-       </div>
-      </td>
-     </tr>
-    </table>
-   </div>
-
-   <div id="vcnotes">
-
-    [%- IF NOTES && NOTES.size %]
-    <p>
-     <table>
-      <tr>
-       <th class="listheading">[% 'Delete' | $T8 %]</th>
-       <th class="listheading">[% 'Subject' | $T8 %]</th>
-       <th class="listheading">[% 'Created on' | $T8 %]</th>
-       <th class="listheading">[% 'Created by' | $T8 %]</th>
-       <th class="listheading">[% 'Follow-Up Date' | $T8 %]</th>
-       <th class="listheading">[% 'Follow-Up for' | $T8 %]</th>
-       <th class="listheading">[% 'Follow-Up done' | $T8 %]</th>
-      </tr>
-
-      [%- FOREACH row = NOTES %]
-      <tr class="listrow[% loop.count % 2 %]">
-       <input type="hidden" name="NOTE_id_[% loop.count %]" value="[% HTML.escape(row.id) %]">
-       <td>[% UNLESS NOTE_id && (NOTE_id == row.id) %]<input type="checkbox" name="NOTE_delete_[% loop.count %]" value="1">[% END %]</td>
-       <td><a href="ct.pl?action=edit&db=[% HTML.url(db) %]&id=[% HTML.url(id) %]&edit_note_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a></td>
-       <td>[% HTML.escape(row.created_on) %]</td>
-       <td>[% IF row.created_by_name %][% HTML.escape(row.created_by_name) %][% ELSE %][% HTML.escape(row.created_by_login) %][% END %]</td>
-       <td>[% HTML.escape(row.follow_up_date) %]</td>
-       <td>[% IF row.created_for_name %][% HTML.escape(row.created_for_name) %][% ELSE %][% HTML.escape(row.created_for_login) %][% END %]</td>
-       <td>[% IF row.follow_up_date %][% IF row.follow_up_done %][% 'Yes' | $T8 %][% ELSE %][% 'No' | $T8 %][% END %][% END %]</td>
-      </tr>
-      [%- END %]
-
-      <input type="hidden" name="NOTES_rowcount" value="[% NOTES.size %]">
-
-     </table>
-    </p>
-    [%- END %]
-
-    <div class="listtop">[% IF NOTE_id %][% 'Edit note' | $T8 %][% ELSE %][% 'Add note' | $T8 %][% END %]</div>
-
-    <input type="hidden" name="NOTE_id" value="[% HTML.escape(NOTE_id) %]">
-    <input type="hidden" name="FU_id" value="[% HTML.escape(FU_id) %]">
-
-    <p>
-     <table>
-      <tr>
-       <td valign="right">[% 'Subject' | $T8 %]</td>
-       <td><input name="NOTE_subject" value="[% HTML.escape(NOTE_subject) %]" size="50"></td>
-      </tr>
-
-      <tr>
-       <td valign="right" align="top">[% 'Body' | $T8 %]</td>
-       <td align="top"><textarea cols="50" rows="10" name="NOTE_body">[% HTML.escape(NOTE_body) %]</textarea></td>
-      </tr>
-
-      <tr>
-       <td valign="right">[% 'Follow-Up On' | $T8 %]</td>
-       <td>
-        [% L.date_tag('FU_date', FU_date) %]
-        [% 'for' | $T8 %]
-        [% L.select_tag('FU_created_for_user', ALL_EMPLOYEES, default = (FU_created_for_user ? FU_created_for_user : USER.id), title_key='safe_name') %]
-       </td>
-      </tr>
-
-      <tr>
-       <td>&nbsp;</td>
-       <td>
-        <input type="checkbox" name="FU_done" id="FU_done" value="1"[% IF FU_done %] checked[% END %]>
-        <label for="FU_done">[% 'Follow-Up done' | $T8 %]</label>
-       </td>
-      </tr>
-     </table>
-    </p>
-   </div>
-
-   [%- IF CUSTOM_VARIABLES.CT.size %]
-   <div id="custom_variables">
-
-    <p>
-     <table>
-      [%- FOREACH var = CUSTOM_VARIABLES.CT %]
-      <tr>
-       <td align="right" valign="top">[% HTML.escape(var.description) %]</td>
-       <td valign="top">[% var.HTML_CODE %]</td>
-      </tr>
-      [%- END %]
-     </table>
-    </p>
-   </div>
-   [%- END %]
-
-  </div>
-
-  <script type="text/javascript">
-   <!--
-   function set_gender(gender) {
-     var s = document.getElementById('cp_gender');
-     if (s) {
-       s.selectedIndex = (gender == 'f') ? 1 : 0;
-     }
-   }
-  -->
-
-  </script>
diff --git a/templates/webpages/ct/get_delivery.html b/templates/webpages/ct/get_delivery.html
deleted file mode 100644 (file)
index 921aaba..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-[%- USE T8 %]
-[% USE HTML %][% USE LxERP %]
-<div id="delivery">
-<table width="100%">
-  <tr>
-    <td>
-      <table width="100%">
-        <tr class="listheading">
-          <th class="listheading">[% 'Shipping Address' | $T8 %]</th>
-          <th class="listheading">[% 'Invoice' | $T8 %]</th>
-          <th class="listheading">[% 'Order' | $T8 %]</th>
-          <th class="listheading">[% 'Invdate' | $T8 %]</th>
-          <th class="listheading">[% 'Description' | $T8 %]</th>
-          <th class="listheading">[% 'Qty' | $T8 %]</th>
-          <th class="listheading">[% 'Unit' | $T8 %]</th>
-[%- IF is_customer %]
-          <th class="listheading">[% 'Sell Price' | $T8 %]</th>
-[%- ELSE %]
-          <th class="listheading">[% 'Last Cost' | $T8 %]</th>
-[%- END %]
-        </tr>
-[%- FOREACH row = DELIVERY %]
-[%-
-    row.script = is_customer ?  ( row.invoice ? 'is' : 'ar' )
-                             :  ( row.invoice ? 'ir' : 'ap' )
--%]
-        <tr class="listrow[% loop.count % 2 %]">
-          <td>[% HTML.escape(row.shiptoname) UNLESS loop.prev.shiptoname == row.shiptoname %]&nbsp;</td>
-          <td>[% IF row.id %]<a href='[% row.script %].pl?action=edit&id=[% HTML.escape(row.id) %]'>[% END %][% HTML.escape(row.invnumber)   || '&nbsp;' %][% IF row.id %]</a>[% END %]</td>
-          <td>[% IF row.oe_id %]<a href='oe.pl?action=edit&type=[% IF is_customer %]sales_order[% ELSE %]purchase_order[% END %]&vc=customer&id=[% HTML.escape(row.oe_id) %]'>[% END %][% HTML.escape(row.ordnumber)   || '&nbsp;' %][% IF row.oe_id %]</a>[% END %]</td>
-          <td>[% HTML.escape(row.transdate)   || '&nbsp;' %]</td>
-          <td>[% HTML.escape(row.description) || '&nbsp;' %]</td>
-          <td>[% HTML.escape(row.qty)         || '&nbsp;' %]</td>
-          <td>[% HTML.escape(row.unit)        || '&nbsp;' %]</td>
-          <td>[% LxERP.format_amount(row.sellprice, 2) || '&nbsp;' %]</td>
-        </tr>
-[%- END %]
-
-     </table>
-[%- IF DELIVERY.size == 15 %]
-     <p>[% 'This list is capped at 15 items to keep it fast. If you need a full list, please use reports.' | $T8 %]</p>
-[%- END %]
-</div>
index 7faabc3118367ee66859a038d76a849cbb7cfcca..9546101fa86ba4405a64e8f43bca04cf1d246290 100644 (file)
@@ -1,10 +1,10 @@
-[% USE T8 %][% USE HTML %]<form method="post" action="dispatcher.pl?M=ct">
+[% USE T8 %][% USE HTML %]
 
- <input name="callback" type="hidden" value="[% HTML.escape(callback) %]">
- <input name="db" type="hidden" value="[% HTML.escape(db) %]">
+<form method="post" action="controller.pl?action=CustomerVendor/add">
+  <input name="callback" type="hidden" value="[% HTML.escape(callback) %]">
+  <input name="db" type="hidden" value="[% HTML.escape(db) %]">
 
- [% IF IS_CUSTOMER %][% 'New customer' | $T8 %][% ELSE %][% 'New vendor' | $T8 %][% END %]<br>
-
- <input class="submit" type="submit" name="A_add" value="[%- 'Add' | $T8 %]">
+  [% IF IS_CUSTOMER %][% 'New customer' | $T8 %][% ELSE %][% 'New vendor' | $T8 %][% END %]<br>
 
+  <input class="submit" type="submit" value="[%- 'Add' | $T8 %]">
 </form>
diff --git a/templates/webpages/ct/testpage.html b/templates/webpages/ct/testpage.html
deleted file mode 100644 (file)
index e8ba838..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-[% USE L %]
-[% USE T8 %]
-[% USE LxERP %]
-[% L.javascript_tag('jquery-ui') %]
-<link rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.12.custom.css" type="text/css" />
-
-<p>Pick a customer</p>
-id: [% L.input_tag('customer_id', '') %]
-nr: [% L.input_tag('customer_customernumber', '') %]
-desc: [% L.input_tag('customer_name', '') %]
-
-<script type='text/javascript'>
-function autocomplete_customer (selector, column) {
-  $(function(){ $(selector).autocomplete({
-    source: function(req, rsp) {
-      $.ajax({
-        url: 'controller.pl?action=Customer/ajax_autocomplete',
-        dataType: "json",
-        data: {
-          column: column,
-          term: req.term,
-          current: function() { $('#customer_id').val() },
-          obsolete: 0,
-        },
-        success: function (data){ rsp(data) }
-      });
-    },
-    limit: 20,
-    delay: 50,
-    select: function(event, ui) {
-      $('#customer_id').val(ui.item.id);
-      $('#customer_customernumber').val(ui.item.customernumber);
-      $('#customer_name').val(ui.item.name);
-    },
-  })});
-}
-//autocomplete_customer('#customer_customernumber', 'customernumber');
-autocomplete_customer('#customer_name', '');
-</script>
-
index c8ec6b02e6b24ab377d3011d29fd12e2cebb96d5..a0d1ab9517dfed0794fe1b4d326eb18f67aade87 100644 (file)
@@ -18,7 +18,7 @@
       <li><a href="#billing">[% 'Billing Address' | $T8 %]</a></li>
       <li><a href="#shipto">[% 'Shipping Address' | $T8 %]</a></li>
       <li><a href="#contacts">[% 'Contacts' | $T8 %]</a></li>
-      [% IF ( SELF.cv.id ) %]
+      [% IF ( SELF.cv.id && AUTH.assert('sales_all_edit', 1) ) %]
         <li><a href="#deliveries">[% 'Supplies' | $T8 %]</a></li>
       [% END %]
       <li><a href="#vcnotes">[% 'Notes' | $T8 %]</a></li>
@@ -31,7 +31,7 @@
     [% PROCESS "customer_vendor/tabs/billing.html" %]
     [% PROCESS "customer_vendor/tabs/shipto.html" %]
     [% PROCESS "customer_vendor/tabs/contacts.html" %]
-    [% IF ( SELF.cv.id ) %]
+    [% IF ( SELF.cv.id && AUTH.assert('sales_all_edit', 1) ) %]
       [% PROCESS "customer_vendor/tabs/deliveries.html" %]
     [% END %]
     [% PROCESS "customer_vendor/tabs/vcnotes.html" %]
index e929a191a57295b4528e5ac909e40c3eae9390bd..5c036e070e096a9d1d82905bcfff0a2568757058 100644 (file)
       <th align="right">[% 'Discount' | $T8 %]</th>
 
       <td>
-        [% L.input_tag('cv.discount_as_percent', SELF.cv.discount_as_percent, size = 4) %]
+        [% L.input_tag('cv.discount_as_percent', SELF.cv.discount_as_percent, size = 4) %]%
       </td>
     </tr>
 
         [% L.select_tag('cv.language_id', SELF.all_languages, default = SELF.cv.language_id, value_key = 'id', title_key = 'description', with_empty = 1) %]
       </td>
 
-      [% IF ( SELF.is_customer() ) %]
-        <th align="right">[% 'Preisklasse' | $T8 %]</th>
+      <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+
+      <td>
+        [% L.select_tag('cv.delivery_term_id', SELF.all_delivery_terms, default = SELF.cv.delivery_term_id, value_key = 'id', title_key = 'description', with_empty = 1) %]
+      </td>
 
-        <td>
-          [% L.select_tag('cv.klass', SELF.all_pricegroups, default = SELF.cv.klass, value_key = 'id', title_key = 'pricegroup', with_empty = 1) %]
-        </td>
-      [% END  %]
     </tr>
 
     <tr>
       <td>
         [% L.checkbox_tag('cv.direct_debit', checked = SELF.cv.direct_debit, for_submit=1) %]
       </td>
+
+      [% IF ( SELF.is_customer() ) %]
+        <th align="right">[% 'Preisklasse' | $T8 %]</th>
+
+        <td>
+          [% L.select_tag('cv.klass', SELF.all_pricegroups, default = SELF.cv.klass, value_key = 'id', title_key = 'pricegroup', with_empty = 1) %]
+        </td>
+      [% END  %]
+
     </tr>
 
     <tr>
index 04bf9516d0934cb2b5c8f17aa6e94b3b363fdaf0..403b0ffc17b64c80c8cde8969304772b4336e4d6 100644 (file)
@@ -3,7 +3,7 @@
 [%- USE L %]
 
 <div id="vcnotes">
-   [% IF ( NOTES && NOTES.size ) %]
+  [% IF ( SELF.notes && SELF.notes.size ) %]
      <p>
        <table>
         <tr>
         [%- FOREACH row = SELF.notes %]
           <tr class="listrow[% loop.count % 2 %]">
             <td>
-              [% L.hidden_tag('notes[+].id', row.id) %]
-              [% IF ( !NOTE_id || (NOTE_id != row.id) ) %]
-                [% L.checkbox_tag('notes[].delete', 0) %]
+              [% IF ( !SELF.note || SELF.note.id != row.id ) %]
+                [% L.checkbox_tag('delete_notes[]', value = row.id) %]
               [% END %]
             </td>
 
             <td>
-              <a href="ct.pl?action=edit&db=[% HTML.url(db) %]&id=[% HTML.url(id) %]&edit_note_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a>
+              <a href="controller.pl?action=CustomerVendor/edit&db=[% SELF.is_vendor() ? 'vendor' : 'customer' %]&id=[% HTML.url(SELF.cv.id) %]&note_id=[% HTML.url(row.id) %]">[% HTML.escape(row.subject) %]</a>
             </td>
 
             <td>
-              [% HTML.escape(row.created_on) %]
+              [% row.itime.to_kivitendo | html %]
             </td>
 
             <td>
-              [% IF ( row.created_by_name ) %]
-                [% HTML.escape(row.created_by_name) %]
-              [% ELSE %]
-                [% HTML.escape(row.created_by_login) %]
-              [% END %]
+              [% row.employee.safe_name | html %]
             </td>
 
             <td>
-              [% HTML.escape(row.follow_up_date) %]
+              [% row.follow_up.follow_up_date.to_kivitendo | html %]
             </td>
 
             <td>
-              [% IF ( row.created_for_name ) %]
-                [% HTML.escape(row.created_for_name) %]
-              [% ELSE %]
-                [% HTML.escape(row.created_for_login) %]
-              [% END %]
+              [% row.follow_up.created_for.safe_name | html %]
             </td>
 
             <td>
-              [% IF ( row.follow_up_date ) %]
-                [% IF ( row.follow_up_done ) %]
+              [% IF ( row.follow_up.follow_up_date ) %]
+                [% IF ( row.follow_up.done ) %]
                   [% 'Yes' | $T8 %]
                 [% ELSE %]
                   [% 'No' | $T8 %]
@@ -68,8 +59,9 @@
     </p>
   [% END %]
 
+
   <div class="listtop">
-    [% IF ( NOTE_id ) %]
+    [% IF ( SELF.note.id ) %]
       [% 'Edit note' | $T8 %]
     [% ELSE %]
       [% 'Add note' | $T8 %]
diff --git a/templates/webpages/delivery_term/form.html b/templates/webpages/delivery_term/form.html
new file mode 100755 (executable)
index 0000000..92d98c2
--- /dev/null
@@ -0,0 +1,44 @@
+[% USE HTML %][% USE T8 %][% USE L %][% USE LxERP %]
+
+ <form method="post" action="controller.pl">
+  <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+  <table>
+   <tr>
+    <td>[%- 'Description' | $T8 %]</td>
+    <td>
+     <input name="delivery_term.description" value="[%- HTML.escape(SELF.delivery_term.description) %]">
+    </td>
+   </tr>
+
+   <tr>
+    <td>[%- 'Long Description' | $T8 %]</td>
+    <td>
+     <input name="delivery_term.description_long" value="[%- HTML.escape(SELF.delivery_term.description_long) %]" size="60">
+    </td>
+   </tr>
+
+   [%- FOREACH language = SELF.languages %]
+    <tr>
+     <td>[%- HTML.escape(language.description) %] ([%- LxERP.t8('Translation') %])</td>
+     <td>
+      <input name="translation_[% language.id %]" value="[%- HTML.escape(SELF.delivery_term.translated_attribute('description_long', language, 1)) %]" size="60">
+     </td>
+    </tr>
+   [%- END %]
+
+  <p>
+   <input type="hidden" name="id" value="[% SELF.delivery_term.id %]">
+   <input type="hidden" name="action" value="DeliveryTerm/dispatch">
+   <input type="submit" class="submit" name="action_[% IF SELF.delivery_term.id %]update[% ELSE %]create[% END %]" value="[% 'Save' | $T8 %]">
+   [%- IF SELF.delivery_term.id %]
+    <input type="submit" class="submit" name="action_destroy" value="[% 'Delete' | $T8 %]"
+           onclick="if (confirm('[% 'Are you sure you want to delete this delivery term?' | $T8 %]')) return true; else return false;">
+   [%- END %]
+   <a href="[% SELF.url_for(action => 'list') %]">[%- 'Abort' | $T8 %]</a>
+  </p>
+
+ </form>
+
diff --git a/templates/webpages/delivery_term/list.html b/templates/webpages/delivery_term/list.html
new file mode 100644 (file)
index 0000000..b47f9d1
--- /dev/null
@@ -0,0 +1,45 @@
+[% USE HTML %][% USE T8 %][% USE L %][% USE LxERP %]
+
+ <div class="listtop">[% FORM.title %]</div>
+
+[%- INCLUDE 'common/flash.html' %]
+
+ <form method="post" action="controller.pl">
+  [% IF !DELIVERY_TERMS.size %]
+   <p>
+    [%- 'No delivery term has been created yet.' | $T8 %]
+   </p>
+
+  [%- ELSE %]
+   <table id="delivery_term_list">
+    <thead>
+    <tr class="listheading">
+     <th align="center"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></th>
+     <th>[%- 'Description' | $T8 %]</th>
+     <th>[%- 'Long Description' | $T8 %]</th>
+    </tr>
+    </thead>
+
+    <tbody>
+    [%- FOREACH delivery_term = DELIVERY_TERMS %]
+    <tr class="listrow[% loop.count % 2 %]" id="delivery_term_id_[% delivery_term.id %]">
+     <td align="center" class="dragdrop"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></td>
+     <td>
+      <a href="[% SELF.url_for(action => 'edit', id => delivery_term.id) %]">
+       [%- HTML.escape(delivery_term.description) %]
+      </a>
+     </td>
+     <td>[%- HTML.escape(delivery_term.description_long) %]</td>
+    </tr>
+    [%- END %]
+    </tbody>
+   </table>
+  [%- END %]
+
+  <p>
+   <a href="[% SELF.url_for(action => 'new') %]">[%- 'Create new delivery term' | $T8 %]</a>
+  </p>
+ </form>
+
+ [% L.sortable_element('#delivery_term_list tbody', url => 'controller.pl?action=DeliveryTerm/reorder', with => 'delivery_term_id') %]
+
index d02a1e52d6eeeb52881602cb7f21eb9f7a3f3005..eea92171aa8f12fc06b47883e50b6064b6bc67c3 100644 (file)
         <td><textarea name="notes" rows="[% LxERP.numtextrows(notes, 25, 8, 2) %]" cols="25" wrap="soft"[% RO %]>[% HTML.escape(notes) %]</textarea></td>
         <td><textarea name="intnotes" rows="[% LxERP.numtextrows(intnotes, 35, 8, 2) %]" cols="35" wrap="soft"[% RO %]>[% HTML.escape(intnotes) %]</textarea></td>
        </tr>
+
+       <tr>
+         <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+         <td>[% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description') %]</td>
+       </tr>
       </table>
 
      </td>
index 1f7dda684f776153a51ef1dc0c6acfe563e4d46f..95a2c8b85dd143dedef9c0582ce1d5afbeae31e1 100644 (file)
     </tr>
 
     <tr>
-     <th align="right">[% 'From' | $T8 %]</th>
+     <th align="right">[% 'Delivery Order Date' | $T8 %] [% 'From' | $T8 %]</th>
      <td>
       [% L.date_tag('transdatefrom') %]
      </td>
      </td>
     </tr>
 
+    <tr>
+     <th align="right">[% 'Reqdate' | $T8 %] [% 'From' | $T8 %]</th>
+     <td>
+      [% L.date_tag('reqdatefrom') %]
+     </td>
+     <th align="right">[% 'Bis' | $T8 %]</th>
+     <td>
+      [% L.date_tag('reqdateto') %]
+     </td>
+    </tr>
+
     <tr>
      <th align="right">[% 'Include in Report' | $T8 %]</th>
      <td colspan="5">
        <tr>
         <td>
          <input name="l_transdate" id="l_transdate" class="checkbox" type="checkbox" value="Y" checked>
-         <label for="l_transdate">[% 'Date' | $T8 %]</label>
+         <label for="l_transdate">[% 'Delivery Order Date' | $T8 %]</label>
         </td>
         <td>
          <input name="l_reqdate" id="l_reqdate" class="checkbox" type="checkbox" value="Y" checked>
index d73380e3d1149f2d724a20129103782edf8b5f43..ec175696d856e093e22aeee257c2d1db63fb0daa 100644 (file)
@@ -16,25 +16,15 @@ function updateTaxes(row)
   var taxkeyposition = taxkey.lastIndexOf(found[0]);
   var account = taxkey.substr(0, taxkeyposition);
 
-  var xmlhttp;
-  if (window.XMLHttpRequest)
-  {// code for IE7+, Firefox, Chrome, Opera, Safari
-    xmlhttp=new XMLHttpRequest();
-  }
-  else
-  {// code for IE6, IE5
-    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
-  }
-  xmlhttp.onreadystatechange=function()
-  {
-    if (xmlhttp.readyState==4 && xmlhttp.status==200)
-    {
-      var element = document.getElementById("taxchart_" + row);
-      element.innerHTML = xmlhttp.responseText;
-    }
-  }
-  xmlhttp.open("GET","gl.pl?action=get_tax_dropdown&accno=" + account + "&select_index=" + index,true);
-  xmlhttp.send();
+  $.ajax({
+    url: 'gl.pl?action=get_tax_dropdown',
+    data: { accno: account,
+            selected_index: index},
+    dataType: 'html',
+    success: function (new_html) {
+                                $("#taxchart_" + row).html(new_html);
+                              },
+  });
 };
 
   function copy_debit_to_credit() {
index 6d3b643392ee67a54eb178b5c11aa06fdd936b8d..e979a807ed323f6f574346b09b40ec8d879f41d7 100644 (file)
@@ -11,7 +11,7 @@
         <tr>
          <th align="left">[% 'Notes' | $T8 %]</th>
          <th align="left">[% 'Internal Notes' | $T8 %]</th>
-         <th align="left">[% 'Payment Options' | $T8 %]</th>
+         <th align="right">[% 'Payment / Delivery Options' | $T8 %]</th>
         </tr>
         <tr valign="top">
          <td>
           <textarea name="intnotes" rows="[% rows %]" cols="35" wrap="soft">[% intnotes %]</textarea>
          </td>
          <td>
-          [%- L.checkbox_tag('direct_debit', 'checked', direct_debit, 'label', LxERP.t8('direct debit')) %]
+           <table>
+             <tr>
+               <th align="right">[% 'Delivery Terms' | $T8 %] </th>
+               <td>
+                 [% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]
+               </td>
+             </tr>
+             <tr>
+               <th align="right">[% 'direct debit' | $T8 %]</th>
+               <td>
+                 [%- L.checkbox_tag('direct_debit', 'checked', direct_debit) %]
+               </td>
+             </tr>
+           </table>
          </td>
         </tr>
 [%- IF id && follow_ups.size %]
index 28fafaa09d1ef1049759b4a67f81d5412ce69924..45664520d663be31c1e3b1124f2323f4271e59e8 100644 (file)
@@ -11,7 +11,7 @@
         <tr>
          <th align="left">[% 'Notes (will appear on hard copy)' | $T8 %]</th>
          <th align="left">[% 'Internal Notes' | $T8 %]</th>
-         <th align="right">[% 'Payment Terms' | $T8 %]</th>
+         <th align="right">[% 'Payment / Delivery Options' | $T8 %]</th>
         </tr>
         <tr valign="top">
          <td>
           <textarea name="intnotes" rows="[% rows %]" cols="35">[% intnotes %]</textarea>
          </td>
          <td>
-            [%- INCLUDE 'generic/multibox.html'
-                 name          = 'payment_id',
-                 style         = 'width: 250px',
-                 DATA          = payment_terms,
-                 id_key        = 'id',
-                 label_key     = 'description',
-                 show_empty    = 1
-                 allow_textbox = 0 -%]
-          <script type='text/javascript'>$('#payment_id').change(function(){ if (this.value) set_duedate()})</script>
-          <br>
-          [%- L.checkbox_tag('direct_debit', 'checked', direct_debit, 'label', LxERP.t8('direct debit')) %]
+           <table>
+             <tr>
+               <th align="right">[% 'Payment Terms' | $T8 %]</th>
+               <td>
+                 [%- INCLUDE 'generic/multibox.html'
+                   name          = 'payment_id',
+                   style         = 'width: 250px',
+                   DATA          = payment_terms,
+                   id_key        = 'id',
+                   label_key     = 'description',
+                   show_empty    = 1
+                   allow_textbox = 0 -%]
+                 <script type='text/javascript'>$('#payment_id').change(function(){ if (this.value) set_duedate()})</script>
+               </td>
+             </tr>
+             <tr>
+               <th align="right">[% 'Delivery Terms' | $T8 %] </th>
+               <td>
+                 [% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]
+               </td>
+             </tr>
+             <tr>
+               <th align="right">[% 'direct debit' | $T8 %]</th>
+               <td>
+                 [%- L.checkbox_tag('direct_debit', 'checked', direct_debit) %]
+               </td>
+             </tr>
+           </table>
          </td>
         </tr>
 [%- IF id && follow_ups.size %]
index c9281ac66d4f8a406f44571098565749eeeba1aa..7c72a452e52eb150f6260a7abfc800aaafa40262 100644 (file)
                            label_key  = 'description',
                            show_empty = 1 -%]
                 </td>
-            </tr>
+              </tr>
+              <tr>
+                <th align="right">[% 'Delivery Terms' | $T8 %]</th>
+                <td>[% L.select_tag('delivery_term_id', ALL_DELIVERY_TERMS, default = delivery_term_id, with_empty = 1, title_key = 'description', style = 'width: 250px') %]</td>
+              </tr>
 
 [%- IF is_sales_ord %]
             <tr>
index c71e60f6d2bd3ca1b6e6ccc56887cc08eda18afb..8d76ecb5986ccab7d90deffc9e1c0eb1e2eb7738 100644 (file)
      <th align="right">[% 'Salesman' | $T8 %]</th>
      <td>[% L.select_tag('salesman_id', ALL_EMPLOYEES, title_key='safe_name', with_empty=1, style='width:250px') %]</td>
     </tr>
+    <tr>
+     <th align="right">[% 'Steuersatz' | $T8 %]</th>
+     <td>[% L.select_tag('taxzone_id', ALL_TAXZONES, with_empty=1, title_key='description', style='width: 250px') %]</td>
+    </tr>
+    <tr>
+     <th align="right">[% 'Shipping Point' | $T8 %]</th>
+     <td colspan="3">[% L.input_tag('shippingpoint', '', style='width:250px') %]</td>
+    </tr>
     <tr>
      <th align="right">[% 'Transaction description' | $T8 %]</th>
      <td colspan="3"><input name="transaction_description" style="width: 250px"></td>
          <input name="l_customernumber" id="l_customernumber" class="checkbox" type="checkbox" value="Y">
          <label for="l_customernumber">[% 'Customer Number' | $T8 %]</label>
         </td>
+        <td>[%- L.checkbox_tag('l_taxzone',       label => LxERP.t8('Steuersatz'))     %]</td>
+        <td>[%- L.checkbox_tag('l_shippingpoint', label => LxERP.t8('Shipping Point')) %]</td>
        </tr>
        <tr>
         <td>