]> wagnertech.de Git - kivitendo-erp.git/commitdiff
Merge branch 'master' into currency
authorNiclas Zimmermann <niclas@kivitendo-premium.de>
Thu, 2 May 2013 08:55:07 +0000 (10:55 +0200)
committerNiclas Zimmermann <niclas@kivitendo-premium.de>
Thu, 2 May 2013 08:55:07 +0000 (10:55 +0200)
Conflicts:
locale/de/all

152 files changed:
.gitignore
SL/AM.pm
SL/BackgroundJob/CreatePeriodicInvoices.pm
SL/ClientJS.pm
SL/Common.pm
SL/Controller/Base.pm
SL/Controller/CsvImport/Base.pm
SL/Controller/CsvImport/Contact.pm
SL/Controller/CsvImport/CustomerVendor.pm
SL/Controller/RecordLinks.pm
SL/DATEV.pm
SL/DB/DeliveryOrder.pm
SL/DB/Helper/FlattenToForm.pm
SL/DB/Helper/Mappings.pm
SL/DB/Helper/TransNumberGenerator.pm
SL/DB/Invoice.pm
SL/DB/MetaSetup/Default.pm
SL/DB/Object.pm
SL/DB/Object/Hooks.pm
SL/DB/Order.pm
SL/DB/PurchaseInvoice.pm
SL/DB/SepaExportItem.pm
SL/DBUpgrade2.pm
SL/DBUpgrade2/Base.pm [new file with mode: 0644]
SL/DO.pm
SL/Form.pm
SL/GL.pm
SL/Git.pm
SL/Helper/DateTime.pm
SL/IC.pm
SL/IR.pm
SL/IS.pm
SL/Layout/Base.pm
SL/Layout/Dispatcher.pm
SL/Layout/Javascript.pm
SL/Layout/MenuLeft.pm
SL/Layout/None.pm
SL/Layout/Top.pm
SL/Layout/V3.pm
SL/OE.pm
SL/PrefixedNumber.pm [new file with mode: 0644]
SL/Presenter.pm
SL/Presenter/Record.pm
SL/Presenter/SepaExport.pm [new file with mode: 0644]
SL/Presenter/Tag.pm
SL/Presenter/Text.pm
SL/RP.pm
SL/ReportGenerator.pm
SL/Request.pm
SL/Taxkeys.pm
SL/Template/LaTeX.pm
SL/Template/OpenDocument.pm
SL/Template/Plugin/L.pm
SL/Template/Plugin/LxLatex.pm [new file with mode: 0644]
SL/Template/Simple.pm
SL/TransNumber.pm
SL/Util.pm [new file with mode: 0644]
SL/X.pm
bin/mozilla/am.pl
bin/mozilla/ct.pl
bin/mozilla/do.pl
bin/mozilla/gl.pl
bin/mozilla/ic.pl
bin/mozilla/io.pl
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
bin/mozilla/rc.pl
css/kivitendo/jqModal.css [new file with mode: 0644]
css/kivitendo/main.css
css/lx-office-erp/jqModal.css [new file with mode: 0644]
css/lx-office-erp/main.css
css/presenter/record/record_list.css [deleted file]
doc/dokumentation.xml
doc/html/ch04s03.html
doc/html/index.html
doc/kivitendo-Dokumentation.pdf
js/client_js.js
js/common.js
js/jqModal/jqModal.js
js/jquery.download.js [new file with mode: 0644]
locale/de/all [changed mode: 0644->0755]
scripts/dbconnect.pl [new file with mode: 0755]
scripts/generate_client_js_actions.tpl
scripts/locales.pl
sql/Pg-upgrade2-auth/auth_schema_normalization_1.pl
sql/Pg-upgrade2/SKR04-3804-addition.pl
sql/Pg-upgrade2/USTVA_abstraction.pl
sql/Pg-upgrade2/USTVA_at.pl
sql/Pg-upgrade2/acc_trans_booleans_not_null.sql [new file with mode: 0644]
sql/Pg-upgrade2/acc_trans_constraints.pl
sql/Pg-upgrade2/acc_trans_id_uniqueness.pl
sql/Pg-upgrade2/add_more_constraints_fibu_projekt_xplace.pl
sql/Pg-upgrade2/ap_deliverydate.sql [new file with mode: 0644]
sql/Pg-upgrade2/auth_enable_ct_all_edit.pl
sql/Pg-upgrade2/auth_enable_edit_prices.pl
sql/Pg-upgrade2/auth_enable_sales_all_edit.pl
sql/Pg-upgrade2/background_job_change_create_periodic_invoices_to_daily.pl [new file with mode: 0644]
sql/Pg-upgrade2/background_jobs_3.pl [changed mode: 0644->0755]
sql/Pg-upgrade2/charts_without_taxkey.pl
sql/Pg-upgrade2/contacts_add_cp_position.pl
sql/Pg-upgrade2/contacts_add_street_and_zipcode_and_city.pl
sql/Pg-upgrade2/contacts_convert_cp_birthday_to_date.pl
sql/Pg-upgrade2/cp_greeting_migration.pl
sql/Pg-upgrade2/defaults_datev_check.pl
sql/Pg-upgrade2/defaults_posting_config.pl
sql/Pg-upgrade2/defaults_show_bestbefore.pl
sql/Pg-upgrade2/emmvee_background_jobs_2.pl
sql/Pg-upgrade2/erzeugnisnummern.pl [new file with mode: 0644]
sql/Pg-upgrade2/finanzamt_update_fa_bufa_nr_hamburg.pl
sql/Pg-upgrade2/fix_acc_trans_ap_taxkey_bug.pl
sql/Pg-upgrade2/globalprojectnumber_ap_ar_oe.pl
sql/Pg-upgrade2/periodic_invoices_background_job.pl
sql/Pg-upgrade2/rundungsfehler_korrigieren_BUG1328.pl
sql/Pg-upgrade2/self_test_background_job.pl
sql/Pg-upgrade2/steuerfilterung.pl [new file with mode: 0644]
sql/Pg-upgrade2/tax_constraints.pl
sql/Pg-upgrade2/umstellung_eur.pl
sql/Pg-upgrade2/warehouse.pl
t/007broken_links.t
t/common.t [new file with mode: 0644]
t/controllers/base/render.t
t/db_helper/acts_as_list.t
t/helper/camelify.t [new file with mode: 0644]
t/helper/hashify.t [new file with mode: 0644]
t/helper/snakify.t [new file with mode: 0644]
t/prefixed_number.t [new file with mode: 0644]
templates/webpages/am/edit_defaults.html
templates/webpages/am/edit_tax.html
templates/webpages/ap/form_header.html
templates/webpages/ar/form_header.html
templates/webpages/csv_import/_deferred_report.html
templates/webpages/csv_import/_form_contacts.html [new file with mode: 0644]
templates/webpages/csv_import/_form_customers_vendors.html
templates/webpages/csv_import/form.html
templates/webpages/ct/_contact.html
templates/webpages/ct/form_header.html
templates/webpages/ct/search_contact.html
templates/webpages/dbupgrade/erzeugnisnummern.html [new file with mode: 0644]
templates/webpages/dbupgrade/steuerfilterung.html [new file with mode: 0644]
templates/webpages/gl/form_header.html
templates/webpages/gl/search.html
templates/webpages/gl/update_tax_accounts.html [new file with mode: 0644]
templates/webpages/oe/edit_periodic_invoices_config.html
templates/webpages/presenter/record/grouped_record_list.html
templates/webpages/rc/step2.html
templates/webpages/record_links/add_filter.html
templates/webpages/rp/balance_sheet.html
templates/webpages/rp/report.html
templates/webpages/sepa/bank_transfer_add.html
templates/webpages/sepa/bank_transfer_create.html
templates/webpages/vk/search_invoice.html

index 44752df300346db40567cd4158b539590c40835b..ff8e2a20f0fac9286f144da7c73ab4ee5fc64ecd 100644 (file)
@@ -5,6 +5,7 @@ crm
 /users/session_files/
 /users/pid/
 /users/.cache/
+/users/kivitendo-print*
 /config/lx_office.conf
 /config/kivitendo.conf
 /doc/online/*/*.html
index 86b29d19633318023f1393f4d8f27522cf948a5a..e4727047cb48692ac6a0cf75810f6bdb99d4d964 100644 (file)
--- a/SL/AM.pm
+++ b/SL/AM.pm
@@ -213,11 +213,8 @@ sub save_account {
       qw(AR_amount AR_tax AR_paid AP_amount AP_tax AP_paid IC_sale IC_cogs IC_taxpart IC_income IC_expense IC_taxservice);
   }
 
-  if ($form->{AR_include_in_dropdown}) {
-    $form->{$form->{AR_include_in_dropdown}} = $form->{AR_include_in_dropdown};
-  }
-  if ($form->{AP_include_in_dropdown}) {
-    $form->{$form->{AP_include_in_dropdown}} = $form->{AP_include_in_dropdown};
+  for (qw(AR_include_in_dropdown AP_include_in_dropdown)) {
+    $form->{$form->{$_}} = $form->{$_} if $form->{$_};
   }
 
   $form->{link} = "";
@@ -265,8 +262,8 @@ sub save_account {
   if (!$form->{id} || $form->{id} eq "") {
     $query = qq|SELECT nextval('id')|;
     ($form->{"id"}) = selectrow_query($form, $dbh, $query);
-    $query = qq|INSERT INTO chart (id, accno) VALUES (?, ?)|;
-    do_query($form, $dbh, $query, $form->{"id"}, $form->{"accno"});
+    $query = qq|INSERT INTO chart (id, accno, link) VALUES (?, ?, ?)|;
+    do_query($form, $dbh, $query, $form->{"id"}, $form->{"accno"}, '');
   }
 
   @values = ();
@@ -1083,6 +1080,7 @@ sub save_defaults {
         vendornumber       = ?,
         articlenumber      = ?,
         servicenumber      = ?,
+        assemblynumber     = ?,
         sdonumber          = ?,
         pdonumber          = ?,
         businessnumber     = ?,
@@ -1095,6 +1093,7 @@ sub save_defaults {
                 $form->{sqnumber},        $form->{rfqnumber},
                 $form->{customernumber},  $form->{vendornumber},
                 $form->{articlenumber},   $form->{servicenumber},
+                $form->{assemblynumber},
                 $form->{sdonumber},       $form->{pdonumber},
                 $form->{businessnumber},  $form->{weightunit},
                 conv_i($form->{language_id}));
@@ -1744,7 +1743,7 @@ sub taxes {
                    (SELECT accno FROM chart WHERE id = chart_id) AS taxnumber,
                    (SELECT description FROM chart WHERE id = chart_id) AS account_description
                  FROM tax t
-                 ORDER BY taxkey|;
+                 ORDER BY taxkey, rate|;
 
   my $sth = $dbh->prepare($query);
   $sth->execute || $form->dberror($query);
@@ -1804,6 +1803,7 @@ sub get_tax {
                    taxdescription,
                    round(rate * 100, 2) AS rate,
                    chart_id,
+                   chart_categories,
                    (id IN (SELECT tax_id
                            FROM acc_trans)) AS tax_already_used
                  FROM tax
@@ -1861,14 +1861,23 @@ sub save_tax {
 
   $form->{rate} = $form->{rate} / 100;
 
-  my @values = ($form->{taxkey}, $form->{taxdescription}, $form->{rate}, $form->{chart_id}, $form->{chart_id} );
+  my $chart_categories = '';
+  $chart_categories .= 'A' if $form->{asset};
+  $chart_categories .= 'L' if $form->{liability};
+  $chart_categories .= 'Q' if $form->{equity};
+  $chart_categories .= 'I' if $form->{revenue};
+  $chart_categories .= 'E' if $form->{expense};
+  $chart_categories .= 'C' if $form->{costs};
+
+  my @values = ($form->{taxkey}, $form->{taxdescription}, $form->{rate}, $form->{chart_id}, $form->{chart_id}, $chart_categories);
   if ($form->{id} ne "") {
     $query = qq|UPDATE tax SET
                   taxkey         = ?,
                   taxdescription = ?,
                   rate           = ?,
                   chart_id       = ?,
-                  taxnumber      = (SELECT accno FROM chart WHERE id= ? )
+                  taxnumber      = (SELECT accno FROM chart WHERE id= ? ),
+                  chart_categories = ?
                 WHERE id = ?|;
     push(@values, $form->{id});
 
@@ -1879,9 +1888,10 @@ sub save_tax {
                   taxdescription,
                   rate,
                   chart_id,
-                  taxnumber
+                  taxnumber,
+                  chart_categories
                 )
-                VALUES (?, ?, ?, ?, (SELECT accno FROM chart WHERE id = ?) )|;
+                VALUES (?, ?, ?, ?, (SELECT accno FROM chart WHERE id = ?), ? )|;
   }
   do_query($form, $dbh, $query, @values);
 
index 474762d86c1b596c699315c36e531d93be53eb08..a316c3afa1256bd3c49c0a7b6877e550cef9bd4d 100644 (file)
@@ -64,9 +64,9 @@ sub run {
 }
 
 sub _log_msg {
-  my $message  = join('', @_);
-  $message    .= "\n" unless $message =~ m/\n$/;
-  # $::lxdebug->message(0, $message);
+  my $message  = join('', @_);
+  $message    .= "\n" unless $message =~ m/\n$/;
+  $::lxdebug->message(LXDebug::DEBUG1(), $message);
 }
 
 sub _generate_time_period_variables {
@@ -232,7 +232,8 @@ sub _print_invoice {
   $form->{formname}     = $form->{type};
   $form->{format}       = 'pdf';
   $form->{media}        = 'printer';
-  $form->{OUT}          = "| " . $config->printer->printer_command;
+  $form->{OUT}          = $config->printer->printer_command;
+  $form->{OUT_MODE}     = '|-';
 
   $form->prepare_for_printing;
 
index 51f663fa52c50b7db1941c0d94037c0cdbdcdff2..a04702eb34932dbb95a2ca7580bf6ce1ee92c2f2 100644 (file)
@@ -58,6 +58,11 @@ my %supported_methods = (
   removeProp   => 2,
   val          => 2,
 
+  # Class attribute
+  addClass     => 2,
+  removeClass  => 2,
+  toggleClass  => 2,
+
   # Data storage
   data         => 3,
   removeData   => 2,
@@ -65,6 +70,11 @@ my %supported_methods = (
   # Form Events
   focus        => 1,
 
+  # ## jqModal plugin ##
+
+  # Closing and removing the popup
+  jqmClose               => 1,
+
   # ## jstree plugin ## pattern: $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>)
 
   # Operations on the whole tree
@@ -90,6 +100,9 @@ my %supported_methods = (
   'jstree:select_node'   => 2,  # $.jstree._reference($(<TARGET>)).<FUNCTION>(<ARGS>, true)
   'jstree:deselect_node' => 2,
   'jstree:deselect_all'  => 1,
+
+  # ## other stuff ##
+  redirect_to            => 1,  # window.location.href = <TARGET>
 );
 
 sub AUTOLOAD {
@@ -123,6 +136,12 @@ sub action {
   return $self;
 }
 
+sub action_if {
+  my ($self, $condition, @args) = @_;
+
+  return $condition ? $self->action(@args) : $self;
+}
+
 sub init__actions {
   return [];
 }
@@ -338,7 +357,38 @@ Instead of:
 
 The first variation is obviously better suited for chaining.
 
-Additional functions:
+=over 4
+
+=item C<action $method, @args>
+
+Call the function with the name C<$method> on C<$self> with arguments
+C<@args>. Returns the return value of the actual function
+called. Useful for chaining (see above).
+
+=item C<action_if $condition, $method, @args>
+
+Call the function with the name C<$method> on C<$self> with arguments
+C<@args> if C<$condition> is trueish. Does nothing otherwise.
+
+Returns the return value of the actual function called if
+C<$condition> is trueish and C<$self> otherwise. Useful for chaining
+(see above).
+
+This function is equivalent to the following:
+
+  if ($condition) {
+    $obj->$method(@args);
+  }
+
+But it is easier to integrate into a method call chain, e.g.:
+
+  $js->html('#content', $html)
+     ->action_if($item->is_flagged, 'toggleClass', '#marker', 'flagged')
+     ->render($self);
+
+=back
+
+=head2 ADDITIONAL FUNCTIONS
 
 =over 4
 
@@ -347,8 +397,8 @@ Additional functions:
 Display a C<$message> in the flash of type C<$type>. Multiple calls of
 C<flash> on the same C<$self> will be merged by type.
 
-On the client side the flash of this type will be cleared before the
-message is shown.
+On the client side the flashes of all types will be cleared after each
+successful ClientJS call that did not end with C<$js-E<gt>error(...)>.
 
 =item C<error $message>
 
@@ -359,6 +409,14 @@ client will then show the message in the 'error' flash.
 The messages of multiple calls of C<error> on the same C<$self> will
 be merged.
 
+=item C<redirect_to $url>
+
+Redirects the browser window to the new URL by setting the JavaScript
+property C<window.location.href>. Note that
+L<SL::Controller::Base/redirect_to> is AJAX aware and uses this
+function if the current request is an AJAX request as determined by
+L<SL::Request/is_ajax>.
+
 =back
 
 =head2 JQUERY FUNCTIONS
index 159609d30960382fc505aa0c9402dbe1ea5026ac..ff6c7218abb0f302d845093d41a5913674e69758 100644 (file)
@@ -46,6 +46,21 @@ sub tmpname {
   return "/tmp/kivitendo-tmp-" . unique_id();
 }
 
+sub truncate {
+  my ($text, %params) = @_;
+
+  $params{at}       //= 50;
+  $params{at}         =  3 if 3 > $params{at};
+
+  $params{strip}    //= '';
+
+  $text =~ s/[\r\n]+$//g if $params{strip} =~ m/^(?: 1 | newlines? | full )$/x;
+  $text =~ s/[\r\n]+/ /g if $params{strip} =~ m/^(?:     newlines? | full )$/x;
+
+  return $text if length($text) <= $params{at};
+  return substr($text, 0, $params{at} - 3) . '...';
+}
+
 sub retrieve_parts {
   $main::lxdebug->enter_sub();
 
@@ -576,3 +591,46 @@ sub check_params_x {
 }
 
 1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+Common - Common routines used in a lot of places.
+
+=head1 SYNOPSIS
+
+  my $short_text = Common::truncate($long_text, at => 10);
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<truncate $text, %params>
+
+Truncates C<$text> at a position and insert an ellipsis if the text is
+longer. The maximum number of characters to return is given with the
+paramter C<at> which defaults to 50.
+
+The optional parameter C<strip> can be used to remove unwanted line
+feed/carriage return characters from the text before truncation. It
+can be set to C<1> (only strip those at the end of C<$text>) or
+C<full> (replace consecutive line feed/carriage return characters in
+the middle by a single space and remove tailing line feed/carriage
+return characters).
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>,
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
index 876aac30fdfb5f92f6c847dc95371a036987d5ec..17a1d61d76358e66524aeadaf4a864c8f37c0993 100644 (file)
@@ -52,6 +52,8 @@ sub redirect_to {
     SL::Helper::Flash::delay_flash();
   }
 
+  return $self->render(SL::ClientJS->new->redirect_to($self->url_for(@_))) if $::request->is_ajax;
+
   print $::request->{cgi}->redirect($url);
 }
 
@@ -80,7 +82,7 @@ sub render {
   }
 
   # Only certain types are supported.
-  croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json)$/;
+  croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json|text)$/;
 
   # The "template" argument must be a string or a reference to one.
   $template = ${ $template }                                       if ((ref($template) || '') eq 'REF') && (ref(${ $template }) eq 'SL::Presenter::EscapedText');
@@ -110,6 +112,7 @@ sub render {
       $::form->{header} = 1;
       my $content_type  = $options->{type} eq 'html' ? 'text/html'
                         : $options->{type} eq 'js'   ? 'text/javascript'
+                        : $options->{type} eq 'text' ? 'text/plain'
                         :                              'application/json';
 
       print $::form->create_http_response(content_type => $content_type,
@@ -399,11 +402,12 @@ C<process> = 1, C<output> = 1, C<header> = 1, C<layout> = 1):
 
 =item C<type>
 
-The template type. Can be C<html> (the default), C<js> for JavaScript
-or C<json> for JSON content. Affects the extension that's added to the
-file name given with a non-reference C<$template> argument, the
-content type HTTP header that is output and whether or not the layout
-will be output as well (see description of C<layout> below).
+The template type. Can be C<html> (the default), C<js> for JavaScript,
+C<json> for JSON and C<text> for plain text content. Affects the
+extension that's added to the file name given with a non-reference
+C<$template> argument, the content type HTTP header that is output and
+whether or not the layout will be output as well (see description of
+C<layout> below).
 
 =item C<process>
 
@@ -500,9 +504,15 @@ Usage from a template might look like this:
 
 =item C<redirect_to %url_params>
 
-Redirects the browser to a new URL by outputting a HTTP redirect
-header. The URL is generated by calling L</url_for> with
-C<%url_params>.
+Redirects the browser to a new URL. The URL is generated by calling
+L</url_for> with C<%url_params>.
+
+This function implements the redirection depending on whether or not
+the current request is an AJAX request as determined by
+L<SL::Request/is_ajax>. If it is a normal request then it outputs a
+standard HTTP redirect header (HTTP code 302). If it is an AJAX
+request then it outputs an AJAX response suitable for the
+C<eval_json_result> function from the L<SL::ClientJS> module.
 
 =item C<run_before $sub, %params>
 
index fa3e4f43e0c848989296fd011f4775524dcf5d30..7f4326a07141f644e0821f6aba5d72aae1f29036 100644 (file)
@@ -153,6 +153,10 @@ sub init_all_vc {
            vendors   => SL::DB::Manager::Vendor->get_all };
 }
 
+sub force_allow_columns {
+  return ();
+}
+
 sub init_vc_by {
   my ($self)    = @_;
 
@@ -222,6 +226,8 @@ sub init_profile {
   eval "require " . $self->class;
 
   my %unwanted = map { ( $_ => 1 ) } (qw(itime mtime), map { $_->name } @{ $self->class->meta->primary_key_columns });
+  delete $unwanted{$_} for ($self->force_allow_columns);
+
   my %profile;
   for my $col ($self->class->meta->columns) {
     next if $unwanted{$col};
@@ -422,5 +428,20 @@ sub fix_field_lengths {
   }
 }
 
-1;
+sub clean_fields {
+  my ($self, $illegal_chars, $object, @fields) = @_;
+
+  my @cleaned_fields;
+  foreach my $field (grep { $object->can($_) } @fields) {
+    my $value = $object->$field;
 
+    next unless defined($value) && ($value =~ s/$illegal_chars/ /g);
+
+    $object->$field($value);
+    push @cleaned_fields, $field;
+  }
+
+  return @cleaned_fields;
+}
+
+1;
index 193f87d8e4f02650819e5e5cf3b2a25d74d5d6ee..2eb877e97f2ef8a94bd9b8716fb63779368caf4f 100644 (file)
@@ -24,20 +24,64 @@ sub init_all_cvar_configs {
   return SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'Contacts' ]);
 }
 
+sub force_allow_columns {
+  return qw(cp_id);
+}
+
 sub check_objects {
   my ($self) = @_;
 
   $self->controller->track_progress(phase => 'building data', progress => 0);
 
-  my $i;
-  my $num_data = scalar @{ $self->controller->data };
+  my $i              = 0;
+  my $num_data       = scalar @{ $self->controller->data };
+  my $update_policy  = $self->controller->profile->get('update_policy') || 'update_existing';
+  my %contacts_by_id = map { ( $_->cp_id => $_ ) } @{ $self->existing_objects };
+  my $methods        = $self->controller->headers->{methods};
+  my %used_methods   = map { ( $_ => 1 ) } @{ $methods };
+
   foreach my $entry (@{ $self->controller->data }) {
     $self->controller->track_progress(progress => $i/$num_data * 100) if $i % 100 == 0;
 
+    my $object = $entry->{object};
+    if ($object->cp_id) {
+      my $existing_contact = $contacts_by_id{ $object->cp_id };
+      if (!$existing_contact) {
+        $contacts_by_id{ $object->cp_id } = $object if $object->cp_id;
+
+      } elsif ($update_policy eq 'skip') {
+        push(@{ $entry->{errors} }, $::locale->text('Skipping due to existing entry in database'));
+        next;
+
+      } elsif ($update_policy eq 'update_existing') {
+        # Update existing customer/vendor records.
+        $entry->{object_to_save} = $existing_contact;
+
+        $object->cp_cv_id($existing_contact->cp_cv_id);
+
+        foreach (qw(cp_name cp_gender)) {
+          $object->$_($existing_contact->$_) if !$object->$_;
+        }
+
+        $existing_contact->$_( $entry->{object}->$_ ) for @{ $methods };
+
+        push @{ $entry->{information} }, $::locale->text('Updating existing entry in database');
+
+      } else {
+        $object->cp_id(undef);
+      }
+    }
+
     $self->check_name($entry);
     $self->check_vc($entry, 'cp_cv_id');
     $self->check_gender($entry);
     $self->handle_cvars($entry);
+
+    my @cleaned_fields = $self->clean_fields(qr{[\r\n]}, $object, qw(cp_title cp_givenname cp_name cp_email cp_phone1 cp_phone2 cp_fax cp_mobile1 cp_mobile2 cp_satphone cp_satfax
+                                                                     cp_privatphone cp_privatemail cp_abteilung cp_street cp_zipcode cp_city cp_position));
+
+    push @{ $entry->{information} }, $::locale->text('Illegal characters have been removed from the following fields: #1', join(', ', @cleaned_fields))
+      if @cleaned_fields;
   } continue {
     $i++;
   }
@@ -105,6 +149,7 @@ sub setup_displayable_columns {
                                  { name => 'cp_fax',         description => $::locale->text('Fax')                           },
                                  { name => 'cp_gender',      description => $::locale->text('Gender')                        },
                                  { name => 'cp_givenname',   description => $::locale->text('Given Name')                    },
+                                 { name => 'cp_id',          description => $::locale->text('Database ID')                   },
                                  { name => 'cp_mobile1',     description => $::locale->text('Mobile1')                       },
                                  { name => 'cp_mobile2',     description => $::locale->text('Mobile2')                       },
                                  { name => 'cp_name',        description => $::locale->text('Name')                          },
index 757c52c5d64dbe468bb8f574e9ba5cdba4f219ad..96eb69691ae30d3800364e26fa26467675124235 100644 (file)
@@ -49,8 +49,11 @@ sub check_objects {
 
   $self->controller->track_progress(phase => 'building data', progress => 0);
 
-  my $numbercolumn  = $self->controller->profile->get('table') . "number";
-  my %vcs_by_number = map { ( $_->$numbercolumn => 1 ) } @{ $self->existing_objects };
+  my $vc            = $self->controller->profile->get('table');
+  my $update_policy = $self->controller->profile->get('update_policy') || 'update_existing';
+  my $numbercolumn  = "${vc}number";
+  my %vcs_by_number = map { ( $_->$numbercolumn => $_ ) } @{ $self->existing_objects };
+  my $methods       = $self->controller->headers->{methods};
 
   my $i;
   my $num_data = scalar @{ $self->controller->data };
@@ -66,10 +69,29 @@ sub check_objects {
 
     next if @{ $entry->{errors} };
 
-    if ($vcs_by_number{ $object->$numbercolumn }) {
-      $entry->{object}->$numbercolumn('####');
+    my @cleaned_fields = $self->clean_fields(qr{[\r\n]}, $object, qw(name department_1 department_2 street zipcode city country contact phone fax homepage email cc bcc
+                                                                     taxnumber account_number bank_code bank username greeting));
+
+    push @{ $entry->{information} }, $::locale->text('Illegal characters have been removed from the following fields: #1', join(', ', @cleaned_fields))
+      if @cleaned_fields;
+
+    my $existing_vc = $vcs_by_number{ $object->$numbercolumn };
+    if (!$existing_vc) {
+      $vcs_by_number{ $object->$numbercolumn } = $object if $object->$numbercolumn;
+
+    } elsif ($update_policy eq 'skip') {
+      push(@{$entry->{errors}}, $::locale->text('Skipping due to existing entry in database'));
+
+    } elsif ($update_policy eq 'update_existing') {
+      # Update existing customer/vendor records.
+      $entry->{object_to_save} = $existing_vc;
+
+      $existing_vc->$_( $entry->{object}->$_ ) for @{ $methods };
+
+      push @{ $entry->{information} }, $::locale->text('Updating existing entry in database');
+
     } else {
-      $vcs_by_number{ $object->$numbercolumn } = $object;
+      $object->$numbercolumn('####');
     }
   } continue {
     $i++;
@@ -139,7 +161,7 @@ sub check_business {
   my $object = $entry->{object};
 
   # Check whether or not business ID is valid.
-  if ($object->business_id && !$self->businesss_by->{id}->{ $object->business_id }) {
+  if ($object->business_id && !$self->businesses_by->{id}->{ $object->business_id }) {
     push @{ $entry->{errors} }, $::locale->text('Error: Invalid business');
     return 0;
   }
index de0377577f7fb59a1a9dcbb83f3e8ef025931134..8d6adc7354414b75491f1de1b0a1a53213aef522 100644 (file)
@@ -44,6 +44,7 @@ sub action_ajax_list {
 
   eval {
     my $linked_records = $self->object->linked_records(direction => 'both');
+    push @{ $linked_records }, $self->object->sepa_export_items if $self->object->can('sepa_export_items');
     my $output         = SL::Presenter->get->grouped_record_list(
       $linked_records,
       with_columns      => [ qw(record_link_direction) ],
index 21e192a085f32bb8d374f8a1ea0a2d019200abde..406f09d76be5cc9ae04b8227be49104e8530782d 100644 (file)
@@ -353,7 +353,7 @@ sub _get_transactions {
 
   my $query    =
     qq|SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey,
-         ar.invnumber, ar.duedate, ar.amount as umsatz,
+         ar.invnumber, ar.duedate, ar.amount as umsatz, ar.deliverydate,
          ct.name,
          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link,
          ar.invoice
@@ -368,7 +368,7 @@ sub _get_transactions {
        UNION ALL
 
        SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey,
-         ap.invnumber, ap.duedate, ap.amount as umsatz,
+         ap.invnumber, ap.duedate, ap.amount as umsatz, ap.deliverydate,
          ct.name,
          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link,
          ap.invoice
@@ -383,7 +383,7 @@ sub _get_transactions {
        UNION ALL
 
        SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey,
-         gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz,
+         gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz, NULL as deliverydate,
          gl.description AS name,
          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, ac.chart_link AS link,
          FALSE AS invoice
@@ -528,7 +528,8 @@ sub _get_transactions {
         push @{ $self->{DATEV} }, [ \%new_trans, $trans->[$j] ];
 
       } elsif (($j != $notsplitindex) && !$trans->[$j]->{is_tax}) {
-        my %tax_info = $taxkeys->get_full_tax_info('transdate' => $trans->[$j]->{transdate});
+        my %tax_info = $taxkeys->get_full_tax_info('transdate' => $trans->[$j]->{transdate},
+                                                   'deliverydate' => $trans->[$j]->{deliverydate});
 
         my %new_trans = ();
         map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
index ca36cbda72c3381e823680d5bb859c9960db7126..8e235fc35febb69695bcfb8602fcfbdb3e5ad4ea 100644 (file)
@@ -51,4 +51,8 @@ sub displayable_state {
     ($self->delivered ? $::locale->text('delivered') : $::locale->text('not delivered'));
 }
 
+sub date {
+  goto &transdate;
+}
+
 1;
index 5af4bc3b1930c93ae40810d9b715980f1ba71387..d5805b0c06bf6fa1e145f4b5ec202e3c2d5185ce 100644 (file)
@@ -18,6 +18,7 @@ sub flatten_to_form {
                                     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));
+  $form->{currency} = $form->{curr}; # curr is called currency in almost all forms
 
   if (_has($self, 'transdate')) {
     my $transdate_idx = ref($self) eq 'SL::DB::Order'   ? ($self->quotation ? 'quodate' : 'orddate')
@@ -39,8 +40,8 @@ sub flatten_to_form {
   _copy($self->contact,                 $form, '',              '', 0, grep { /^cp_/    } map { $_->name } SL::DB::Contact->meta->columns) if _has($self, 'cp_id');
   _copy($self->shipto,                  $form, '',              '', 0, grep { /^shipto/ } map { $_->name } SL::DB::Shipto->meta->columns)  if _has($self, 'shipto_id');
   _copy($self->globalproject,           $form, 'globalproject', '', 0, qw(number description))                                             if _has($self, 'globalproject_id');
-  _copy($self->employee,                $form, 'employee',      '', 0, map { $_->name } SL::DB::Employee->meta->columns)                   if _has($self, 'employee_id');
-  _copy($self->salesman,                $form, 'salesman',      '', 0, map { $_->name } SL::DB::Employee->meta->columns)                   if _has($self, 'salesman_id');
+  _copy($self->employee,                $form, 'employee_',     '', 0, map { $_->name } SL::DB::Employee->meta->columns)                   if _has($self, 'employee_id');
+  _copy($self->salesman,                $form, 'salesman_',     '', 0, map { $_->name } SL::DB::Employee->meta->columns)                   if _has($self, 'salesman_id');
   _copy($self->acceptance_confirmed_by, $form, 'acceptance_confirmed_by_', '', 0, map { $_->name } SL::DB::Employee->meta->columns)        if _has($self, 'acceptance_confirmed_by_id');
 
   $form->{employee}   = $self->employee->name          if _has($self, 'employee_id');
index 35845a8eaf3597f579d4f387219097b71f0746a8..cc4d2abe161f192bb530c01d7943854c3c827de6 100644 (file)
@@ -3,6 +3,8 @@ package SL::DB::Helper::Mappings;
 use utf8;
 use strict;
 
+use SL::Util qw(camelify);
+
 require Exporter;
 our @ISA       = qw(Exporter);
 our @EXPORT_OK = qw(get_table_for_package get_package_for_table get_package_names);
@@ -157,18 +159,6 @@ sub db {
   die "Can't resolve '$string' as a database model, sorry. Did you perhaps forgot to load it?";
 }
 
-sub camelify {
-  my ($str) = @_;
-  $str =~ s/_+(.)/uc($1)/ge;
-  ucfirst $str;
-}
-
-sub snakify {
-  my ($str) = @_;
-  $str =~ s/(?<!^)\u(.)/'_' . lc($1)/ge;
-  lcfirst $str;
-}
-
 sub plurify {
   my ($str) = @_;
   $str . 's';
index 4b4fe126d50ad3547b75135af5d630ee2532edc9..d790b34847ef68a01d9622590d557c61b28272ea 100644 (file)
@@ -9,6 +9,7 @@ use Carp;
 use List::Util qw(max);
 
 use SL::DB::Default;
+use SL::PrefixedNumber;
 
 sub oe_scoping {
   SL::DB::Manager::Order->type_filter($_[0]);
@@ -33,7 +34,7 @@ my %specs = ( ar                      => { number_column => 'invnumber',
               vendor                  => { number_column => 'vendornumber',   number_range_column => 'vendornumber',                                                     },
               part                    => { number_column => 'partnumber',     number_range_column => 'articlenumber',  scoping => \&parts_scoping                        },
               service                 => { number_column => 'partnumber',     number_range_column => 'servicenumber',  scoping => \&parts_scoping                        },
-              assembly                => { number_column => 'partnumber',     number_range_column => 'articlenumber',  scoping => \&parts_scoping                        },
+              assembly                => { number_column => 'partnumber',     number_range_column => 'assemblynumber', scoping => \&parts_scoping                        },
             );
 
 sub get_next_trans_number {
@@ -50,34 +51,23 @@ sub get_next_trans_number {
 
   return $number if $self->id && $number;
 
-  my $re              = '^(.*?)(\d+)$';
-  my %conditions      = $scoping_conditions ? ( query => [ $scoping_conditions->($spec_type) ] ) : ();
-  my @numbers         = map { $_->$number_column } @{ $self->_get_manager_class->get_all(%conditions) };
-  my %numbers_in_use  = map { ( $_ => 1 )        } @numbers;
-  @numbers            = grep { $_ } map { my @matches = m/$re/; @matches ? $matches[-1] * 1 : undef } @numbers;
-
-  my $defaults        = SL::DB::Default->get;
-  my $number_range    = $defaults->$number_range_column;
-  my @matches         = $number_range =~ m/$re/;
-  my $prefix          = (2 != scalar(@matches)) ? ''  : $matches[ 0];
-  my $ref_number      = !@matches               ? '1' : $matches[-1];
-  my $min_places      = length($ref_number);
-
-  my $new_number      = $fill_holes_in_range ? $ref_number : max($ref_number, @numbers);
-  my $new_number_full = undef;
-
-  while (1) {
-    $new_number      =  $new_number + 1;
-    my $new_number_s =  $new_number;
-    $new_number_s    =~ s/\.\d+//g;
-    $new_number_full =  $prefix . ('0' x max($min_places - length($new_number_s), 0)) . $new_number_s;
-    last if !$numbers_in_use{$new_number_full};
-  }
-
-  $defaults->update_attributes($number_range_column => $new_number_full) if $params{update_defaults};
-  $self->$number_column($new_number_full)                                if $params{update_record};
-
-  return $new_number_full;
+  my %conditions     = $scoping_conditions ? ( query => [ $scoping_conditions->($spec_type) ] ) : ();
+  my @numbers        = map { $_->$number_column } @{ $self->_get_manager_class->get_all(%conditions) };
+  my %numbers_in_use = map { ( $_ => 1 )        } @numbers;
+
+  my $defaults       = SL::DB::Default->get;
+  $number_range_column = 'articlenumber' if $number_range_column eq 'assemblynumber' and length($defaults->$number_range_column) < 1;
+  my $sequence       = SL::PrefixedNumber->new(number => $defaults->$number_range_column);
+
+  $sequence->set_to_max(@numbers) if !$fill_holes_in_range;
+
+  my $new_number = $sequence->get_next;
+  $new_number    = $sequence->get_next while $numbers_in_use{$new_number};
+
+  $defaults->update_attributes($number_range_column => $new_number) if $params{update_defaults};
+  $self->$number_column($new_number)                                if $params{update_record};
+
+  return $new_number;
 }
 
 sub create_trans_number {
index 77d6769a8c13cabdcc5cff81e83967099b3c94a0..09c7f86e7d11aa4f75e215c5f44087beac4faa8b 100644 (file)
@@ -33,6 +33,12 @@ __PACKAGE__->meta->add_relationship(
     class         => 'SL::DB::Invoice',
     column_map    => { id => 'storno_id' },
   },
+  sepa_export_items => {
+    type            => 'one to many',
+    class           => 'SL::DB::SepaExportItem',
+    column_map      => { id => 'ar_id' },
+    manager_args    => { with_objects => [ 'sepa_export' ] }
+  },
 );
 
 __PACKAGE__->meta->initialize;
@@ -90,7 +96,7 @@ sub new_from {
   croak("Unsupported source object type '" . ref($source) . "'") unless ref($source) =~ m/^ SL::DB:: (?: Order | DeliveryOrder ) $/x;
   croak("Cannot create invoices for purchase records")           unless $source->customer_id;
 
-  my $terms = $source->can('payment_id') && $source->payment_id ? $source->payment_term->terms_netto : 0;
+  my $terms = $source->can('payment_id') && $source->payment_id ? $source->payment_terms->terms_netto : 0;
 
   my %args = ( map({ ( $_ => $source->$_ ) } qw(customer_id taxincluded shippingpoint shipvia notes intnotes curr salesman_id cusordnumber ordnumber quonumber
                                                 department_id cp_id language_id payment_id delivery_customer_id delivery_vendor_id taxzone_id shipto_id
@@ -241,6 +247,10 @@ sub displayable_state {
   return $self->closed ? $::locale->text('closed') : $::locale->text('open');
 }
 
+sub date {
+  goto &transdate;
+}
+
 1;
 
 __END__
index 9365eac9dcf8638847ab6fcb32b89dd2d5c30fe3..bb174ebaa4700d7887491ff2454c6aa4214b6829 100644 (file)
@@ -36,9 +36,6 @@ __PACKAGE__->meta->setup(
     mtime                               => { type => 'timestamp' },
     rmanumber                           => { type => 'text' },
     cnnumber                            => { type => 'text' },
-    accounting_method                   => { type => 'text' },
-    inventory_system                    => { type => 'text' },
-    profit_determination                => { type => 'text' },
     dunning_ar_amount_fee               => { type => 'integer' },
     dunning_ar_amount_interest          => { type => 'integer' },
     dunning_ar                          => { type => 'integer' },
@@ -47,31 +44,33 @@ __PACKAGE__->meta->setup(
     ar_paid_accno_id                    => { type => 'integer' },
     id                                  => { type => 'serial', not_null => 1 },
     language_id                         => { type => 'integer' },
-    payments_changeable                 => { type => 'integer', default => '0', not_null => 1 },
-    show_bestbefore                     => { type => 'boolean', default => 'false' },
+    accounting_method                   => { type => 'text' },
+    inventory_system                    => { type => 'text' },
+    profit_determination                => { type => 'text' },
     datev_check_on_sales_invoice        => { type => 'boolean', default => 'true' },
     datev_check_on_purchase_invoice     => { type => 'boolean', default => 'true' },
     datev_check_on_ar_transaction       => { type => 'boolean', default => 'true' },
     datev_check_on_ap_transaction       => { type => 'boolean', default => 'true' },
     datev_check_on_gl_transaction       => { type => 'boolean', default => 'true' },
+    payments_changeable                 => { type => 'integer', default => '0', not_null => 1 },
     is_changeable                       => { type => 'integer', default => 2, not_null => 1 },
     ir_changeable                       => { type => 'integer', default => 2, not_null => 1 },
     ar_changeable                       => { type => 'integer', default => 2, not_null => 1 },
     ap_changeable                       => { type => 'integer', default => 2, not_null => 1 },
     gl_changeable                       => { type => 'integer', default => 2, not_null => 1 },
-    is_show_mark_as_paid                => { type => 'boolean', default => 'true' },
-    ir_show_mark_as_paid                => { type => 'boolean', default => 'true' },
-    ar_show_mark_as_paid                => { type => 'boolean', default => 'true' },
-    ap_show_mark_as_paid                => { type => 'boolean', default => 'true' },
+    show_bestbefore                     => { type => 'boolean', default => 'false' },
     sales_order_show_delete             => { type => 'boolean', default => 'true' },
     purchase_order_show_delete          => { type => 'boolean', default => 'true' },
     sales_delivery_order_show_delete    => { type => 'boolean', default => 'true' },
     purchase_delivery_order_show_delete => { type => 'boolean', default => 'true' },
+    is_show_mark_as_paid                => { type => 'boolean', default => 'true' },
+    ir_show_mark_as_paid                => { type => 'boolean', default => 'true' },
+    ar_show_mark_as_paid                => { type => 'boolean', default => 'true' },
+    ap_show_mark_as_paid                => { type => 'boolean', default => 'true' },
+    assemblynumber                      => { type => 'text' },
   ],
 
   primary_key_columns => [ 'id' ],
-
-  allow_inline_column_values => 1,
 );
 
 1;
index 1c7c050ea2d54924138c0f1b508fde76c62b0a35..8b86e97111a9942d2b0e422a30026d9557f571ff 100755 (executable)
@@ -134,12 +134,12 @@ sub save {
 
   my ($result, $exception);
   my $worker = sub {
-    SL::DB::Object::Hooks::run_hooks($self, 'before_save');
     $exception = $EVAL_ERROR unless eval {
+      SL::DB::Object::Hooks::run_hooks($self, 'before_save');
       $result = $self->SUPER::save(@args);
+      SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
       1;
     };
-    SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
 
     return $result;
   };
@@ -156,12 +156,12 @@ sub delete {
 
   my ($result, $exception);
   my $worker = sub {
-    SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
     $exception = $EVAL_ERROR unless eval {
+      SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
       $result = $self->SUPER::delete(@args);
+      SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
       1;
     };
-    SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
 
     return $result;
   };
index 3839286ba141f50a702cac9f346fda513970de80..371e4450e4198133ccdd7990664d37aa5c72b04a 100644 (file)
@@ -44,9 +44,10 @@ sub run_hooks {
 
   foreach my $sub (@{ ( $hooks{$when} || { })->{ ref($object) } || [ ] }) {
     my $result = ref($sub) eq 'CODE' ? $sub->($object, @args) : $object->call_sub($sub, @args);
-    die SL::X::DBHookError->new(when   => $when,
-                                hook   => (ref($sub) eq 'CODE' ? '<anonymous sub>' : $sub),
-                                object => $object)
+    die SL::X::DBHookError->new(when        => $when,
+                                hook        => (ref($sub) eq 'CODE' ? '<anonymous sub>' : $sub),
+                                object      => $object,
+                                object_type => ref($object))
       if !$result;
   }
 }
index b9959aa7e2c9437adddecf656f1c3ef72718366f..63d23f79fb25317d127822cfc42ae0cf59984010 100644 (file)
@@ -132,6 +132,10 @@ sub number {
   return $self->${ \ $number_method{$self->type} }(@_);
 }
 
+sub date {
+  goto &transdate;
+}
+
 1;
 
 __END__
index da34a27b8a496d8abd30f891bc913532d0c4af8d..f74ee19e146c0a006b79303e61ca973e6e5e3e29 100644 (file)
@@ -10,12 +10,20 @@ use SL::DB::Helper::LinkedRecords;
 # The calculator hasn't been adjusted for purchase invoices yet.
 # use SL::DB::Helper::PriceTaxCalculator;
 
-__PACKAGE__->meta->add_relationship(invoiceitems => { type         => 'one to many',
-                                                      class        => 'SL::DB::InvoiceItem',
-                                                      column_map   => { id => 'trans_id' },
-                                                      manager_args => { with_objects => [ 'part' ] }
-                                                    },
-                                   );
+__PACKAGE__->meta->add_relationship(
+  invoiceitems   => {
+    type         => 'one to many',
+    class        => 'SL::DB::InvoiceItem',
+    column_map   => { id => 'trans_id' },
+    manager_args => { with_objects => [ 'part' ] }
+  },
+  sepa_export_items => {
+    type            => 'one to many',
+    class           => 'SL::DB::SepaExportItem',
+    column_map      => { id => 'ap_id' },
+    manager_args    => { with_objects => [ 'sepa_export' ] }
+  },
+);
 
 __PACKAGE__->meta->initialize;
 
@@ -27,4 +35,8 @@ sub is_sales {
   return 0;
 }
 
+sub date {
+  goto &transdate;
+}
+
 1;
index 3c46ee39b41fac1ab3015c638c4684fe2a56ad06..75701166024de8280df01b982fc28524648117e2 100644 (file)
@@ -1,6 +1,3 @@
-# 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::SepaExportItem;
 
 use strict;
@@ -10,4 +7,15 @@ use SL::DB::MetaSetup::SepaExportItem;
 # Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
 __PACKAGE__->meta->make_manager_class;
 
+sub compare_to {
+  my ($self, $other) = @_;
+
+  return  1 if  $self->execution_date && !$other->execution_date;
+  return -1 if !$self->execution_date &&  $other->execution_date;
+
+  my $result = 0;
+  $result    = $self->execution_date <=> $other->execution_date if $self->execution_date;
+  return $result || ($self->sepa_export_id <=> $other->sepa_export_id) || ($self->id <=> $other->id);
+}
+
 1;
index 83210316c795ea4e718330138172f40cc73bd930..d98627c563362906f280ef6958766d0bc16815ad 100644 (file)
@@ -4,6 +4,7 @@ use IO::File;
 use List::MoreUtils qw(any);
 
 use SL::Common;
+use SL::DBUpgrade2::Base;
 use SL::DBUtils;
 use SL::Iconv;
 
@@ -79,6 +80,7 @@ sub parse_dbupdate_controls {
 
     next if ($control->{ignore});
 
+    $control->{charset} = 'UTF-8' if $file =~ m/\.pl$/;
     $control->{charset} = $control->{charset} || $control->{encoding} || Common::DEFAULT_CHARSET;
 
     if (!$control->{"tag"}) {
@@ -240,49 +242,23 @@ sub process_perl_script {
 
   my ($self, $dbh, $filename, $version_or_control, $db_charset) = @_;
 
-  my $form         = $self->{form};
-  my $fh           = IO::File->new($filename, "r") or $form->error("$filename : $!\n");
-  my $file_charset = Common::DEFAULT_CHARSET;
-
-  if (ref($version_or_control) eq "HASH") {
-    $file_charset = $version_or_control->{charset};
-
-  } else {
-    while (<$fh>) {
-      last if !/^--/;
-      next if !/^--\s*\@(?:charset|encoding):\s*(.+)/;
-      $file_charset = $1;
-      last;
-    }
-    $fh->seek(0, SEEK_SET);
-  }
-
-  my $contents = join "", <$fh>;
-  $fh->close();
+  $dbh->begin_work;
 
-  $db_charset ||= Common::DEFAULT_CHARSET;
-
-  my $iconv = SL::Iconv->new($file_charset, $db_charset);
-
-  $dbh->begin_work();
-
-  # setup dbup_ export vars
-  my %dbup_myconfig = ();
-  map({ $dbup_myconfig{$_} = $form->{$_}; } qw(dbname dbuser dbpasswd dbhost dbport dbconnect));
-
-  my $dbup_locale = $::locale;
-
-  my $result = eval($contents);
+  # setup dbup_ export vars & run script
+  my %dbup_myconfig = map { ($_ => $::form->{$_}) } qw(dbname dbuser dbpasswd dbhost dbport dbconnect);
+  my $result        = SL::DBUpgrade2::Base::execute_script(
+    file_name => $filename,
+    tag       => $version_or_control->{tag},
+    dbh       => $dbh,
+    myconfig  => \%dbup_myconfig,
+  );
 
-  if (1 != $result) {
+  if (1 != ($result // 1)) {
     $dbh->rollback();
-    $dbh->disconnect();
   }
 
   if (!defined($result)) {
-    print $form->parse_html_template("dbupgrade/error",
-                                     { "file"  => $filename,
-                                       "error" => $@ });
+    print $::form->parse_html_template("dbupgrade/error", { file  => $filename, error => $@ });
     ::end_of_request();
   } elsif (1 != $result) {
     unlink("users/nologin") if (2 == $result);
@@ -290,7 +266,7 @@ sub process_perl_script {
   }
 
   if (ref($version_or_control) eq "HASH") {
-    $dbh->do("INSERT INTO " . $self->{schema} . "schema_info (tag, login) VALUES (" . $dbh->quote($version_or_control->{"tag"}) . ", " . $dbh->quote($form->{"login"}) . ")");
+    $dbh->do("INSERT INTO " . $self->{schema} . "schema_info (tag, login) VALUES (" . $dbh->quote($version_or_control->{tag}) . ", " . $dbh->quote($::form->{login}) . ")");
   } elsif ($version_or_control) {
     $dbh->do("UPDATE defaults SET version = " . $dbh->quote($version_or_control));
   }
diff --git a/SL/DBUpgrade2/Base.pm b/SL/DBUpgrade2/Base.pm
new file mode 100644 (file)
index 0000000..09e4e5f
--- /dev/null
@@ -0,0 +1,242 @@
+package SL::DBUpgrade2::Base;
+
+use strict;
+
+use parent qw(Rose::Object);
+
+use Carp;
+use English qw(-no_match_vars);
+use File::Basename ();
+use File::Copy ();
+use File::Path ();
+use List::MoreUtils qw(uniq);
+
+use Rose::Object::MakeMethods::Generic (
+  scalar => [ qw(dbh myconfig) ],
+);
+
+use SL::DBUtils;
+
+sub execute_script {
+  my (%params) = @_;
+
+  my $file_name = delete $params{file_name};
+
+  if (!eval { require $file_name }) {
+    delete $INC{$file_name};
+    die $EVAL_ERROR;
+  }
+
+  my $package =  delete $params{tag};
+  $package    =~ s/[^a-zA-Z0-9_]+/_/g;
+  $package    =  "SL::DBUpgrade2::${package}";
+
+  $package->new(%params)->run;
+}
+
+sub db_error {
+  my ($self, $msg) = @_;
+
+  die $::locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr;
+}
+
+sub db_query {
+  my ($self, $query, $may_fail) = @_;
+
+  return if $self->dbh->do($query);
+
+  $self->db_error($query) unless $may_fail;
+
+  $self->dbh->rollback;
+  $self->dbh->begin_work;
+}
+
+sub check_coa {
+  my ($self, $wanted_coa) = @_;
+
+  my ($have_coa)          = selectrow_query($::form, $self->dbh, q{ SELECT count(*) FROM defaults WHERE coa = ? }, $wanted_coa);
+
+  return $have_coa;
+}
+
+sub is_coa_empty {
+  my ($self) = @_;
+
+  my $query = q{ SELECT count(*)
+                 FROM ar, ap, gl, invoice, acc_trans, customer, vendor, parts
+               };
+  my ($empty) = selectrow_query($::form, $self->dbh, $query);
+
+  return !$empty;
+}
+
+sub add_print_templates {
+  my ($self, $src_dir, @files) = @_;
+
+  $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: src_dir $src_dir files " . join('  ', @files));
+
+  foreach (@files) {
+    croak "File '${src_dir}/$_' does not exist" unless -f "${src_dir}/$_";
+  }
+
+  my %users         = $::auth->read_all_users;
+  my @template_dirs = uniq map { $_ = $_->{templates}; s:/+$::; $_ } values %users;
+
+  $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: template_dirs " . join('  ', @template_dirs));
+
+  foreach my $src_file (@files) {
+    foreach my $template_dir (@template_dirs) {
+      my $dest_file = $template_dir . '/' . $src_file;
+
+      if (-f $dest_file) {
+        $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: dest_file exists, skipping: ${dest_file}");
+        next;
+      }
+
+      my $dest_dir = File::Basename::dirname($dest_file);
+
+      if ($dest_dir && !-d $dest_dir) {
+        File::Path::make_path($dest_dir) or die "Cannot create directory '${dest_dir}': $!";
+      }
+
+      File::Copy::copy($src_dir . '/' . $src_file, $dest_file) or die "Cannot copy '${src_dir}/${src_file}' to '${dest_file}': $!";
+
+      $::lxdebug->message(LXDebug::DEBUG1(), "add_print_templates: copied '${src_dir}/${src_file}' to '${dest_file}'");
+    }
+  }
+
+  return 1;
+}
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::DBUpgrade2::Base - Base class for Perl-based database upgrade files
+
+=head1 OVERVIEW
+
+Database scripts written in Perl must be derived from this class and
+provide a method called C<run>.
+
+The functions in this base class offer functionality for the upgrade
+scripts.
+
+=head1 PROPERTIES
+
+The following properties (which can be accessed with
+C<$self-E<gt>property_name>) are available to the database upgrade
+script:
+
+=over 4
+
+=item C<dbh>
+
+The database handle; an Instance of L<DBI>. It is connected, and a
+transaction has been started right before the script (the method
+L</run>)) was executed.
+
+=item C<myconfig>
+
+The stripped-down version of the C<%::myconfig> hash: this hash
+reference only contains the database connection parameters applying to
+the current database.
+
+=back
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<add_print_templates $source_dir, @files>
+
+Adds (copies) new print templates to existing users. All existing
+users in the authentication database are read. The listed C<@files>
+are copied to each user's configured templates directory preserving
+sub-directory structure (non-existing sub-directories will be
+created). If a template with the same name exists it will be skipped.
+
+The source file names must all be relative to the source directory
+C<$source_dir>. This way only the desired sub-directories are created
+in the users' template directories. Example:
+
+  $self->add_print_templates(
+    'templates/print/Standard',
+    qw(receipt.tex common.sty images/background.png)
+  );
+
+Let's assume a user's template directory is
+C<templates/big-money-inc>. The call above would trigger five actions:
+
+=over 2
+
+=item 1. Create the directory C<templates/big-money-inc> if it doesn't
+exist.
+
+=item 2. Copy C<templates/print/Standard/receipt.tex> to
+C<templates/big-money-inc/receipt.tex> if there's no such file in that
+directory.
+
+=item 3. Copy C<templates/print/Standard/common.sty> to
+C<templates/big-money-inc/common.sty> if there's no such file in that
+directory.
+
+=item 4. Create the directory C<templates/big-money-inc/images> if it
+doesn't exist.
+
+=item 5. Copy C<templates/print/Standard/images/background.png> to
+C<templates/big-money-inc/images/background.png> if there's no such
+file in that directory.
+
+=back
+
+=item C<check_coa $coa_name>
+
+Returns trueish if the database uses the chart of accounts named
+C<$coa_name>.
+
+=item C<db_error $message>
+
+Outputs an error message C<$message> to the user and aborts execution.
+
+=item C<db_query $query, $may_fail>
+
+Executes an SQL query. What the method does if the query fails depends
+on C<$may_fail>. If it is falsish then the method will simply die
+outputting the error message via L</db_error>. If C<$may_fail> is
+trueish then the current transaction will be rolled back, a new one
+will be started
+
+=item C<execute_script>
+
+Executes a named database upgrade script. This function is not
+supposed to be called from an upgrade script. Instead, the upgrade
+manager L<SL::DBUpgrade2> uses it in order to execute the actual
+database upgrade scripts.
+
+=item C<is_coa_empty>
+
+Returns trueish if no transactions have been recorded in the table
+C<acc_trans> yet.
+
+=item C<run>
+
+This method is the entry point for the actual upgrade. Each upgrade
+script must provide this method.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index a12fac14bc06a4c226b9d19b66f7311f347b75a1..2eb93376f132158cd8a60553bc1c04278968ccf6 100644 (file)
--- a/SL/DO.pm
+++ b/SL/DO.pm
@@ -737,10 +737,7 @@ sub retrieve {
 sub order_details {
   $main::lxdebug->enter_sub();
 
-  my ($self)   = @_;
-
-  my $myconfig = \%main::myconfig;
-  my $form     = $main::form;
+  my ($self, $myconfig, $form) = @_;
 
   # connect to database
   my $dbh = $form->get_standard_dbh($myconfig);
@@ -805,7 +802,7 @@ sub order_details {
   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
   $form->{TEMPLATE_ARRAYS} = { };
-  IC->prepare_parts_for_printing();
+  IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
   my @arrays =
     qw(runningnumber number description longdescription qty unit
index 98a8b5e9ad076f5c0c7f9256c63875256c82c2b7..c2385ce791a61a435ab5ab24e7721451dc4a5bc1 100644 (file)
@@ -62,6 +62,7 @@ use SL::Mailer;
 use SL::Menu;
 use SL::MoreCommon qw(uri_encode uri_decode);
 use SL::OE;
+use SL::PrefixedNumber;
 use SL::Request;
 use SL::Template;
 use SL::User;
@@ -470,11 +471,11 @@ sub header {
     main menu list_accounts jquery.autocomplete
     jquery.multiselect2side frame_header/header
     ui-lightness/jquery-ui
-    jquery-ui.custom
+    jquery-ui.custom jqModal
   );
 
   $layout->use_javascript("$_.js") for (qw(
-    jquery jquery-ui jquery.cookie jqModal jquery.checkall
+    jquery jquery-ui jquery.cookie jqModal jquery.checkall jquery.download
     common part_selection switchmenuframe
   ), "jquery/ui/i18n/jquery.ui.datepicker-$::myconfig{countrycode}");
 
@@ -997,7 +998,6 @@ sub parse_template {
     $ext_for_format = $self->{"format"} =~ m/pdf/ ? 'pdf' : 'odt';
 
   } elsif ($self->{"format"} =~ /(postscript|pdf)/i) {
-    $ENV{"TEXINPUTS"} = ".:" . getcwd() . "/" . $myconfig->{"templates"} . ":" . $ENV{"TEXINPUTS"};
     $template_type    = 'LaTeX';
     $ext_for_format   = 'pdf';
 
@@ -2111,7 +2111,7 @@ sub _get_taxcharts {
 
   my $where = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : '';
 
-  my $query = qq|SELECT * FROM tax $where ORDER BY taxkey|;
+  my $query = qq|SELECT * FROM tax $where ORDER BY taxkey, rate|;
 
   $self->{$key} = selectall_hashref_query($self, $dbh, $query);
 
@@ -3184,15 +3184,8 @@ sub update_defaults {
   my ($var) = $sth->fetchrow_array;
   $sth->finish;
 
-  if ($var =~ m/\d+$/) {
-    my $new_var  = (substr $var, $-[0]) * 1 + 1;
-    my $len_diff = length($var) - $-[0] - length($new_var);
-    $var         = substr($var, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_var;
-
-  } else {
-    $var = $var . '1';
-  }
-
+  $var   = 0 if !defined($var) || ($var eq '');
+  $var   = SL::PrefixedNumber->new(number => $var)->get_next;
   $query = qq|UPDATE defaults SET $fld = ?|;
   do_query($self, $dbh, $query, $var);
 
@@ -3409,7 +3402,7 @@ sub prepare_for_printing {
   IC->retrieve_accounts(\%::myconfig, $self, map { $_ => $self->{"id_$_"} } 1 .. $self->{rowcount});
 
   if ($self->{type} =~ /_delivery_order$/) {
-    DO->order_details();
+    DO->order_details(\%::myconfig, $self);
   } elsif ($self->{type} =~ /sales_order|sales_quotation|request_quotation|purchase_order/) {
     OE->order_details(\%::myconfig, $self);
   } else {
index 10d6ee120a00ecac476d69d550d1fb7b84ed0288..a422877356583d853d30ba8914d524de884aa576 100644 (file)
--- a/SL/GL.pm
+++ b/SL/GL.pm
@@ -285,9 +285,7 @@ sub all_transactions {
     push(@apvalues, '%' . $form->{description} . '%');
   }
 
-  if ($form->{employee} =~ /--/) {
-    ($form->{employee_id},$form->{employee_name}) = split(/--/,$form->{employee});
-  #if ($form->{employee_id}) {
+  if ($form->{employee_id}) {
     $glwhere .= " AND g.employee_id = ? ";
     $arwhere .= " AND a.employee_id = ? ";
     $apwhere .= " AND a.employee_id = ? ";
@@ -803,5 +801,24 @@ sub get_chart_balances {
   $main::lxdebug->leave_sub();
 }
 
+sub get_tax_dropdown {
+  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});
+
+  $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} = [];
+  while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
+    push(@{ $form->{TAX_ACCOUNTS} }, $ref);
+  }
+
+}
 
 1;
index a292a78ef7bea643bb37bce6be1529f64620a954..76fd8e6c63354c4c817946f497d2a3c7dc0ceb7c 100644 (file)
--- a/SL/Git.pm
+++ b/SL/Git.pm
@@ -34,6 +34,7 @@ sub get_log {
   my $in          = IO::File->new($self->git_exe . qq! log --format='tformat:\%H|\%an|\%ae|\%ai|\%s' ${since_until} |!);
 
   if (!$in) {
+    no warnings 'once';
     $::lxdebug->message(LXDebug::WARN(), "Error spawning git: $!");
     return ();
   }
index b72207f8a4ba5edf22fe2744ddfead7673c4c09d..639ed45318e31173e7ca65d8adb4129887bce08b 100644 (file)
@@ -2,6 +2,8 @@ package DateTime;
 
 use strict;
 
+use SL::Util qw(_hashify);
+
 sub now_local {
   return shift->now(time_zone => $::locale->get_local_time_zone);
 }
@@ -11,8 +13,7 @@ sub today_local {
 }
 
 sub to_kivitendo {
-  my $self   = shift;
-  my %params = (scalar(@_) == 1) && (ref($_[0]) eq 'HASH') ? %{ $_[0] } : @_;
+  my ($self, %params) = _hashify(1, @_);
   return $::locale->format_date_object($self, %params);
 }
 
index 4b48dcc4f3f702ae19a50b564e77a4c99197b85c..74cfe17023fa683234f52db6ad368d424a4ece55 100644 (file)
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -1487,8 +1487,22 @@ sub retrieve_accounts {
 
   # transdate madness.
   my $transdate = "";
-  if ($form->{type} eq "invoice") {
-    if (($form->{vc} eq "vendor") || !$form->{deliverydate}) {
+  if ($form->{type} eq "invoice" or $form->{type} eq "credit_note") {
+    # use deliverydate for sales and purchase invoice, if it exists
+    # also use deliverydate for credit notes
+    if (!$form->{deliverydate}) {
+      $transdate = $form->{invdate};
+    } else {
+      $transdate = $form->{deliverydate};
+    }
+  } elsif ($form->{script} eq 'ir.pl') {
+    # when a purchase invoice is opened from the report of purchase invoices 
+    # $form->{type} isn't set, but $form->{script} is, not sure why this is or
+    # whether this distinction matters in some other scenario. Otherwise one
+    # could probably take out this elsif and add a
+    # " or $form->{script} eq 'ir.pl' "
+    # to the above if-statement
+    if (!$form->{deliverydate}) {
       $transdate = $form->{invdate};
     } else {
       $transdate = $form->{deliverydate};
@@ -1497,6 +1511,8 @@ sub retrieve_accounts {
     # if credit_note has a deliverydate, use this instead of invdate
     # useful for credit_notes of invoices from an old period with different tax
     # if there is no deliverydate then invdate is used, old default (see next elsif)
+    # Falls hier der Stichtag für Steuern anders bestimmt wird,
+    # entsprechend auch bei Taxkeys.pm anpassen
     $transdate = $form->{deliverydate};
   } elsif (($form->{type} eq "credit_note") || ($form->{script} eq 'ir.pl')) {
     $transdate = $form->{invdate};
@@ -1616,8 +1632,8 @@ sub prepare_parts_for_printing {
   my $self     = shift;
   my %params   = @_;
 
-  my $myconfig = \%main::myconfig;
-  my $form     = $main::form;
+  my $myconfig = $params{myconfig} || \%main::myconfig;
+  my $form     = $params{form}     || $main::form;
 
   my $dbh      = $params{dbh} || $form->get_standard_dbh($myconfig);
 
index d52641e862302f7d758aade38a185e73db29e229..0179d6e78c244a86901f5ca8637792f288e8b780 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -215,14 +215,16 @@ sub post_invoice {
 
       # check if we sold the item already and
       # make an entry for the expense and inventory
+      my $taxzone = $form->{taxzone_id} * 1;
       $query =
         qq|SELECT i.id, i.qty, i.allocated, i.trans_id, i.base_qty,
-             p.inventory_accno_id, p.expense_accno_id, a.transdate
-           FROM invoice i, ar a, parts p
+             bg.inventory_accno_id, bg.expense_accno_id_${taxzone} AS expense_accno_id, a.transdate
+           FROM invoice i, ar a, parts p, buchungsgruppen bg
            WHERE (i.parts_id = p.id)
              AND (i.parts_id = ?)
              AND ((i.base_qty + i.allocated) > 0)
              AND (i.trans_id = a.id)
+             AND (p.buchungsgruppen_id = bg.id)
            ORDER BY transdate|;
            # ORDER BY transdate guarantees FIFO
 
@@ -268,7 +270,7 @@ sub post_invoice {
 
             # allocated >= 0
             # add entry for inventory, this one is for the sold item
-            $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, tax_id) VALUES (?, ?, ?, ?,
+            $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, tax_id, chart_link) VALUES (?, ?, ?, ?,
                                (SELECT taxkey_id
                                 FROM taxkeys
                                 WHERE chart_id= ?
@@ -279,13 +281,13 @@ sub post_invoice {
                                 WHERE chart_id= ?
                                 AND startdate <= ?
                                 ORDER BY startdate DESC LIMIT 1),
-                               (SELECT chart_link FROM chart WHERE id = ?))|;
+                               (SELECT link FROM chart WHERE id = ?))|;
             @values = ($ref->{trans_id},  $ref->{inventory_accno_id}, $linetotal, $ref->{transdate}, $ref->{inventory_accno_id}, $ref->{transdate}, $ref->{inventory_accno_id}, $ref->{transdate},
                        $ref->{inventory_accno_id});
             do_query($form, $dbh, $query, @values);
 
 # add expense
-            $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, tax_id) VALUES (?, ?, ?, ?,
+            $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, tax_id, chart_link) VALUES (?, ?, ?, ?,
                                 (SELECT taxkey_id
                                  FROM taxkeys
                                  WHERE chart_id= ?
@@ -296,7 +298,7 @@ sub post_invoice {
                                  WHERE chart_id= ?
                                  AND startdate <= ?
                                  ORDER BY startdate DESC LIMIT 1),
-                                (SELECT chart_link FROM chart WHERE id = ?))|;
+                                (SELECT link FROM chart WHERE id = ?))|;
             @values = ($ref->{trans_id},  $ref->{expense_accno_id}, ($linetotal * -1), $ref->{transdate}, $ref->{expense_accno_id}, $ref->{transdate}, $ref->{expense_accno_id}, $ref->{transdate},
                        $ref->{expense_accno_id});
             do_query($form, $dbh, $query, @values);
@@ -1210,7 +1212,9 @@ sub retrieve_item {
 
   my $transdate = "";
   if ($form->{type} eq "invoice") {
-    $transdate = $form->{invdate} ? $dbh->quote($form->{invdate}) : "current_date";
+    $transdate = $form->{deliverydate} ? $dbh->quote($form->{deliverydate}) 
+               : $form->{invdate} ? $dbh->quote($form->{invdate}) 
+               : "current_date";
   } else {
     $transdate = $form->{transdate} ? $dbh->quote($form->{transdate}) : "current_date";
   }
index cd4461891ac81a6baa9fe71b94943c2a07167ef1..c5147b1afb4957d81a00f3d1c466e1092a16b440 100644 (file)
--- a/SL/IS.pm
+++ b/SL/IS.pm
@@ -144,7 +144,7 @@ sub invoice_details {
 
   $form->{discount} = [];
 
-  IC->prepare_parts_for_printing();
+  IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
@@ -817,7 +817,7 @@ sub post_invoice {
   }
 
   $project_id = conv_i($form->{"globalproject_id"});
-
+  # entsprechend auch beim Bestimmen des Steuerschlüssels in Taxkey.pm berücksichtigen
   my $taxdate = $form->{deliverydate} ? $form->{deliverydate} : $form->{invdate};
 
   foreach my $trans_id (keys %{ $form->{amount_cogs} }) {
index bd08d8164ad26bd8518dd3cc76c6dfba7ee0da59..2212f718edbc264b66c6d008ed5a9e9904fdb0a6 100644 (file)
@@ -1,7 +1,7 @@
 package SL::Layout::Base;
 
 use strict;
-use parent qw(SL::Controller::Base);
+use parent qw(Rose::Object);
 
 use List::MoreUtils qw(uniq);
 use Time::HiRes qw();
@@ -18,6 +18,7 @@ use Rose::Object::MakeMethods::Generic (
 );
 
 use SL::Menu;
+use SL::Presenter;
 
 my %menu_cache;
 
@@ -154,4 +155,176 @@ sub need_footer {
   $_[0]{_header_done};
 }
 
+sub presenter {
+  SL::Presenter->get;
+}
+
 1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Layout::Base - Base class for layouts
+
+=head1 SYNOPSIS
+
+  package SL::Layout::MyLayout;
+
+  use parent qw(SL::Layout::Base);
+
+=head1 DESCRIPTION
+
+For a description about the external interface of layouts in general see
+L<SL::Layout::Dispatcher>.
+
+This is a base class for layouts in general. It provides the basic interface
+and some capabilities to extend and cascade layouts.
+
+
+=head1 IMPLEMENTING LAYOUT CALLBACKS
+
+There are eight callbacks (C<pre_content>, C<post_content>, C<start_content>,
+C<end_content>, C<stylesheets>, C<stylesheets_inline>, C<javscripts>,
+C<javascripts_inline>) which are documented in L<SL::Layout::Dispatcher>. If
+you are writing a new simple layout, you can just override some of them like
+this:
+
+  package SL::Layout::MyEvilLayout;
+
+  sub pre_content {
+    '<h1>This is MY page now</h1>'
+  }
+
+  sub post_content {
+    '<p align="right"><small><em>Brought to you by my own layout class</em></small></p>'
+  }
+
+
+To preserve the sanitizing effects of C<stylesheets> and C<javascripts> you should instead do the following:
+
+  sub stylesheets {
+    $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
+    $_[0]->SUPER::stylesheets;
+  }
+
+If you want to add something to a different layout, you should write a sub
+layout and add it to the other layouts.
+
+
+=head1 SUB LAYOUTS
+
+Layouts can be aggregated, so that common elements can be used in different
+layouts. Currently this is used for the L<None|SL::Layout::None> sub layout,
+which contains a lot of the stylesheets and javascripts necessary. Another
+example is the L<Top|SL::Layout::Top> layout, which is used to generate a
+common top bar for all menu types.
+
+To add a sub layout to your layout just overwrite the sub_layout method:
+
+  package SL::Layout::MyFinalLayout;
+
+  sub init_sub_layout {
+    [
+      SL::Layout::None->new,
+      SL::Layout::MyEvilLayout->new,
+    ]
+  }
+
+You can also add a sublayout at runtime:
+
+  $layout->add_sub_layout(SL::Layout::SideBar->new);
+
+The standard implementation for the callbacks will see to it that the contents
+of all sub layouts will get rendered.
+
+
+=head1 COMBINING SUB LAYOUTS AND OWN BEHAVIOUR
+
+This is still somewhat rough, and improvements are welcome.
+
+For the C<*_content> callbacks this works if you just remember to dispatch to the base method:
+
+  sub post_content {
+    return $_[0]->render_status_bar .
+    $_[0]->SUPER::post_content
+  }
+
+For the stylesheet and javascript callbacks things are hard, because of the
+backwards compatibility, and the built-in sanity checks. The best way currently
+is to just add your content and dispatch to the base method.
+
+  sub stylesheets {
+    $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
+    $_[0]->SUPER::stylesheets;
+  }
+
+=head1 GORY DETAILS ABOUT JAVASCRIPT AND STYLESHEET OVERLOADING
+
+The original code used to store one stylehsheet in C<< $form->{stylesheet} >> and
+allowed/expected authors of potential C<bin/mozilla/> controllers to change
+that into their own modified stylesheet.
+
+This was at some point cleaned up into a method C<use stylesheet> which took a
+string of space separated stylesheets and processed them into the response.
+
+A lot of controllers are still using this methods so the layout interface
+supports it to change as few controller code as possible, while providing the
+more intuitive C<add_stylesheets> method.
+
+At the same time the following things need to be possible:
+
+=over 4
+
+=item 1.
+
+Runtime additions.
+
+  $layout->add_stylesheets(...)
+
+Since add_stylesheets adds to C<< $self->{stylesheets} >> there must be a way to read
+from it. Currently this is the deprecated C<use_stylesheet>.
+
+=item 2.
+
+Overriding Callbacks
+
+A leaf layout should be able to override a callback to return a list.
+
+=item 3.
+
+Sanitizing
+
+C<stylesheets> needs to retain it's sanitizing behaviour.
+
+=item 4.
+
+Aggregation
+
+The standard implementation should be able to collect from sub layouts.
+
+=item 5.
+
+Preserving of Inclusion Order
+
+Since there is currently no standard way of mixing own content and including
+sub layouts, this has to be done manually. Certain things like jquery get added
+in L<SL::Layout::None> so that they get rendered first.
+
+=back
+
+The current implementation provides no good candidate for overriding in sub
+classes, which should be changed. The other points work pretty well.
+
+=head1 BUGS
+
+None yet, if you don't count the horrible stylesheet/javascript interface.
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
+
index 5d7688e2c96881dfb96daf9804143fc7bee5f17f..3dcde2733a164ae7494aae1ca950e629497a9255 100644 (file)
@@ -20,3 +20,188 @@ sub new {
 }
 
 1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Layout::Dispatcher - provides layouts by name.
+
+=head1 SYNOPSIS
+
+  use SL::Layout::Dispatcher;
+  $::request->layout(SL::Layout::Dispatcher->new(style => 'login'));
+
+  # same as
+
+  use SL::Layout::Login;
+  $::request->layout(SL::Layout::Login->new);
+
+=head1 INTRODUCTION
+
+A layout in kivitendo is anything that should be included into a text/html
+response without having each controller do it manually. This includes:
+
+=over 4
+
+=item *
+
+menus
+
+=item *
+
+status bars
+
+=item
+
+div containers for error handling and ajax responses
+
+=item *
+
+javascript and css includes
+
+=item *
+
+html header and body
+
+=back
+
+It does not include:
+
+=over 4
+
+=item *
+
+http headers
+
+=item *
+
+anthing that is not a text/html response
+
+=back
+
+All of these tasks are handled by a layout object, which is stored in the
+global L<$::request|SL::Request> object. An appropriate layout object will be
+chosen during the login/session_restore process.
+
+
+=head1 INTERFACE
+
+Every layout must be instantiated from L<SL::Layout::Base> and must implement
+the following eight callbacks:
+
+=over 4
+
+=item C<pre_content>
+
+Content that must, for whatever reason, appear before the main content.
+
+=item C<start_content>
+
+An introcutory clause for the main content. Usually something like C<< <div
+class='content'> >>.
+
+=item C<end_content>
+
+The corresponding end of L</start_content> like C<< </div> >>
+
+=item C<post_content>
+
+Any extra content that should appear after the main content in the response
+source. Note that it is preferred to put extra content after the main content,
+so that it gets rendered faster.
+
+=item C<stylesheets>
+
+A list of stylesheets that should be included as a full relative web path. Will
+be rendered into the html header.
+
+=item C<stylesheets_inline>
+
+A list of stylesheet snippets that need to be included in the response. Will be
+added to the html header.
+
+=item C<javascripts>
+
+A list of javascripts that should be included as a full relative web path.
+
+Note:
+There is no guarantee where these will end up in the content. Currently they
+will be rendered into the header, but are likely to be moved into the footer in
+the future.
+
+=item C<javascripts_inline>
+
+A list of javascript snippets that need to be included in the response.
+
+Note:
+These will end up in the footer, so make sure they don't contain
+initializations to static javascript includes that may be included earlier.
+
+=back
+
+=head1 RUNTIME INTERFACE
+
+Each layout object can add stylesheets and javascripts at runtime, as long as
+its before the actual rendering has begun. This can be used to add special
+javascripts only your controller needs.
+
+=over 4
+
+=item C<add_stylesheets>
+
+Adds the list of arguments to the list of used stylesheets.
+
+These will first be searched in the theme folder for the current user, and only
+after that be searched from the common C<css/> folder.
+
+Duplicated files will be only included once.
+
+Non-existing files will be pruned from the list.
+
+=item C<use_stylesheet>
+
+Backwards compatible alias for C<add_stylesheets>. Deprecated.
+
+=item C<add_javascripts>
+
+Adds the list of arguments to the list of used javascripts.
+
+Duplicated files will be only included once.
+
+Non-existing files will be pruned from the list.
+
+=item C<use_javascript>
+
+Backwards compatible alias for C<add_javascripts>. Deprecated.
+
+=item C<add_javascripts_inline>
+
+Add a snippet of javascript.
+
+=item C<add_stylesheets_inline>
+
+Add a snippet of css.
+
+=item C<focus>
+
+If set with a selector, the layout will generate javascript to set the page
+focus to that selector on document.ready.
+
+=back
+
+=head1 BUGS
+
+None yet :)
+
+=head1 TODO
+
+non existing css or js includes should generate a log entry.
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
index 2cda0f45916e31f7f3b39f1075e55b72d95d3f59..b08cf6dccbb715796d12162ad291058db77d57b1 100644 (file)
@@ -47,7 +47,7 @@ sub display {
   $callback               = URI->new($callback)->rel($callback) if $callback;
   $callback               = "login.pl?action=company_logo"      if $callback =~ /^(\.\/)?$/;
 
-  $self->render("menu/menunew", { output => 0 },
+  $self->presenter->render("menu/menunew",
     force_ul_width  => 1,
     date            => $self->clock_line,
     menu_items      => $self->acc_menu,
index d740640c6edae1dd2321ec9de08fbab4f6b79077..9a47d292dfa6dc2409857781fe0b40c6b417e1a3 100644 (file)
@@ -15,7 +15,7 @@ sub javascripts_inline {
   my $self = shift;
   $self->SUPER::javascripts_inline;
   my $sections = [ section_menu($self->menu) ];
-  $self->render('menu/menu', { output => 0 },
+  $self->presenter->render('menu/menu',
     sections  => $sections,
   )
 }
index 971ef857a00980edd8855d44b13d1890a4d2f643..4a02d652a81ff3c609c887e564e4b6b88c76eb93 100644 (file)
@@ -14,9 +14,9 @@ sub javascripts_inline {
     s/y+/yy/gi;
   } $::myconfig{dateformat};
 
-  return $self->render(
+  return $self->presenter->render(
     'layout/javascript_setup',
-    { type => 'js', output => 0, },
+    { type => 'js' },
     datefmt      => $datefmt,
     focus        => $::request->layout->focus,
     ajax_spinner => 1,
index 9c4fdea360d660e08c73b4f41ae07e70dba9954c..a838aeee12b3de12c389e0092971651edc6e0d4c 100644 (file)
@@ -6,7 +6,7 @@ use parent qw(SL::Layout::Base);
 sub pre_content {
   my ($self) = @_;
 
-  $self->SUPER::render('menu/header', { output => 0 },
+  $self->presenter->render('menu/header',
                 now        => DateTime->now_local,
                 is_fastcgi => scalar($::dispatcher->interface_type =~ /fastcgi/i),
                 is_links   => scalar($ENV{HTTP_USER_AGENT}         =~ /links/i));
index 9d5e37d86a9b97cdda50f3c14d076620a7a54cd7..03c6f3ebf5cd6c96e6fc121912ed4815d9eeb517 100644 (file)
@@ -164,7 +164,7 @@ sub render {
   $callback               = URI->new($callback)->rel($callback) if $callback;
   $callback               = "login.pl?action=company_logo"      if $callback =~ /^(\.\/)?$/;
 
-  $self->SUPER::render('menu/menuv3', { output => 0 },
+  $self->presenter->render('menu/menuv3',
     force_ul_width => 1,
     date           => $self->clock_line,
     menu           => $self->print_menu,
index 5c1ecd7eb0a4038600ed7660315221e177248c09..c5a8c26aec80121b539d13854b6e65b93f2004f3 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -1107,7 +1107,7 @@ sub order_details {
   $form->{discount} = [];
 
   $form->{TEMPLATE_ARRAYS} = { };
-  IC->prepare_parts_for_printing();
+  IC->prepare_parts_for_printing(myconfig => $myconfig, form => $form);
 
   my $ic_cvar_configs = CVar->get_configs(module => 'IC');
 
diff --git a/SL/PrefixedNumber.pm b/SL/PrefixedNumber.pm
new file mode 100644 (file)
index 0000000..849421f
--- /dev/null
@@ -0,0 +1,139 @@
+package SL::PrefixedNumber;
+
+use strict;
+
+use parent qw(Rose::Object);
+
+use Carp;
+use List::Util qw(max);
+
+use Rose::Object::MakeMethods::Generic
+(
+ scalar                  => [ qw(number) ],
+ 'scalar --get_set_init' => [ qw(_state) ],
+);
+
+sub init__state {
+  my ($self) = @_;
+
+  croak "No 'number' set" if !defined($self->number);
+
+  my @matches    = $self->number =~ m/^(.*?)(\d+)$/;
+  my @matches2   = $self->number =~ m/^(.*[^\d])$/;
+  my $prefix     =  @matches2 ? $matches2[0] : (2 != scalar(@matches)) ? '' : $matches[ 0],;
+  my $ref_number = !@matches  ? '0'          : $matches[-1];
+  my $min_places = length $ref_number;
+
+  return {
+    prefix     => $prefix,
+    ref_number => $ref_number,
+    min_places => $min_places,
+  };
+}
+
+sub get_current {
+  my ($self) = @_;
+
+  return $self->format($self->_state->{ref_number});
+}
+
+sub get_next {
+  my ($self) = @_;
+
+  return $self->set_to($self->_state->{ref_number} + 1);
+}
+
+sub format {
+  my ($self, $number) = @_;
+
+  my $state           = $self->_state;
+  $number             =~ s/\.\d+//g;
+
+  return $state->{prefix} . ('0' x max($state->{min_places} - length($number), 0)) . $number;
+}
+
+sub set_to {
+  my ($self, $new_number) = @_;
+
+  my $state            = $self->_state;
+  $state->{ref_number} = $new_number;
+
+  return $self->number($self->format($new_number));
+}
+
+sub set_to_max {
+  my ($self, @numbers) = @_;
+
+  return $self->set_to(max map { SL::PrefixedNumber->new(number => $_)->_state->{ref_number} } @numbers);
+}
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::PrefixedNumber - Increment a number prefixed with some text
+
+=head1 SYNOPSIS
+
+  my $number = SL::PrefixedNumber->new(number => 'FB000042')->get_next;
+  print $number; # FB000043
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<format $number>
+
+Returns C<$number> formatted according to the rules in C<$self>. Does
+not modify C<$self>. E.g.
+
+  my $sequence = SL::PrefixedNumber->new('FB12345');
+  print $sequence->format(42); # FB00042
+  print $sequence->get_next;   # FB12346
+
+=item C<get_current>
+
+Returns the current number in the sequence (formatted). Does not
+modify C<$self>.
+
+=item C<get_next>
+
+Returns the next number in the sequence (formatted). Modifies C<$self>
+accordingly so that calling C<get_next> multiple times will actually
+iterate over the sequence.
+
+=item C<set_to $number>
+
+Sets the current postfix to C<$number> but does not change the
+prefix. Returns the formatted new number. E.g.:
+
+  my $sequence = SL::PrefixedNumber->new(number => 'FB000042');
+  print $sequence->set_to(123); # FB000123
+  print $sequence->get_next;    # FB000124
+
+=item C<set_to_max @numbers>
+
+Sets the current postfix to the maximum of all the numbers listed in
+C<@numbers>. All those numbers can be prefixed numbers. Returns the
+formatted maximum number. E.g.
+
+  my $sequence = SL::PrefixedNumber->new(number => 'FB000042');
+  print $sequence->set_to_max('FB000123', 'FB999', 'FB00001'); # FB000999
+  print $sequence->get_next;                                   # FB001000
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index 61ddab4082922064730bfc1fec7efd780715042a..67e1948e4f33a899c307abe92bb62028badb8877 100644 (file)
@@ -14,6 +14,7 @@ use SL::Presenter::Invoice;
 use SL::Presenter::Order;
 use SL::Presenter::Project;
 use SL::Presenter::Record;
+use SL::Presenter::SepaExport;
 use SL::Presenter::Text;
 use SL::Presenter::Tag;
 
@@ -40,7 +41,7 @@ sub render {
   }
 
   # Only certain types are supported.
-  croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json)$/;
+  croak "Unsupported type: " . $options->{type} unless $options->{type} =~ m/^(?:html|js|json|text)$/;
 
   # The "template" argument must be a string or a reference to one.
   $template = ${ $template }                                       if ((ref($template) || '') eq 'REF') && (ref(${ $template }) eq 'SL::Presenter::EscapedText');
@@ -49,7 +50,8 @@ sub render {
   # Look for the file given by $template if $template is not a reference.
   my $source;
   if (!ref $template) {
-    $source = "templates/webpages/${template}." . $options->{type};
+    my $ext = $options->{type} eq 'text' ? 'txt' : $options->{type};
+    $source = "templates/webpages/${template}.${ext}";
     croak "Template file ${source} not found" unless -f $source;
 
   } elsif (ref($template) eq 'SCALAR') {
@@ -223,9 +225,10 @@ The following options are available:
 
 =item C<type>
 
-The template type. Can be C<html> (the default), C<js> for JavaScript
-or C<json> for JSON content. Affects only the extension that's added
-to the file name given with a non-reference C<$template> argument.
+The template type. Can be C<html> (the default), C<js> for JavaScript,
+C<json> for JSON and C<text> for plain text content. Affects only the
+extension that's added to the file name given with a non-reference
+C<$template> argument.
 
 =item C<process>
 
index 18c719092ddcd6e7a7d9883e8d35e1340f89bbcc..a00f40055e81525e5118e6ddf5fae31be80f515c 100644 (file)
@@ -7,6 +7,8 @@ use parent qw(Exporter);
 use Exporter qw(import);
 our @EXPORT = qw(grouped_record_list empty_record_list record_list);
 
+use SL::Util;
+
 use Carp;
 use List::Util qw(first);
 
@@ -22,7 +24,7 @@ sub grouped_record_list {
 
   %params    = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links with_columns object_id object_model);
 
-  my %groups = _group_records($list);
+  my %groups = _sort_grouped_lists(_group_records($list));
   my $output = '';
 
   $output .= _sales_quotation_list(        $self, $groups{sales_quotations},         %params) if $groups{sales_quotations};
@@ -37,6 +39,9 @@ sub grouped_record_list {
   $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
   $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
 
+  $output .= _sepa_collection_list(        $self, $groups{sepa_collections},         %params) if $groups{sepa_collections};
+  $output .= _sepa_transfer_list(          $self, $groups{sepa_transfers},           %params) if $groups{sepa_transfers};
+
   $output  = $self->render('presenter/record/grouped_record_list', %params, output => $output);
 
   return $output;
@@ -92,8 +97,9 @@ sub record_list {
       my $meta         =  $column_meta{ $spec->{data} };
       my $type         =  ref $meta;
       my $relationship =  $relationships{ $spec->{data} };
-      my $rel_type     =  !$relationship ? '' : lc $relationship->class;
-      $rel_type        =~ s/^sl::db:://;
+      my $rel_type     =  !$relationship ? '' : $relationship->class;
+      $rel_type        =~ s/^SL::DB:://;
+      $rel_type        =  SL::Util::snakify($rel_type);
 
       if (ref($spec->{data}) eq 'CODE') {
         $cell{value} = $spec->{data}->($obj);
@@ -146,6 +152,8 @@ sub _group_records {
     purchase_delivery_orders => sub { (ref($_[0]) eq 'SL::DB::DeliveryOrder')   && !$_[0]->is_sales                     },
     purchase_invoices        => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') &&  $_[0]->invoice                      },
     ap_transactions          => sub { (ref($_[0]) eq 'SL::DB::PurchaseInvoice') && !$_[0]->invoice                      },
+    sepa_collections         => sub { (ref($_[0]) eq 'SL::DB::SepaExportItem')  &&  $_[0]->ar_id                        },
+    sepa_transfers           => sub { (ref($_[0]) eq 'SL::DB::SepaExportItem')  &&  $_[0]->ap_id                        },
   );
 
   my %groups;
@@ -159,6 +167,21 @@ sub _group_records {
   return %groups;
 }
 
+sub _sort_grouped_lists {
+  my (%groups) = @_;
+
+  foreach my $group (keys %groups) {
+    next unless @{ $groups{$group} };
+    if ($groups{$group}->[0]->can('compare_to')) {
+      $groups{$group} = [ sort { $a->compare_to($b)    } @{ $groups{$group} } ];
+    } else {
+      $groups{$group} = [ sort { $a->date <=> $b->date } @{ $groups{$group} } ];
+    }
+  }
+
+  return %groups;
+}
+
 sub _sales_quotation_list {
   my ($self, $list, %params) = @_;
 
@@ -363,6 +386,41 @@ sub _ap_transaction_list {
   );
 }
 
+sub _sepa_export_list {
+  my ($self, $list, %params) = @_;
+
+  my ($source, $destination) = $params{type} eq 'sepa_transfer' ? qw(our vc)                                 : qw(vc our);
+  $params{title}             = $params{type} eq 'sepa_transfer' ? $::locale->text('Bank transfers via SEPA') : $::locale->text('Bank collections via SEPA');
+  $params{with_columns}      = [ grep { $_ ne 'record_link_direction' } @{ $params{with_columns} || [] } ];
+
+  delete $params{edit_record_links};
+
+  return $self->record_list(
+    $list,
+    columns => [
+      [ $::locale->text('Export Number'),    'sepa_export',                                  ],
+      [ $::locale->text('Execution date'),   'execution_date'                                ],
+      [ $::locale->text('Export date'),      sub { $_[0]->sepa_export->itime->to_kivitendo } ],
+      [ $::locale->text('Source BIC'),       "${source}_bic"                                 ],
+      [ $::locale->text('Source IBAN'),      "${source}_iban"                                ],
+      [ $::locale->text('Destination BIC'),  "${destination}_bic"                            ],
+      [ $::locale->text('Destination IBAN'), "${destination}_iban"                           ],
+      [ $::locale->text('Amount'),           'amount'                                        ],
+    ],
+    %params,
+  );
+}
+
+sub _sepa_transfer_list {
+  my ($self, $list, %params) = @_;
+  _sepa_export_list($self, $list, %params, type => 'sepa_transfer');
+}
+
+sub _sepa_collection_list {
+  my ($self, $list, %params) = @_;
+  _sepa_export_list($self, $list, %params, type => 'sepa_collection');
+}
+
 1;
 
 __END__
@@ -433,6 +491,10 @@ The order in which the records are grouped is:
 
 =item * AP transactions
 
+=item * SEPA collections
+
+=item * SEPA transfers
+
 =back
 
 Objects of unknown types are skipped.
diff --git a/SL/Presenter/SepaExport.pm b/SL/Presenter/SepaExport.pm
new file mode 100644 (file)
index 0000000..ceb483f
--- /dev/null
@@ -0,0 +1,89 @@
+package SL::Presenter::SepaExport;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Exporter qw(import);
+our @EXPORT = qw(sepa_export);
+
+use Carp;
+
+sub sepa_export {
+  my ($self, $sepa_export, %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="sepa.pl?action=bank_transfer_edit&amp;vc=' . $self->escape($sepa_export->vc) . '&amp;id=' . $self->escape($sepa_export->id) . '">',
+    $self->escape($sepa_export->id),
+    $params{no_link} ? '' : '</a>',
+  );
+  return $self->escaped_text($text);
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Presenter::SepaExport - Presenter module for Rose::DB objects
+for SEPA transfers and collections
+
+=head1 SYNOPSIS
+
+  # Collections from an invoice:
+  my $invoice = SL::DB::Invoice->new(id => 123)->load;
+  my $object  = $invoice->sepa_export_items->[0]->sepa_export;
+  my $html    = SL::Presenter->get->sepa_export($object, display => 'inline');
+
+  # Transfers from a purchase invoice:
+  my $invoice = SL::DB::PurchaseInvoice->new(id => 123)->load;
+  my $object  = $invoice->sepa_export_items->[0]->sepa_export;
+  my $html    = SL::Presenter->get->sepa_export($object, display => 'inline');
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<sepa_export $object, %params>
+
+Returns a rendered version (actually an instance of
+L<SL::Presenter::EscapedText>) of the SEPA collection/transfer object
+C<$object>.
+
+C<%params> can include:
+
+=over 2
+
+=item * display
+
+Either C<inline> (the default) or C<table-cell>. At the moment both
+representations are identical and produce the objects's delivery
+order number linked to the corresponding 'edit' action.
+
+=item * no_link
+
+If falsish (the default) then the delivery order number will be linked
+to the "edit SEPA transfer" dialog from the 'cash' menu.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index e65ca7722ef27abed26c402f62f22e58d6d2a242..9ead2f24a371e07eb507982d2541e407af655830 100644 (file)
@@ -45,7 +45,7 @@ sub html_tag {
 sub input_tag {
   my ($self, $name, $value, %attributes) = @_;
 
-  $attributes{id}   ||= $self->name_to_id($name);
+  _set_id_attribute(\%attributes, $name);
   $attributes{type} ||= 'text';
 
   return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
@@ -77,7 +77,7 @@ sub name_to_id {
 sub select_tag {
   my ($self, $name, $collection, %attributes) = @_;
 
-  $attributes{id}   ||= $self->name_to_id($name);
+  _set_id_attribute(\%attributes, $name);
 
   my $value_key       = delete($attributes{value_key})   || 'id';
   my $title_key       = delete($attributes{title_key})   || $value_key;
@@ -173,6 +173,14 @@ sub select_tag {
   return $self->html_tag('select', $code, %attributes, name => $name);
 }
 
+sub _set_id_attribute {
+  my ($attributes, $name) = @_;
+
+  $attributes->{id} = name_to_id(undef, $name) if !delete($attributes->{no_id}) && !$attributes->{id};
+
+  return %{ $attributes };
+}
+
 1;
 __END__
 
@@ -205,6 +213,10 @@ Usage from a template:
 A module modeled a bit after Rails' ActionView helpers. Several small
 functions that create HTML tags from various kinds of data sources.
 
+The C<id> attribute is usually calculated automatically. This can be
+overridden by either specifying an C<id> attribute or by setting
+C<no_id> to trueish.
+
 =head1 FUNCTIONS
 
 =head2 LOW-LEVEL FUNCTIONS
index 637bff6b652f7b8188153aafff4a35d5bb50dbd1..af3732309e777c149612062498851d10f6590bfd 100644 (file)
@@ -12,12 +12,7 @@ use Carp;
 sub truncate {
   my ($self, $text, %params) = @_;
 
-  $params{at}             ||= 50;
-  $params{at}               =  3 if 3 > $params{at};
-  $params{at}              -= 3;
-
-  return $text if length($text) < $params{at};
-  return substr($text, 0, $params{at}) . '...';
+  return Common::truncate($text, %params);
 }
 
 sub simple_format {
@@ -77,15 +72,11 @@ it, e.g. C<2 PT 2 h> for the value C<18> and German.
 If the parameter C<skip_zero> is trueish then C<---> is returned
 instead of the normal formatting if C<$value> equals 0.
 
-=item C<truncate $text, [%params]>
+=item C<truncate $text, %params>
 
 Returns the C<$text> truncated after a certain number of
-characters.
-
-The number of characters to truncate at is determined by the parameter
-C<at> which defaults to 50. If the text is longer than C<$params{at}>
-then it will be truncated and postfixed with '...'. Otherwise it will
-be returned unmodified.
+characters. See L<Common/truncate> for the actual implementation and
+supported parameters.
 
 =item C<simple_format $text>
 
index 1afd180e396c033afbb0ac28484e810bbb658432..65cb3029529ed9e4f1b205dc8a593eccd9bd0406 100644 (file)
--- a/SL/RP.pm
+++ b/SL/RP.pm
@@ -211,13 +211,25 @@ sub get_accounts {
 
   $sth->finish;
 
+  # filter for opening and closing bookings
   # if l_ob is selected l_cb is always ignored
-  if ( $form->{l_ob} ) {
-    $where .= ' AND ac.ob_transaction is true  '
-  } elsif ( not $form->{l_cb} ) {
-    $where .= ' AND ac.cb_transaction is false ';
+  if ( $last_period ) {
+    # ob/cb-settings for "compared to" balance
+    if ( $form->{l_ob_compared} ) {
+      $where .= ' AND ac.ob_transaction is true  '
+    } elsif ( not $form->{l_cb_compared} ) {
+      $where .= ' AND ac.cb_transaction is false ';
+    };
+  } else {
+    # ob/cb-settings for "as of" balance
+    if ( $form->{l_ob} ) {
+      $where .= ' AND ac.ob_transaction is true  '
+    } elsif ( not $form->{l_cb} ) {
+      $where .= ' AND ac.cb_transaction is false ';
+    };
   };
 
+
   if ($fromdate) {
     $fromdate = conv_dateq($fromdate);
     if ($form->{method} eq 'cash') {
index 40beabb92f1038fdeb7b66c57d435143ba16c283..d9edae200594e16d552cf4506d582bec894b1a96 100644 (file)
@@ -349,7 +349,7 @@ sub prepare_html_content {
         $col->{CELL_ROWS} = [ ];
         foreach my $i (0 .. scalar(@{ $col->{data} }) - 1) {
           push @{ $col->{CELL_ROWS} }, {
-            'data' => $self->html_format($col->{data}->[$i]),
+            'data' => '' . $self->html_format($col->{data}->[$i]),
             'link' => $col->{link}->[$i],
           };
         }
index 5a840a641076202d6a13b3bdd3937b84999fe47b..4479bd73ae7d532a0d2672c397f2114affd15a05 100644 (file)
@@ -522,6 +522,13 @@ an 'AJAX' request.
 
 Returns the requested content type (either C<html>, C<js> or C<json>).
 
+=item C<layout>
+
+Set and retrieve the layout object for the current request. Must be an instance
+of L<SL::Layout::Base>. Defaults to an isntance of L<SL::Layout::None>.
+
+For more information about layouts, see L<SL::Layout::Dispatcher>.
+
 =back
 
 =head1 SPECIAL FUNCTIONS
index cf3463bb7db50497df59aa96b9f8d51a5dd6e2c3..d921a9056716b6272d5ddd73bf1bf9a3a9a8e0ef 100644 (file)
@@ -78,7 +78,8 @@ sub get_tax_info {
   }
 
   my $sth = $self->{handles}->{get_tax_info};
-  do_statement($form, $sth, $self->{queries}->{get_tax_info}, $params{taxkey}, $params{transdate});
+  # Lieferdatum (deliverydate) ist entscheidend für den Steuersatz
+  do_statement($form, $sth, $self->{queries}->{get_tax_info}, $params{taxkey}, $params{deliverydate} || $params{transdate});
 
   my $ref = $sth->fetchrow_hashref() || { };
 
@@ -106,7 +107,7 @@ sub get_full_tax_info {
   my @all_taxkeys = map { $_->{taxkey} } (selectall_hashref_query($form, $form->get_standard_dbh(), qq|SELECT DISTINCT taxkey FROM tax WHERE taxkey IS NOT NULL|));
 
   foreach my $taxkey (@all_taxkeys) {
-    my $ref = $self->get_tax_info('transdate' => $params{transdate}, 'taxkey' => $taxkey);
+    my $ref = $self->get_tax_info('transdate' => $params{transdate}, 'taxkey' => $taxkey, 'deliverydate' => $params{deliverydate});
 
     $tax_info{taxkeys}->{$taxkey}            = $ref;
     $tax_info{accnos}->{$ref->{taxchart_id}} = $ref if ($ref->{taxchart_id});
index a862431d81f4e32c28efedb2f3c3fcb56728be53..291eb9f0ff9b484100f7dbc2ba73cfeafd811571 100644 (file)
@@ -4,7 +4,12 @@ use parent qw(SL::Template::Simple);
 
 use strict;
 
+use Carp;
 use Cwd;
+use English qw(-no_match_vars);
+use File::Basename;
+use File::Temp;
+use List::MoreUtils qw(any);
 use Unicode::Normalize qw();
 
 sub new {
@@ -17,7 +22,6 @@ sub new {
 
 sub format_string {
   my ($self, $variable) = @_;
-  my $form = $self->{"form"};
 
   $variable = $main::locale->quote_special_chars('Template/LaTeX', $variable);
 
@@ -257,6 +261,9 @@ sub _parse_config_option {
   if ($key eq 'tag-style') {
     $self->set_tag_style(split(m/\s+/, $value, 2));
   }
+  if ($key eq 'use-template-toolkit') {
+    $self->set_use_template_toolkit($value);
+  }
 }
 
 sub _parse_config_lines {
@@ -302,7 +309,7 @@ sub _force_mandatory_packages {
       $used_packages{$1} = 1;
       $last_usepackage_line = $i;
 
-    } elsif ($lines->[$i] =~ m/\\begin{document}/) {
+    } elsif ($lines->[$i] =~ m/\\begin\{document\}/) {
       $document_start_line = $i;
       last;
 
@@ -326,7 +333,7 @@ sub parse {
   my $form = $self->{"form"};
 
   if (!open(IN, "$form->{templates}/$form->{IN}")) {
-    $self->{"error"} = "$!";
+    $self->{"error"} = "$form->{templates}/$form->{IN}: $!";
     return 0;
   }
   binmode IN, ":utf8" if $::locale->is_utf8;
@@ -350,7 +357,16 @@ sub parse {
 
   $self->{"forced_pagebreaks"} = [];
 
-  my $new_contents = $self->parse_block($contents);
+  my $new_contents;
+  if ($self->{use_template_toolkit}) {
+    if ($self->{custom_tag_style}) {
+      $contents = "[% TAGS $self->{tag_start} $self->{tag_end} %]\n" . $contents;
+    }
+
+    $::form->init_template->process(\$contents, $form, \$new_contents) || die $::form->template->error;
+  } else {
+    $new_contents = $self->parse_block($contents);
+  }
   if (!defined($new_contents)) {
     $main::lxdebug->leave_sub();
     return 0;
@@ -378,6 +394,7 @@ sub convert_to_postscript {
   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
   # Convert the tex file to postscript
+  local $ENV{TEXINPUTS} = ".:" . $form->{cwd} . "/" . $form->{templates} . ":" . $ENV{TEXINPUTS};
 
   if (!chdir("$userspath")) {
     $self->{"error"} = "chdir : $!";
@@ -427,6 +444,7 @@ sub convert_to_pdf {
   my ($form, $userspath) = ($self->{"form"}, $self->{"userspath"});
 
   # Convert the tex file to PDF
+  local $ENV{TEXINPUTS} = ".:" . $form->{cwd} . "/" . $form->{templates} . ":" . $ENV{TEXINPUTS};
 
   if (!chdir("$userspath")) {
     $self->{"error"} = "chdir : $!";
@@ -458,6 +476,8 @@ sub convert_to_pdf {
   $form->{tmpfile} =~ s/tex$/pdf/;
 
   $self->cleanup();
+
+  return 1;
 }
 
 sub _get_latex_path {
@@ -478,4 +498,54 @@ sub uses_temp_file {
   return 1;
 }
 
+sub parse_and_create_pdf {
+  my ($class, $template_file_name, %params) = @_;
+
+  my $keep_temp                = $::lx_office_conf{debug} && $::lx_office_conf{debug}->{keep_temp_files};
+  my ($tex_fh, $tex_file_name) = File::Temp::tempfile(
+    'kivitendo-printXXXXXX',
+    SUFFIX => '.tex',
+    DIR    => $::lx_office_conf{paths}->{userspath},
+    UNLINK => $keep_temp ? 0 : 1,,
+  );
+
+  my $old_wd               = getcwd();
+
+  my $local_form           = Form->new('');
+  $local_form->{cwd}       = $old_wd;
+  $local_form->{IN}        = $template_file_name;
+  $local_form->{tmpdir}    = $::lx_office_conf{paths}->{userspath};
+  $local_form->{tmpfile}   = $tex_file_name;
+  $local_form->{templates} = $::myconfig{templates};
+
+  foreach (keys %params) {
+    croak "The parameter '$_' must not be used." if exists $local_form->{$_};
+    $local_form->{$_} = $params{$_};
+  }
+
+  my $error;
+  eval {
+    my $template = SL::Template::LaTeX->new($template_file_name, $local_form, \%::myconfig, $::lx_office_conf{paths}->{userspath});
+    my $result   = $template->parse($tex_fh) && $template->convert_to_pdf;
+
+    die $template->{error} unless $result;
+
+    1;
+  } or do { $error = $EVAL_ERROR; };
+
+  chdir $old_wd;
+  close $tex_fh;
+
+  if ($keep_temp) {
+    chmod(((stat $tex_file_name)[2] & 07777) | 0660, $tex_file_name);
+  } else {
+    my $tmpfile =  $tex_file_name;
+    $tmpfile    =~ s/\.\w+$//;
+    unlink(grep { !m/\.pdf$/ } <$tmpfile.*>);
+  }
+
+  return (error     => $error) if $error;
+  return (file_name => do { $tex_file_name =~ s/tex$/pdf/; $tex_file_name });
+}
+
 1;
index f9507f9ba4982127640aaadd5f612f5c8170c575..4eed5eae20ad54151a841438974fa0bf9ed94c4f 100644 (file)
@@ -260,7 +260,14 @@ sub parse {
   $contents =~ s|</office:automatic-styles>|${new_styles}</office:automatic-styles>|;
   $contents =~ s|[\n\r]||gm;
 
-  my $new_contents = $self->parse_block($contents);
+  my $new_contents;
+  if ($self->{use_template_toolkit}) {
+    my $additional_params = $::form;
+
+    $::form->init_template->process(\$contents, $additional_params, \$new_contents) || die $::form->template->error;
+  } else {
+    $new_contents = $self->parse_block($contents);
+  }
   if (!defined($new_contents)) {
     $main::lxdebug->leave_sub();
     return 0;
index 0bdef40f98bc04bd31a2656631db14ad560ab4fa..fa18692156e6eebcd392f9a7b93ea66043798d10 100644 (file)
@@ -7,6 +7,7 @@ use List::Util qw(max);
 use Scalar::Util qw(blessed);
 
 use SL::Presenter;
+use SL::Util qw(_hashify);
 
 use strict;
 
@@ -30,10 +31,6 @@ sub _J {
   return $string;
 }
 
-sub _hashify {
-  return (@_ && (ref($_[0]) eq 'HASH')) ? %{ $_[0] } : @_;
-}
-
 sub new {
   my ($class, $context, @args) = @_;
 
@@ -69,9 +66,13 @@ sub input_tag     { return _call_presenter('input_tag',     @_); }
 sub truncate      { return _call_presenter('truncate',      @_); }
 sub simple_format { return _call_presenter('simple_format', @_); }
 
+sub _set_id_attribute {
+  my ($attributes, $name) = @_;
+  SL::Presenter::Tag::_set_id_attribute($attributes, $name);
+}
+
 sub img_tag {
-  my ($self, @slurp) = @_;
-  my %options = _hashify(@slurp);
+  my ($self, %options) = _hashify(1, @_);
 
   $options{alt} ||= '';
 
@@ -79,10 +80,9 @@ sub img_tag {
 }
 
 sub textarea_tag {
-  my ($self, $name, $content, @slurp) = @_;
-  my %attributes      = _hashify(@slurp);
+  my ($self, $name, $content, %attributes) = _hashify(3, @_);
 
-  $attributes{id}   ||= $self->name_to_id($name);
+  _set_id_attribute(\%attributes, $name);
   $attributes{rows}  *= 1; # required by standard
   $attributes{cols}  *= 1; # required by standard
   $content            = $content ? _H($content) : '';
@@ -91,10 +91,9 @@ sub textarea_tag {
 }
 
 sub checkbox_tag {
-  my ($self, $name, @slurp) = @_;
-  my %attributes       = _hashify(@slurp);
+  my ($self, $name, %attributes) = _hashify(2, @_);
 
-  $attributes{id}    ||= $self->name_to_id($name);
+  _set_id_attribute(\%attributes, $name);
   $attributes{value}   = 1 unless defined $attributes{value};
   my $label            = delete $attributes{label};
   my $checkall         = delete $attributes{checkall};
@@ -113,12 +112,10 @@ sub checkbox_tag {
 }
 
 sub radio_button_tag {
-  my $self             = shift;
-  my $name             = shift;
-  my %attributes       = _hashify(@_);
+  my ($self, $name, %attributes) = _hashify(2, @_);
 
+  _set_id_attribute(\%attributes, $name);
   $attributes{value}   = 1 unless defined $attributes{value};
-  $attributes{id}    ||= $self->name_to_id($name . "_" . $attributes{value});
   my $label            = delete $attributes{label};
 
   if ($attributes{checked}) {
@@ -134,8 +131,8 @@ sub radio_button_tag {
 }
 
 sub hidden_tag {
-  my ($self, $name, $value, @slurp) = @_;
-  return $self->input_tag($name, $value, _hashify(@slurp), type => 'hidden');
+  my ($self, $name, $value, %attributes) = _hashify(3, @_);
+  return $self->input_tag($name, $value, %attributes, type => 'hidden');
 }
 
 sub div_tag {
@@ -154,8 +151,7 @@ sub li_tag {
 }
 
 sub link {
-  my ($self, $href, $content, @slurp) = @_;
-  my %params = _hashify(@slurp);
+  my ($self, $href, $content, %params) = _hashify(3, @_);
 
   $href ||= '#';
 
@@ -163,8 +159,7 @@ sub link {
 }
 
 sub submit_tag {
-  my ($self, $name, $value, @slurp) = @_;
-  my %attributes = _hashify(@slurp);
+  my ($self, $name, $value, %attributes) = _hashify(3, @_);
 
   if ( $attributes{confirm} ) {
     $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
@@ -174,18 +169,28 @@ sub submit_tag {
 }
 
 sub button_tag {
-  my ($self, $onclick, $value, @slurp) = @_;
-  my %attributes = _hashify(@slurp);
+  my ($self, $onclick, $value, %attributes) = _hashify(3, @_);
 
-  $attributes{id}   ||= $self->name_to_id($attributes{name}) if $attributes{name};
+  _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
   $attributes{type} ||= 'button';
 
+  $onclick = 'if (!confirm("'. _J(delete($attributes{confirm})) .'")) return false; ' . $onclick if $attributes{confirm};
+
   return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
 }
 
+sub ajax_submit_tag {
+  my ($self, $url, $form_selector, $text, @slurp) = @_;
+
+  $url           = _J($url);
+  $form_selector = _J($form_selector);
+  my $onclick    = qq|submit_ajax_form('${url}', '${form_selector}')|;
+
+  return $self->button_tag($onclick, $text, @slurp);
+}
+
 sub yes_no_tag {
-  my ($self, $name, $value) = splice @_, 0, 3;
-  my %attributes            = _hashify(@_);
+  my ($self, $name, $value, %attributes) = _hashify(3, @_);
 
   return $self->select_tag($name, [ [ 1 => $::locale->text('Yes') ], [ 0 => $::locale->text('No') ] ], default => $value ? 1 : 0, %attributes);
 }
@@ -211,16 +216,14 @@ sub stylesheet_tag {
 
 my $date_tag_id_idx = 0;
 sub date_tag {
-  my ($self, $name, $value, @slurp) = @_;
+  my ($self, $name, $value, %params) = _hashify(3, @_);
 
-  my %params   = _hashify(@slurp);
-  my $id       = $self->name_to_id($name) . _tag_id();
+  _set_id_attribute(\%params, $name);
   my @onchange = $params{onchange} ? (onChange => delete $params{onchange}) : ();
   my @class    = $params{no_cal} || $params{readonly} ? () : (class => 'datepicker');
 
   return $self->input_tag(
     $name, blessed($value) ? $value->to_lxoffice : $value,
-    id     => $id,
     size   => 11,
     onblur => "check_right_date_format(this);",
     %params,
@@ -283,8 +286,7 @@ sub javascript_tag {
 }
 
 sub tabbed {
-  my ($self, $tabs, @slurp) = @_;
-  my %params   = _hashify(@slurp);
+  my ($self, $tabs, %params) = _hashify(2, @_);
   my $id       = $params{id} || 'tab_' . _tag_id();
 
   $params{selected} *= 1;
@@ -310,8 +312,7 @@ sub tabbed {
 }
 
 sub tab {
-  my ($self, $name, $src, @slurp) = @_;
-  my %params = _hashify(@slurp);
+  my ($self, $name, $src, %params) = _hashify(3, @_);
 
   $params{method} ||= 'process';
 
@@ -332,8 +333,7 @@ sub tab {
 }
 
 sub areainput_tag {
-  my ($self, $name, $value, @slurp) = @_;
-  my %attributes      = _hashify(@slurp);
+  my ($self, $name, $value, %attributes) = _hashify(3, @_);
 
   my ($rows, $cols);
   my $min  = delete $attributes{min_rows} || 1;
@@ -351,8 +351,7 @@ sub areainput_tag {
 }
 
 sub multiselect2side {
-  my ($self, $id, @slurp) = @_;
-  my %params              = _hashify(@slurp);
+  my ($self, $id, %params) = _hashify(2, @_);
 
   $params{labelsx}        = "\"" . _J($params{labelsx} || $::locale->text('Available')) . "\"";
   $params{labeldx}        = "\"" . _J($params{labeldx} || $::locale->text('Selected'))  . "\"";
@@ -371,8 +370,7 @@ EOCODE
 }
 
 sub sortable_element {
-  my ($self, $selector, @slurp) = @_;
-  my %params                    = _hashify(@slurp);
+  my ($self, $selector, %params) = _hashify(2, @_);
 
   my %attributes = ( distance => 5,
                      helper   => <<'JAVASCRIPT' );
@@ -429,8 +427,7 @@ JAVASCRIPT
 }
 
 sub online_help_tag {
-  my ($self, $tag, @slurp) = @_;
-  my %params               = _hashify(@slurp);
+  my ($self, $tag, %params) = _hashify(2, @_);
   my $cc                   = $::myconfig{countrycode};
   my $file                 = "doc/online/$cc/$tag.html";
   my $text                 = $params{text} || $::locale->text('Help');
@@ -447,8 +444,7 @@ sub dump {
 }
 
 sub sortable_table_header {
-  my ($self, $by, @slurp) = @_;
-  my %params              = _hashify(@slurp);
+  my ($self, $by, %params) = _hashify(2, @_);
 
   my $controller          = $self->{CONTEXT}->stash->get('SELF');
   my $sort_spec           = $controller->get_sort_spec;
@@ -479,7 +475,7 @@ sub paginate_controls {
   my %template_params = (
     pages             => \%paginate_params,
     url_maker         => sub {
-      my %url_params                                    = _hashify(@_);
+      my %url_params                                    = _hashify(0, @_);
       $url_params{ $paginate_spec->{FORM_PARAMS}->[0] } = delete $url_params{page};
       $url_params{ $paginate_spec->{FORM_PARAMS}->[1] } = delete $url_params{per_page} if exists $url_params{per_page};
 
@@ -519,6 +515,10 @@ Usage from a template:
 A module modeled a bit after Rails' ActionView helpers. Several small
 functions that create HTML tags from various kinds of data sources.
 
+The C<id> attribute is usually calculated automatically. This can be
+overridden by either specifying an C<id> attribute or by setting
+C<no_id> to trueish.
+
 =head1 FUNCTIONS
 
 =head2 LOW-LEVEL FUNCTIONS
@@ -572,9 +572,28 @@ tag's C<id> defaults to C<name_to_id($name)>.
 
 If C<$attributes{confirm}> is set then a JavaScript popup dialog will
 be added via the C<onclick> handler asking the question given with
-C<$attributes{confirm}>. If request is only submitted if the user
+C<$attributes{confirm}>. The request is only submitted if the user
 clicks the dialog's ok/yes button.
 
+=item C<ajax_submit_tag $url, $form_selector, $text, %attributes>
+
+Creates a HTML 'input type="button"' tag with a very specific onclick
+handler that submits the form given by the jQuery selector
+C<$form_selector> to the URL C<$url> (the actual JavaScript function
+called for that is C<submit_ajax_form()> in C<js/client_js.js>). The
+button's label will be C<$text>.
+
+=item C<button_tag $onclick, $text, %attributes>
+
+Creates a HTML 'input type="button"' tag with an onclick handler
+C<$onclick> and a value of C<$text>. The button does not have a name
+nor an ID by default.
+
+If C<$attributes{confirm}> is set then a JavaScript popup dialog will
+be prepended to the C<$onclick> handler asking the question given with
+C<$attributes{confirm}>. The request is only submitted if the user
+clicks the dialog's "ok/yes" button.
+
 =item C<textarea_tag $name, $value, %attributes>
 
 Creates a HTML 'textarea' tag named C<$name> with the content
diff --git a/SL/Template/Plugin/LxLatex.pm b/SL/Template/Plugin/LxLatex.pm
new file mode 100644 (file)
index 0000000..fbbd53c
--- /dev/null
@@ -0,0 +1,27 @@
+package SL::Template::Plugin::LxLatex;
+
+use strict;
+use parent qw( Template::Plugin::Filter );
+
+my $cached_instance;
+
+sub new {
+  my $class = shift;
+
+  return $cached_instance ||= $class->SUPER::new(@_);
+}
+
+sub init {
+  my $self = shift;
+
+  $self->install_filter($self->{ _ARGS }->[0] || 'LxLatex');
+
+  return $self;
+}
+
+sub filter {
+  my ($self, $text, $args) = @_;
+  return $::locale->quote_special_chars('Template/LaTeX', $text);
+}
+
+return 'SL::Template::Plugin::LxLatex';
index 926ef1f7f95347abaef79354467d3fe6601541fa..be5ba8b5db1beecc82178200fcdff44f9a030625 100644 (file)
@@ -48,6 +48,7 @@ sub set_tag_style {
   my $tag_start               = shift;
   my $tag_end                 = shift;
 
+  $self->{custom_tag_style}   = 1;
   $self->{tag_start}          = $tag_start;
   $self->{tag_end}            = $tag_end;
   $self->{tag_start_qm}       = quotemeta $tag_start;
@@ -56,6 +57,13 @@ sub set_tag_style {
   $self->{substitute_vars_re} = "$self->{tag_start_qm}(.+?)$self->{tag_end_qm}";
 }
 
+sub set_use_template_toolkit {
+  my $self                    = shift;
+  my $value                   = shift;
+
+  $self->{use_template_toolkit} = $value;
+}
+
 sub cleanup {
   my ($self) = @_;
 }
index 4f8cb43451ee98ce431c6e4d63a8046d6e1f9661..9d6d2993aa43e7e8e264604e2298906b08174edf 100644 (file)
@@ -7,6 +7,7 @@ use parent qw(Rose::Object);
 use Carp;
 use List::MoreUtils qw(any none);
 use SL::DBUtils;
+use SL::PrefixedNumber;
 
 use Rose::Object::MakeMethods::Generic
 (
@@ -68,8 +69,8 @@ sub _get_filters {
   } elsif ($type =~ /part|service|assembly/) {
     $filters{trans_number}  = "partnumber";
     $filters{numberfield}   = $type eq 'service' ? 'servicenumber' : 'articlenumber';
+    $filters{numberfield}   = $type eq 'assembly' ? 'assemblynumber' : $filters{numberfield};
     $filters{table}         = "parts";
-    $filters{where}         = 'COALESCE(inventory_accno_id, 0) ' . ($type eq 'service' ? '=' : '<>') . ' 0';
   }
 
   return %filters;
@@ -128,17 +129,15 @@ SQL
   ($business_number) = selectfirst_array_query($form, $self->dbh, qq|SELECT customernumberinit FROM business WHERE id = ?|, $self->business_id) if $self->business_id;
   my $number         = $business_number;
   ($number)          = selectfirst_array_query($form, $self->dbh, qq|SELECT $filters{numberfield} FROM defaults|)                               if !$number;
+  if ($filters{numberfield} eq 'assemblynumber' and length($number) < 1) {
+    $filters{numberfield} = 'articlenumber';
+    ($number)          = selectfirst_array_query($form, $self->dbh, qq|SELECT $filters{numberfield} FROM defaults|)                               if !$number;
+  }
   $number          ||= '';
+  my $sequence       = SL::PrefixedNumber->new(number => $number);
 
   do {
-    if ($number =~ m/\d+$/) {
-      my $new_number = substr($number, $-[0]) * 1 + 1;
-      my $len_diff   = length($number) - $-[0] - length($new_number);
-      $number        = substr($number, 0, $-[0]) . ($len_diff > 0 ? '0' x $len_diff : '') . $new_number;
-
-    } else {
-      $number = $number . '1';
-    }
+    $number = $sequence->get_next;
   } while ($numbers_in_use{$number});
 
   if ($self->save) {
diff --git a/SL/Util.pm b/SL/Util.pm
new file mode 100644 (file)
index 0000000..2fdd9c1
--- /dev/null
@@ -0,0 +1,103 @@
+package SL::Util;
+
+use strict;
+
+use parent qw(Exporter);
+
+use Carp;
+
+our @EXPORT_OK = qw(_hashify camelify snakify);
+
+sub _hashify {
+  my $keep = shift;
+
+  croak "Invalid number of entries to keep" if 0 > $keep;
+
+  return @_[0..scalar(@_) - 1] if $keep >= scalar(@_);
+  return ($keep ? @_[0..$keep - 1] : (),
+          ((1 + $keep) == scalar(@_)) && ((ref($_[$keep]) || '') eq 'HASH') ? %{ $_[$keep] } : @_[$keep..scalar(@_) - 1]);
+}
+
+sub camelify {
+  my ($str) = @_;
+  $str =~ s/_+([[:lower:]])/uc($1)/ge;
+  ucfirst $str;
+}
+
+sub snakify {
+  my ($str) = @_;
+  $str =~ s/_([[:upper:]])/'_' . lc($1)/ge;
+  $str =~ s/(?<!^)([[:upper:]])/'_' . lc($1)/ge;
+  lc $str;
+}
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Util - Assorted utility functions
+
+=head1 OVERVIEW
+
+Most important things first:
+
+DO NOT USE C<@EXPORT> HERE! Only C<@EXPORT_OK> is allowed!
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<_hashify $num, @args>
+
+Hashifies the very last argument. Returns a list consisting of two
+parts:
+
+The first part are the first C<$num> elements of C<@args>.
+
+The second part depends on the remaining arguments. If exactly one
+argument remains and is a hash reference then its dereferenced
+elements will be used. Otherwise the remaining elements of C<@args>
+will be returned as-is.
+
+Useful if you want to write code that can be called from Perl code and
+Template code both. Example:
+
+  use SL::Util qw(_hashify);
+
+  sub do_stuff {
+    my ($self, %params) = _hashify(1, @_);
+    # Now do stuff, obviously!
+  }
+
+=item C<camilify $string>
+
+Returns C<$string> converted from underscore-style to
+camel-case-style, e.g. for the string C<stupid_example_dude> it will
+return C<StupidExampleDude>.
+
+L</snakify> does the reverse.
+
+=item C<snakify $string>
+
+Returns C<$string> converted from camel-case-style to
+underscore-style, e.g. for the string C<EvenWorseExample> it will
+return C<even_worse_example>.
+
+L</camilify> does the reverse.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
diff --git a/SL/X.pm b/SL/X.pm
index 0f206949bb34f03e5f68f08e727985533c105bcd..552e5ef29a0e3976f4f58a6dc5227d2a0cd51469 100644 (file)
--- a/SL/X.pm
+++ b/SL/X.pm
@@ -5,6 +5,6 @@ use strict;
 use Exception::Lite qw(declareExceptionClass);
 
 declareExceptionClass('SL::X::FormError');
-declareExceptionClass('SL::X::DBHookError', [ '%s hook \'%s\' failed', qw(when hook object) ]);
+declareExceptionClass('SL::X::DBHookError', [ '%s hook \'%s\' for object type \'%s\' failed', qw(when hook object_type object) ]);
 
 1;
index bcb39d258f066a65813f4ca81a272541914ae4be..ebe7ed1879b1260722e9157a0408ef26a9904c79 100644 (file)
@@ -1461,6 +1461,13 @@ sub add_tax {
 
   _get_taxaccount_selection();
 
+  $form->{asset}      = 1;
+  $form->{liability}  = 1;
+  $form->{equity}     = 1;
+  $form->{revenue}    = 1;
+  $form->{expense}    = 1;
+  $form->{costs}      = 1;
+
   $form->header();
 
   my $parameters_ref = {
@@ -1485,17 +1492,21 @@ sub edit_tax {
   $form->{title} =  $locale->text('Edit');
 
   AM->get_tax(\%myconfig, \%$form);
+
   _get_taxaccount_selection();
 
+  $form->{asset}      = $form->{chart_categories} =~ 'A' ? 1 : 0;
+  $form->{liability}  = $form->{chart_categories} =~ 'L' ? 1 : 0;
+  $form->{equity}     = $form->{chart_categories} =~ 'Q' ? 1 : 0;
+  $form->{revenue}    = $form->{chart_categories} =~ 'I' ? 1 : 0;
+  $form->{expense}    = $form->{chart_categories} =~ 'E' ? 1 : 0;
+  $form->{costs}      = $form->{chart_categories} =~ 'C' ? 1 : 0;
+
   $form->{rate} = $form->format_amount(\%myconfig, $form->{rate}, 2);
 
   $form->header();
 
-  #set readonly if the there are entries in acc_trans with the tax
-  my $readonly = $form->{tax_already_used} ? 'readonly' : '';
-
   my $parameters_ref = {
-    readonly => $readonly, 
   };
 
   # Ausgabe des Templates
index 8a30094fad969c104cb289a1c53404bb78252510..797ee393e3a8f38a13c54ee3470898f1969eb58e 100644 (file)
@@ -305,8 +305,8 @@ sub list_contacts {
   my $cvar_configs = CVar->get_configs('module' => 'Contacts');
 
   my @columns      = qw(
-    cp_id vcname vcnumber cp_name cp_givenname cp_street cp_zipcode cp_city cp_phone1 cp_phone2
-    cp_mobile1 cp_mobile2 cp_email cp_abteilung cp_position cp_birthday cp_gender
+    cp_id vcname vcnumber cp_name cp_givenname cp_street cp_zipcode cp_city cp_phone1 cp_phone2 cp_privatphone
+    cp_mobile1 cp_mobile2 cp_fax cp_email cp_privatemail cp_abteilung cp_position cp_birthday cp_gender
   );
 
   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
@@ -342,6 +342,9 @@ sub list_contacts {
     'cp_position'  => { 'text' => $::locale->text('Function/position'), },
     'cp_birthday'  => { 'text' => $::locale->text('Birthday'), },
     'cp_gender'    => { 'text' => $::locale->text('Gender'), },
+    'cp_fax'       => { 'text' => $::locale->text('Fax'), },
+    'cp_privatphone' => { 'text' => $::locale->text('Private Phone') },
+    'cp_privatemail' => { 'text' => $::locale->text('Private E-mail') },
     %column_defs_cvars,
   );
 
@@ -399,7 +402,10 @@ sub list_contacts {
 
     $row->{vcname}->{link}   = build_std_url('action=edit', 'id=' . E($ref->{vcid}), 'db=' . E($ref->{db}), 'callback', @hidden_nondefault);
     $row->{vcnumber}->{link} = $row->{vcname}->{link};
-    $row->{cp_email}->{link} = 'mailto:' . E($ref->{cp_email});
+
+    for (qw(cp_email cp_privatemail)) {
+      $row->{$_}->{link} = 'mailto:' . E($ref->{$_}) if $ref->{$_};
+    }
 
     $report->add_data($row);
   }
index 248bbb1f83e5261cbde9a776cdb3e673acd24fcb..576564fe78f22f611099cacf1b61bc75ad78b964 100644 (file)
@@ -313,8 +313,6 @@ sub form_header {
 
   $form->{follow_up_trans_info} = $form->{donumber} .'('. $follow_up_vc .')';
 
-  $::request->layout->use_stylesheet('presenter/record/record_list.css');
-
   $form->header();
   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
   # und Erweiterung für Bug 1760:
index 626d688179aadbad19dc93eb787a50ad8b943045..4c97bd028f9aa2c26eaedfb206b8da531b1c9075 100644 (file)
@@ -304,19 +304,21 @@ sub generate_report {
   );
 
   # add employee here, so that variable is still known and passed in url when choosing a different sort order in resulting table
-  my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee datesort category l_subtotal);
+  my @hidden_variables = qw(accno source reference department description notes project_id datefrom dateto employee_id datesort category l_subtotal);
   push @hidden_variables, map { "l_${_}" } @columns;
   foreach ( @hidden_variables ) {
       print URL "$_\n";
   };
 
+  my $employee = $form->{employee_id} ? SL::DB::Employee->new(id => $form->{employee_id})->load->name : '';
+
   my (@options, @date_options);
   push @options,      $locale->text('Account')     . " : $form->{accno} $form->{account_description}" if ($form->{accno});
   push @options,      $locale->text('Source')      . " : $form->{source}"                             if ($form->{source});
   push @options,      $locale->text('Reference')   . " : $form->{reference}"                          if ($form->{reference});
   push @options,      $locale->text('Description') . " : $form->{description}"                        if ($form->{description});
   push @options,      $locale->text('Notes')       . " : $form->{notes}"                              if ($form->{notes});
-  push @options,      $locale->text('Employee')       . " : $form->{employee_name}"                              if ($form->{employee_name});
+  push @options,      $locale->text('Employee')    . " : $employee"                                   if $employee;
   my $datesorttext = $form->{datesort} eq 'transdate' ? $locale->text('Invoice Date') :  $locale->text('Booking Date');
   push @date_options,      "$datesorttext"                              if ($form->{datesort} and ($form->{datefrom} or $form->{dateto}));
   push @date_options, $locale->text('From'), $locale->date(\%myconfig, $form->{datefrom}, 1)          if ($form->{datefrom});
@@ -684,7 +686,7 @@ sub display_rows {
   my %taxchart_labels = ();
   my @taxchart_values = ();
   my %taxcharts = ();
-  foreach my $item (@{ $form->{ALL_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);
@@ -728,7 +730,7 @@ sub display_rows {
     my $accno = qq|<td>| .
       NTI($cgi->popup_menu('-name' => "accno_$i",
                            '-id' => "accno_$i",
-                           '-onChange' => "setTaxkey($i)",
+                           '-onChange' => "updateTaxes($i);",
                            '-style' => 'width:200px',
                            '-values' => \@chart_values,
                            '-labels' => \%chart_labels,
@@ -860,8 +862,10 @@ sub form_header {
                                     "all"       => 0,
                                     "old_id"    => \@old_project_ids },
                    "charts"    => { "key"       => "ALL_CHARTS",
-                                    "transdate" => $::form->{transdate} },
-                   "taxcharts" => "ALL_TAXCHARTS");
+                                    "transdate" => $::form->{transdate} });
+
+  $::form->{accno} = $::form->{ALL_CHARTS}[0]->{accno};
+  GL->get_tax_dropdown();
 
   GL->get_chart_balances('charts' => $::form->{ALL_CHARTS});
 
@@ -1217,4 +1221,21 @@ sub continue {
   call_sub($main::form->{nextsub});
 }
 
+sub get_tax_dropdown {
+
+  my $form = $main::form;
+  $main::lxdebug->enter_sub();
+  GL->get_tax_dropdown();
+
+  foreach my $item (@{ $form->{TAX_ACCOUNTS} }) {
+    $item->{taxdescription} = $::locale->{iconv_utf8}->convert($item->{taxdescription});
+    $item->{taxdescription} .= ' ' . $form->round_amount($item->{rate} * 100);
+  }
+
+  print $form->ajax_response_header, $form->parse_html_template("gl/update_tax_accounts");
+
+  $main::lxdebug->leave_sub();
+
+}
+
 1;
index 17d589f56e3660f0c00754875fd90236763dbcc2..8200b01100700d6e758bdfc3c5072fb609a7b1bb 100644 (file)
@@ -1674,15 +1674,15 @@ sub assembly_row {
   }
 
   my %header = (
-   runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%'  },
-   qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%' },
-   unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%'  },
-   partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%' },
-   description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%' },
-   lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%' },
-   total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                },
-   bom           => { text =>  $locale->text('BOM'),                                          },
-   partsgroup    => { text =>  $locale->text('Group'),                                        },
+   runningnumber => { text =>  $locale->text('No.'),              nowrap => 1, width => '5%',  align => 'left',},
+   qty           => { text =>  $locale->text('Qty'),              nowrap => 1, width => '10%', align => 'left',},
+   unit          => { text =>  $locale->text('Unit'),             nowrap => 1, width => '5%',  align => 'left',},
+   partnumber    => { text =>  $locale->text('Part Number'),      nowrap => 1, width => '20%', align => 'left',},
+   description   => { text =>  $locale->text('Part Description'), nowrap => 1, width => '50%', align => 'left',},
+   lastcost      => { text =>  $locale->text('Purchase Prices'),  nowrap => 1, width => '50%', align => 'right',},
+   total         => { text =>  $locale->text('Sale Prices'),      nowrap => 1,                 align => 'right',},
+   bom           => { text =>  $locale->text('BOM'),                                           align => 'center',},
+   partsgroup    => { text =>  $locale->text('Group'),                                         align => 'left',},
   );
 
   my @ROWS;
@@ -1760,12 +1760,14 @@ sub update {
 
   # parse pricegroups. and no, don't rely on check_form for this...
   map { $form->{"price_$_"} = $form->parse_amount(\%myconfig, $form->{"price_$_"}) } 1 .. $form->{price_rows};
+  $form->{sellprice} = $form->parse_amount(\%myconfig, $form->{sellprice});
 
   # same for makemodel lastcosts
   # but parse_amount not necessary for assembly component lastcosts
   unless ($form->{item} eq "assembly") {
     map { $form->{"lastcost_$_"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$_"}) } 1 .. $form->{"makemodel_rows"};
   };
+  $form->{listprice} = $form->parse_amount(\%myconfig, $form->{listprice});
 
   if ($form->{item} eq "assembly") {
     my $i = $form->{assembly_rows};
index 4ec2729255f4e3c1fbc0e7f1a201399f5141afbc..4db8256c26c67b45e0662e11804c0613311d1538 100644 (file)
@@ -571,7 +571,6 @@ sub item_selected {
   map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
     qw(sellprice listprice weight);
 
-  $form->{sellprice} += ($form->{"sellprice_$i"} * $form->{"qty_$i"});
   $form->{weight}    += ($form->{"weight_$i"} * $form->{"qty_$i"});
 
   if ($form->{"not_discountable_$i"}) {
@@ -1336,7 +1335,7 @@ sub print_form {
       call_sub($display_form);
       # saving the history
       if(!exists $form->{addition}) {
-        $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
+        $form->{snumbers} = "${inv}number" . "_" . $form->{"${inv}number"};
         $form->{addition} = "PRINTED";
         $form->save_history;
       }
@@ -1394,7 +1393,7 @@ sub print_form {
 
   # create the form variables
   if ($form->{type} =~ /_delivery_order$/) {
-    DO->order_details();
+    DO->order_details(\%myconfig, \%$form);
   } elsif ($order) {
     OE->order_details(\%myconfig, \%$form);
   } else {
@@ -1577,7 +1576,7 @@ sub print_form {
 
 # saving the history
   if(!exists $form->{addition}) {
-    $form->{snumbers} = qq|ordnumber_| . $form->{ordnumber};
+    $form->{snumbers} = "${inv}number" . "_" . $form->{"${inv}number"};
     if($form->{media} =~ /printer/) {
       $form->{addition} = "PRINTED";
     }
@@ -1597,11 +1596,11 @@ sub print_form {
   # prepare meta information for template introspection
   $form->{template_meta} = {
     formname  => $form->{formname},
-    language  => SL::DB::Manager::Language->find_by_or_create(id => $form->{language_id}),
+    language  => SL::DB::Manager::Language->find_by_or_create(id => $form->{language_id} || undef),
     format    => $form->{format},
     media     => $form->{media},
     extension => $extension,
-    printer   => SL::DB::Manager::Printer->find_by_or_create(id => $form->{printer_id}),
+    printer   => SL::DB::Manager::Printer->find_by_or_create(id => $form->{printer_id} || undef),
     today     => DateTime->today,
   };
 
@@ -1770,7 +1769,7 @@ sub set_duedate {
   my $invdate = $form->{invdate} eq 'undefined' ? undef : $form->{invdate};
   my $duedate = $form->get_duedate(\%myconfig, $invdate);
 
-  print $form->ajax_response_header() . $duedate;
+  print $form->ajax_response_header() . ($duedate || $invdate);
 
   $main::lxdebug->leave_sub();
 }
index 9b400bf928798fbba165efd7b8ff0e992cf035a6..4209fdf6addc989e1d9f43bc19fe2abc70df7e07 100644 (file)
@@ -358,7 +358,6 @@ sub form_header {
   map { $_.'_rate', $_.'_description', $_.'_taxnumber' } split / /, $form->{taxaccounts}];
 
   $form->{jsscript} = 1;
-  $::request->layout->use_stylesheet('presenter/record/record_list.css');
   $form->header();
 
   print $form->parse_html_template("ir/form_header", \%TMPL_VAR);
index 8546cb6658e1880dc165f4c022d54ec9dc9af967..cbddde60d85ed8b36233c465ef097a474ee34c4b 100644 (file)
@@ -382,7 +382,6 @@ sub form_header {
   map { $_.'_rate', $_.'_description', $_.'_taxnumber' } split / /, $form->{taxaccounts}];
 
   $form->{jsscript} = 1;
-  $::request->layout->use_stylesheet('presenter/record/record_list.css');
   $form->header();
 
   print $form->parse_html_template("is/form_header", \%TMPL_VAR);
@@ -411,7 +410,7 @@ sub form_footer {
   my ($tax, $subtotal);
   $form->{taxaccounts_array} = [ split(/ /, $form->{taxaccounts}) ];
 
-  if ($form->{customer_id}) {
+  if( $form->{customer_id} && !$form->{taxincluded_changed_by_user} ) {
     my $customer = SL::DB::Customer->new(id => $form->{customer_id})->load();
     $form->{taxincluded} = defined($customer->taxincluded_checked) ? $customer->taxincluded_checked : $myconfig{taxincluded_checked};
   }
index cfdb11aa021166b7897ed479ae5d4b8ee4b6d349..3dbb113d11c9f60923338c5fcb685bf44771293f 100644 (file)
@@ -436,8 +436,6 @@ sub form_header {
   $form->{javascript} .= qq|<script type="text/javascript" src="js/show_history.js"></script>|;
   $form->{javascript} .= qq|<script type="text/javascript" src="js/show_vc_details.js"></script>|;
 
-  $::request->layout->use_stylesheet('presenter/record/record_list.css');
-
   $form->header;
 
   $TMPL_VAR{HIDDENS} = [ map { name => $_, value => $form->{$_} },
@@ -483,7 +481,7 @@ sub form_footer {
   $TMPL_VAR{notes}    = qq|<textarea name=notes rows="$rows" cols="25">| . H($form->{notes}) . qq|</textarea>|;
   $TMPL_VAR{intnotes} = qq|<textarea name=intnotes rows="$introws" cols="35">| . H($form->{intnotes}) . qq|</textarea>|;
 
-  if ($form->{customer_id}) {
+  if( $form->{customer_id} && !$form->{taxincluded_changed_by_user} ) {
     my $customer = SL::DB::Customer->new(id => $form->{customer_id})->load();
     $form->{taxincluded} = defined($customer->taxincluded_checked) ? $customer->taxincluded_checked : $myconfig{taxincluded_checked};
   }
index 7d4245bfe0f8e37b5fe426ffff6cf8bb92bb03e5..f1aeb47f29e06709896567a8b2e660b2928cf670 100644 (file)
@@ -128,6 +128,9 @@ sub update {
   $::lxdebug->enter_sub;
   $::auth->assert('cash');
 
+  # reset difference as it doesn't always arrive here empty
+  $::form->{difference} = 0;
+
   RC->payment_transactions(\%::myconfig, $::form);
 
   my $i;
diff --git a/css/kivitendo/jqModal.css b/css/kivitendo/jqModal.css
new file mode 100644 (file)
index 0000000..7c8c92f
--- /dev/null
@@ -0,0 +1,37 @@
+/* the overlayed element */
+.jqModal_overlay {
+  position: fixed;
+  top: 50%;
+  margin-top: -250px;
+  height: 500px;
+
+  left: 50%;
+  margin-left: -400px;
+  width: 800px;
+
+  background-color: #fff;
+  border: 1px solid #333;
+
+  /* CSS3 styling for latest browsers */
+  box-shadow: 0 0 90px 5px #000;
+  -moz-box-shadow: 0 0 90px 5px #000;
+  -webkit-box-shadow: 0 0 90px #000;
+
+  padding: 10px;
+}
+
+.jqModal_overlay .overlay_content {
+  width: 790px;
+  height: 490px;
+  overflow: auto;
+}
+
+.jqModal_overlay .close {
+  background-image: url(../../image/dialog-close.png);
+  position: absolute;
+  right: -16px;
+  top: -16px;
+  cursor: pointer;
+  height: 32px;
+  width: 32px;
+}
index 4cad54f27e350e21a08bfd1c8d3d37399b00f86f..12db365e1c3cd32e8b9a3ece96361f078680799f 100644 (file)
@@ -225,12 +225,12 @@ body.menu {
        border-width: 1px;
        background: #FFFFE0;
 }
-.listrow1 {
+.listrow1, .listrow:nth-child(odd) {
        background-color: #FFFFFF;
        color: black;
        vertical-align: top;
 }
-.listrow0 {
+.listrow0, .listrow:nth-child(even) {
        background-color: #FFFF99;
        color: black;
        vertical-align: top;
diff --git a/css/lx-office-erp/jqModal.css b/css/lx-office-erp/jqModal.css
new file mode 100644 (file)
index 0000000..90f9594
--- /dev/null
@@ -0,0 +1,39 @@
+/* the overlayed element */
+.jqModal_overlay {
+  position: fixed;
+  top: 50%;
+  margin-top: -250px;
+  height: 500px;
+
+  left: 50%;
+  margin-left: -400px;
+  width: 800px;
+
+  background-image: url("../../image/fade.png");
+  background-repeat: repeat-x;
+  background-color: #fff;
+  border: 1px solid #333;
+
+  /* CSS3 styling for latest browsers */
+  box-shadow: 0 0 90px 5px #000;
+  -moz-box-shadow: 0 0 90px 5px #000;
+  -webkit-box-shadow: 0 0 90px #000;
+
+  padding: 10px;
+}
+
+.jqModal_overlay .overlay_content {
+  width: 790px;
+  height: 490px;
+  overflow: auto;
+}
+
+.jqModal_overlay .close {
+  background-image: url(../../image/dialog-close.png);
+  position: absolute;
+  right: -16px;
+  top: -16px;
+  cursor: pointer;
+  height: 32px;
+  width: 32px;
+}
index 76ae01814a7734b42dd67a86e627c36bf2cc7be0..1e200b0431ee5212db3d233649d3354a827ba50e 100644 (file)
@@ -257,8 +257,8 @@ div.admin {
 }
 
 
-.listrow1 { background-color: rgb(208,207,201); color: black; vertical-align: top; }
-.listrow0 { background-color: rgb(236,233,216); color: black; vertical-align: top; }
+.listrow1, .listrow:nth-child(odd)  { background-color: rgb(208,207,201); color: black; vertical-align: top; }
+.listrow0, .listrow:nth-child(even) { background-color: rgb(236,233,216); color: black; vertical-align: top; }
 .listrowempty { background-color: rgb(255,255,255); color: black; vertical-align: top; }
 
 .redrow1 { background-color: rgb(250,167, 161); color: black; vertical-align: top; }
@@ -381,36 +381,6 @@ label {
   font-weight: bold;
 }
 
-.jqmWindow {
-  display: none;
-
-  position: fixed;
-  top: 17%;
-  left: 40%;
-
-  margin-left: -200px;
-  width: 700px;
-
-  background-color: lemonchiffon;
-  color: #333;
-  border: 1px solid black;
-  padding: 4px;
-}
-
-.jqmContent {
-  padding: 8px;
-}
-
-.jqmWindow h1 {
-  border: 0;
-  padding: 0;
-  background-color: lemonchiffon;
-}
-
-.jqmOverlay {
-  background-color: #000;
-}
-
 /* Kontenliste Styles */
 
 .coa_listrow1 {
diff --git a/css/presenter/record/record_list.css b/css/presenter/record/record_list.css
deleted file mode 100644 (file)
index 59ab177..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/* the overlayed element */
-.record_list_overlay {
-  position: fixed;
-  top: 50%;
-  margin-top: -250px;
-  height: 500px;
-
-  left: 50%;
-  margin-left: -400px;
-  width: 800px;
-
-  background-color: #fff;
-  border: 1px solid #333;
-
-  /* CSS3 styling for latest browsers */
-  box-shadow: 0 0 90px 5px #000;
-  -moz-box-shadow: 0 0 90px 5px #000;
-  -webkit-box-shadow: 0 0 90px #000;
-
-  padding: 10px;
-}
-
-.record_list_overlay .overlay_content {
-  width: 790px;
-  height: 490px;
-  overflow: auto;
-}
-
-.record_list_overlay .close {
-  background-image: url(../../../image/dialog-close.png);
-  position: absolute;
-  right: -16px;
-  top: -16px;
-  cursor: pointer;
-  height: 32px;
-  width: 32px;
-}
index 0c41875ee4101522de9577af03f59239960390a6..759ff0f01683bac91fa5094926439fd2baf9faf0 100644 (file)
@@ -5568,11 +5568,10 @@ file = /tmp/kivitendo-debug.log</programlisting>
             <term><varname>charset</varname></term>
 
             <listitem>
-              <para>Empfohlen. Gibt den Zeichensatz an, in dem das Script
-              geschrieben wurde, z.B. "<literal>UTF-8</literal>". Aus
-              Kompatibilitätsgründen mit alten Upgrade-Scripten wird bei
-              Abwesenheit des Tags der Zeichensatz
-              "<literal>ISO-8859-15</literal>" angenommen.</para>
+              <para>Empfohlen. Gibt den Zeichensatz an, in dem das Script geschrieben wurde, z.B. "<literal>UTF-8</literal>". Aus
+              Kompatibilitätsgründen mit alten Upgrade-Scripten wird bei Abwesenheit des Tags für SQL-Upgradedateien der Zeichensatz
+              "<literal>ISO-8859-15</literal>" angenommen. Perl-Upgradescripte hingegen müssen immer in UTF-8 encodiert sein und sollten
+              demnach auch ein "<literal>use utf8;</literal>" enthalten.</para>
             </listitem>
           </varlistentry>
 
@@ -5640,6 +5639,48 @@ file = /tmp/kivitendo-debug.log</programlisting>
         </variablelist>
       </sect2>
 
+      <sect2 id="db-upgrade-files.format-perl-files" xreflabel="Format von Perl-Upgradedateien">
+       <title>Format von in Perl geschriebenen Datenbankupgradescripten</title>
+
+       <para>In Perl geschriebene Datenbankscripte werden nicht einfach so ausgeführt sondern müssen sich an gewisse Konventionen
+       halten. Dafür bekommen sie aber auch einige Komfortfunktionen bereitgestellt.</para>
+
+       <para>Ein Upgradescript stellt dabei eine vollständige Objektklasse dar, die vom Elternobjekt
+       "<literal>SL::DBUpgrade2::Base</literal>" erben und eine Funktion namens "<literal>run</literal>" zur Verfügung stellen muss. Das
+       Script wird ausgeführt, indem eine Instanz dieser Klasse erzeugt und darauf die erwähnte "<literal>run</literal>" aufgerufen
+       wird.</para>
+
+       <para>Zu beachten ist, dass sich der Paketname der Datei aus dem Wert für "<literal>@tag</literal>" ableitet. Dabei werden alle
+       Zeichen, die in Paketnamen ungültig wären (gerade Bindestriche), durch Unterstriche ersetzt. Insgesamt sieht der Paketname wie folgt
+       aus: "<literal>SL::DBUpgrade2::tag</literal>".</para>
+
+       <para>Welche Komfortfunktionen zur Verfügung stehen, erfahren Sie in der Perl-Dokumentation zum oben genannten Modul; aufzurufen mit
+       "<command>perldoc SL/DBUpgrade2/Base.pm</command>".</para>
+
+       <para>Ein Mindestgerüst eines gültigen Perl-Upgradescriptes sieht wie folgt aus:</para>
+
+       <programlisting># @tag: beispiel-upgrade-file42
+# @description: Ein schönes Beispielscript
+# @depends: release_3_0_0
+package SL::DBUpgrade2::beispiel_upgrade_file42;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  # hier Aktionen ausführen
+
+  return 1;
+}
+
+1;
+</programlisting>
+      </sect2>
+
       <sect2 id="db-upgrade-files.dbupgrade-tool"
              xreflabel="Hilfsscript dbupgrade2_tool.pl">
         <title>Hilfsscript dbupgrade2_tool.pl</title>
index b8e68f9937321e154f5a783b0953d24ac14f4d5c..39f03d32c1cf0a4c32bf0c821f7977d73a85113d 100644 (file)
               erlaubt und sollten stattdessen mit Unterstrichen ersetzt
               werden.</p></dd><dt><span class="term">
                      <code class="varname">charset</code>
-                  </span></dt><dd><p>Empfohlen. Gibt den Zeichensatz an, in dem das Script
-              geschrieben wurde, z.B. "<code class="literal">UTF-8</code>". Aus
-              Kompatibilitätsgründen mit alten Upgrade-Scripten wird bei
-              Abwesenheit des Tags der Zeichensatz
-              "<code class="literal">ISO-8859-15</code>" angenommen.</p></dd><dt><span class="term">
+                  </span></dt><dd><p>Empfohlen. Gibt den Zeichensatz an, in dem das Script geschrieben wurde, z.B. "<code class="literal">UTF-8</code>". Aus
+              Kompatibilitätsgründen mit alten Upgrade-Scripten wird bei Abwesenheit des Tags für SQL-Upgradedateien der Zeichensatz
+              "<code class="literal">ISO-8859-15</code>" angenommen. Perl-Upgradescripte hingegen müssen immer in UTF-8 encodiert sein und sollten
+              demnach auch ein "<code class="literal">use utf8;</code>" enthalten.</p></dd><dt><span class="term">
                      <code class="varname">description</code>
                   </span></dt><dd><p>Benötigt. Eine Beschreibung, was in diesem Update
               passiert. Diese wird dem Benutzer beim eigentlichen
                      <code class="varname">ignore</code>
                   </span></dt><dd><p>Optional. Falls der Wert auf 1 (true) steht, wird das
               Skript bei der Anmeldung ignoriert und entsprechend nicht
-              ausgeführt.</p></dd></dl></div></div><div class="sect2" title="4.3.3. Hilfsscript dbupgrade2_tool.pl"><div class="titlepage"><div><div><h3 class="title"><a name="db-upgrade-files.dbupgrade-tool"></a>4.3.3. Hilfsscript dbupgrade2_tool.pl</h3></div></div></div><p>Um die Arbeit mit den Abhängigkeiten etwas zu erleichtern,
+              ausgeführt.</p></dd></dl></div></div><div class="sect2" title="4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten"><div class="titlepage"><div><div><h3 class="title"><a name="db-upgrade-files.format-perl-files"></a>4.3.3. Format von in Perl geschriebenen Datenbankupgradescripten</h3></div></div></div><p>In Perl geschriebene Datenbankscripte werden nicht einfach so ausgeführt sondern müssen sich an gewisse Konventionen
+       halten. Dafür bekommen sie aber auch einige Komfortfunktionen bereitgestellt.</p><p>Ein Upgradescript stellt dabei eine vollständige Objektklasse dar, die vom Elternobjekt
+       "<code class="literal">SL::DBUpgrade2::Base</code>" erben und eine Funktion namens "<code class="literal">run</code>" zur Verfügung stellen muss. Das
+       Script wird ausgeführt, indem eine Instanz dieser Klasse erzeugt und darauf die erwähnte "<code class="literal">run</code>" aufgerufen
+       wird.</p><p>Zu beachten ist, dass sich der Paketname der Datei aus dem Wert für "<code class="literal">@tag</code>" ableitet. Dabei werden alle
+       Zeichen, die in Paketnamen ungültig wären (gerade Bindestriche), durch Unterstriche ersetzt. Insgesamt sieht der Paketname wie folgt
+       aus: "<code class="literal">SL::DBUpgrade2::tag</code>".</p><p>Welche Komfortfunktionen zur Verfügung stehen, erfahren Sie in der Perl-Dokumentation zum oben genannten Modul; aufzurufen mit
+       "<span class="command"><strong>perldoc SL/DBUpgrade2/Base.pm</strong></span>".</p><p>Ein Mindestgerüst eines gültigen Perl-Upgradescriptes sieht wie folgt aus:</p><pre class="programlisting"># @tag: beispiel-upgrade-file42
+# @description: Ein schönes Beispielscript
+# @depends: release_3_0_0
+package SL::DBUpgrade2::beispiel_upgrade_file42;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  # hier Aktionen ausführen
+
+  return 1;
+}
+
+1;
+</pre></div><div class="sect2" title="4.3.4. Hilfsscript dbupgrade2_tool.pl"><div class="titlepage"><div><div><h3 class="title"><a name="db-upgrade-files.dbupgrade-tool"></a>4.3.4. Hilfsscript dbupgrade2_tool.pl</h3></div></div></div><p>Um die Arbeit mit den Abhängigkeiten etwas zu erleichtern,
         existiert ein Hilfsscript namens
         "<code class="filename">scripts/dbupgrade2_tool.pl</code>". Es muss aus dem
         kivitendo-ERP-Basisverzeichnis heraus aufgerufen werden. Dieses Tool
index 340202aa844c9b9e74b1600fe24aa4a2335b4508..2eb6bde78c2bbf1dd9ca2714f51d1b594e433df6 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 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#d0e678">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. 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#Datenbanken-anlegen">2.9.2. Datenbanken anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppen-anlegen">2.9.3. Gruppen anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Benutzer-anlegen">2.9.4. Benutzer anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppenmitgliedschaften-verwalten">2.9.5. Gruppenmitgliedschaften verwalten</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Migration-alter-Installationen">2.9.6. Migration alter Installationen</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#d0e5252">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5353">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5386">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5766">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><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.known-issues">4.2.5. Bekannte Probleme</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.dbupgrade-tool">4.3.3. 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.file-structure">4.4.2. 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#d0e5252">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5353">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5386">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e5766">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><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.known-issues">4.2.5. Bekannte Probleme</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.file-structure">4.4.2. 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 ecd5a0da8d9bcd1fd08576e058799a977fdb96e1..52ade6eb70f5209c76ae36a656b7196118ae05ba 100644 (file)
Binary files a/doc/kivitendo-Dokumentation.pdf and b/doc/kivitendo-Dokumentation.pdf differ
index 031b5c8f4c4bec7f411a3bcf4d3fced3cd6d9c5b..abeac1b39090d13ea0b8f03db5065608b6ed66ec 100644 (file)
@@ -16,8 +16,10 @@ function eval_json_result(data) {
   if (data.error)
     return display_flash('error', data.error);
 
-  $('#flash_error').hide();
-  $('#flash_error_content').empty();
+  $(['info', 'warning', 'error']).each(function(idx, category) {
+    $('#flash_' + category).hide();
+    $('#flash_' + category + '_content').empty();
+  });
 
   if ((data.js || '') != '')
     eval(data.js);
@@ -71,6 +73,11 @@ function eval_json_result(data) {
       else if (action[0] == 'removeProp')           $(action[1]).removeProp(action[2]);
       else if (action[0] == 'val')                  $(action[1]).val(action[2]);
 
+      // Class attribute
+      else if (action[0] == 'addClass')             $(action[1]).addClass(action[2]);
+      else if (action[0] == 'removeClass')          $(action[1]).removeClass(action[2]);
+      else if (action[0] == 'toggleClass')          $(action[1]).toggleClass(action[2]);
+
       // Data storage
       else if (action[0] == 'data')                 $(action[1]).data(action[2], action[3]);
       else if (action[0] == 'removeData')           $(action[1]).removeData(action[2]);
@@ -78,6 +85,11 @@ function eval_json_result(data) {
       // Form Events
       else if (action[0] == 'focus')                $(action[1]).focus();
 
+      // ## jqModal plugin ##
+
+      // Closing and removing the popup
+      else if (action[0] == 'jqmClose')             $(action[1]).jqmClose();
+
       // ## jstree plugin ##
 
       // Operations on the whole tree
@@ -104,6 +116,9 @@ function eval_json_result(data) {
       else if (action[0] == 'jstree:deselect_node') $.jstree._reference($(action[1])).deselect_node(action[2]);
       else if (action[0] == 'jstree:deselect_all')  $.jstree._reference($(action[1])).deselect_all();
 
+      // ## other stuff ##
+      else if (action[0] == 'redirect_to')          window.location.href = action[1];
+
       else                                          console.log('Unknown action: ' + action[0]);
 
     });
@@ -111,6 +126,12 @@ function eval_json_result(data) {
   // console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
 }
 
+function submit_ajax_form(url, form_selector, additional_data) {
+  var separator = /\?/.test(url) ? '&' : '?';
+  $.post(url + separator + $(form_selector).serialize(), additional_data, eval_json_result);
+  return true;
+}
+
 // Local Variables:
 // mode: js
 // End:
index e40b9abfec5c4035dd36039ee7d29af6ef8a4236..d2594a2778b551007302afe7f81fdee617cb43b9 100644 (file)
@@ -160,6 +160,30 @@ function focus_by_name(name){
   return false;
 }
 
+function open_jqm_window(params) {
+  params = params || { };
+  var url = params.url;
+  var id  = params.id ? params.id : 'jqm_popup_dialog';
+
+  if (params.data) {
+    var data  = typeof params.data === "string" ? params.data : $.param(params.data);
+    url      += (/\?/.exec(url) ? "&" : "?") + data;
+  }
+
+  $('#' + id).remove();
+  var div     = $('<div id="' + id + '" class="jqmWindow jqModal_overlay ' + (params.class || '') + '"></div>').hide().appendTo('body');
+  var close   = $('<div class="close"></div>').appendTo(div);
+  var content = $('<div class="overlay_content"></div>').appendTo(div);
+  div.jqm({ modal: true });
+  div.jqmShow();
+  $.ajax({ url: url, success: function(new_html) { $(content).html(new_html); } });
+  $(close).click(function() {
+    div.jqmClose();
+  });
+
+  return true;
+}
+
 $(document).ready(function () {
   // initialize all jQuery UI tab elements:
   $(".tabwidget").each(function(idx, element) { $(element).tabs(); });
index db318ffb87b2c0409b19c862cf91577e33d49c9c..db1dd9e7c2fe360475f7f0e25f3b1ca1675be4c5 100644 (file)
@@ -34,6 +34,7 @@ $.fn.jqmAddClose=function(e){return hs(this,e,'jqmHide');};
 $.fn.jqmAddTrigger=function(e){return hs(this,e,'jqmShow');};
 $.fn.jqmShow=function(t){return this.each(function(){t=t||window.event;$.jqm.open(this._jqm,t);});};
 $.fn.jqmHide=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t)});};
+$.fn.jqmClose=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t);this.remove();});};
 
 $.jqm = {
 hash:{},
diff --git a/js/jquery.download.js b/js/jquery.download.js
new file mode 100644 (file)
index 0000000..54ffb6a
--- /dev/null
@@ -0,0 +1,19 @@
+jQuery.download = function(url, data, method) {
+  //url and data options required
+  if (!url || !data)
+    return;
+
+  //data can be string of parameters or array/object
+  data = typeof data == 'string' ? data : jQuery.param(data);
+  //split params into form inputs
+  var form = jQuery('<form action="'+ url +'" method="'+ (method||'post') +'"></form>');
+  jQuery.each(data.split('&'), function(){
+    var pair  = this.split('=');
+    var input = jQuery('<input type="hidden"/>');
+    input.attr('name', decodeURIComponent(pair[0]));
+    input.val(decodeURIComponent(pair[1]));
+    input.appendTo(form);
+  });
+  //send request
+  form.appendTo('body').submit().remove();
+};
old mode 100644 (file)
new mode 100755 (executable)
index bb7a9f3..2e84032
@@ -98,6 +98,7 @@ $self->{texts} = {
   'Account Nummer'              => 'Kontonummer',
   'Account Type'                => 'Kontoart',
   'Account Type missing!'       => 'Kontoart fehlt!',
+  'Account categories'          => 'Kontoarten',
   'Account deleted!'            => 'Konto gelöscht!',
   'Account for fees'            => 'Konto f&uuml;r Geb&uuml;hren',
   'Account for interest'        => 'Konto f&uuml;r Zinsen',
@@ -283,6 +284,8 @@ $self->{texts} = {
   'Basic Data'                  => 'Basisdaten',
   'Batch Printing'              => 'Druck',
   'Bcc'                         => 'Bcc',
+  'Bcc E-mail'                  => 'BCC (E-Mail)',
+  'Because the useability gets worth if one partnumber is used for several parts (for example if you are searching a position for an invoice), partnumbers should be unique.' => 'Da die Benutzerfreundlichkeit durch doppelte Artikelnummern erheblich verschlechtert wird (zum Beispiel, wenn man einen Artikel für eine Rechnung sucht), sollten Artikelnummern eindeutig vergeben sein.',
   'Belegnummer'                 => 'Buchungsnummer',
   'Beratername'                 => 'Beratername',
   'Beraternummer'               => 'Beraternummer',
@@ -393,6 +396,7 @@ $self->{texts} = {
   'Carry over shipping address' => 'Lieferadresse &uuml;bernehmen',
   'Cash'                        => 'Zahlungsverkehr',
   'Cc'                          => 'Cc',
+  'Cc E-mail'                   => 'CC (E-Mail)',
   'Change kivitendo installation settings (all menu entries beneath \'System\')' => 'Ver&auml;ndern der kivitendo-Installationseinstellungen (Men&uuml;punkte unterhalb von \'System\')',
   'Change representative to'    => 'Vertreter Ã¤ndern in',
   '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).' => 'Es ist nur sinnvoll Ã„nderungen vorzunehmen, wenn das Konto KEIN Sammelkonto ist und wenn ein gültiger Steuerschlüssel für das Konto existiert. Gleichzeitig Haken bei Forderungen und Verbindlichkeiten zu setzen, macht auch NUR für den Zahlungsein- und Ausgang (bspw. Bank oder Kasse) Sinn.',
@@ -685,6 +689,7 @@ $self->{texts} = {
   'Documentation (in German)'   => 'Dokumentation',
   'Documents in the WebDAV repository' => 'Dokumente im WebDAV-Repository',
   'Done'                        => 'Fertig',
+  'Double partnumbers'          => 'Doppelte Artikelnummern',
   'Download SEPA XML export file' => 'SEPA-XML-Exportdatei herunterladen',
   'Download sample file'        => 'Beispieldatei herunterladen',
   'Download the backup'         => 'Die Sicherungsdatei herunterladen',
@@ -857,6 +862,8 @@ $self->{texts} = {
   'Execution type'              => 'Ausführungsart',
   'Existing Buchungsgruppen'    => 'Existierende Buchungsgruppen',
   'Existing Datasets'           => 'Existierende Datenbanken',
+  'Existing contacts (with column \'cp_id\')' => 'Existierende Ansprechpersonen (mit Spalte \'cp_id\')',
+  'Existing customers/vendors with same customer/vendor number' => 'Existierende Kunden/Lieferanten mit derselben Kunden-/Lieferantennummer',
   'Existing file on server'     => 'Auf dem Server existierende Datei',
   'Existing pending follow-ups for this item' => 'Noch nicht erledigte Wiedervorlagen f&uuml;r dieses Dokument',
   'Existing profiles'           => 'Existierende Profile',
@@ -868,6 +875,7 @@ $self->{texts} = {
   'Expenses EU with UStId'      => 'Aufwand EU m. UStId',
   'Expenses EU without UStId'   => 'Aufwand EU o. UStId',
   'Export Buchungsdaten'        => 'Export Buchungsdaten',
+  'Export Number'               => 'Exportnummer',
   'Export Stammdaten'           => 'Export Stammdaten',
   'Export as CSV'               => 'Als CSV exportieren',
   'Export as PDF'               => 'Als PDF exportieren',
@@ -927,6 +935,8 @@ $self->{texts} = {
   'From'                        => 'Von',
   'From Date'                   => 'Von',
   'From this version on it is necessary to name a default value.' => 'Ab dieser Version benötigt kivitendo eine Standardwährung.',
+  'From this version on a new feature is available.' => 'Ab dieser Version ist ein neues Feature verfügbar.',
+  'From this version on the partnumber of services, articles and assemblies have to be unique.' => 'Ab dieser Version müssen Artikelnummern eindeutig vergeben werden.',
   'From this version on the taxkey 0 must have a tax rate of 0 (for DATEV compatibility).' => 'Ab dieser Version muss der Steuerschlüssel 0 einen Steuersatz von 0% haben (auf Grund der DATEV-Kompatibilität).',
   'Full Access'                 => 'Vollzugriff',
   'Full Preview'                => 'Alles',
@@ -990,11 +1000,13 @@ $self->{texts} = {
   '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:' => 'Falls der oben genannte Datenbankbenutzer nicht die Berechtigung zum Anlegen neuer Datenbanken hat, so k&ouml;nnen Sie hier den Namen und das Passwort des Datenbankadministratoraccounts angeben:',
   'If you chose to let kivitendo do the migration then kivitendo will also remove the old member file after creating a backup copy of it in the directory &quot;#1&quot;.' => 'Falls Sie sich entscheiden, kivitendo die Migration durchführen zu lassen, so wird kivitendo ein Backup der alten Dateien im Verzeichnis "#1" erstellen und die Dateien anschließend löschen.',
   '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.' => 'Wenn Sie f&uuml;r die Artikelnummer und / oder die Beschreibung etwas eingeben, so werden nur die Lagerpl&auml;tze angezeigt, in denen Waren eingelagert sind, die Ihre Suchbegriffe enthalten.',
+  '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.' => 'Wenn Sie z.B. die Kategory Erlös für eine Steuer nicht gewählt haben und ein Erlöskonto beim Erstellen einer Dialogbuchung wählen, wird diese Steuer auch nicht im Dropdown-Menü für die Steuern angezeigt.',
   '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.' => 'Wenn Sie diese Meldung sehen haben Sie wahrscheinlich ein frisches LX-Office Setup und noch keine Buchungsgruppen eingerichtet. Ein Administrator kann dies im Systemmen&uuml; erledigen.',
   'If you select a base unit then you also have to enter a factor.' => 'Wenn Sie eine Basiseinheit auswählen, dann müssen Sie auch einen Faktor eingeben.',
   'If you want to change any of these parameters then press the &quot;Back&quot; button, edit the file &quot;config/kivitendo.conf&quot; and login into the admin module again.' => 'Wenn Sie einen der Parameter &auml;ndern wollen, so dr&uuml;cken Sie auf den &quot;Zur&uuml;ck&quot;-Button, bearbeiten Sie die Datei &quot;config/kivitendo.conf&quot;, und melden Sie sich erneut im Administrationsbereich an.',
   '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.' => 'Wenn Sie eine solche Datenbank l&ouml;schen wollen, so m&uuml;ssen Sie zuerst die Benutzer bearbeiten, die die fragliche Datenbank benutzen, und sie so &auml;ndern, dass sie eine andere Datenbank benutzen.',
   '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.' => 'Wenn Sie die Authentifizierungs-Datenbank selber einrichten wollen, so melden Sie sich im Administrationsbereich an. kivitendo wird dann die Datenbank und die erforderlichen Tabellen für Sie anlegen.',
+  'Illegal characters have been removed from the following fields: #1' => 'Ungültige Zeichen wurden aus den folgenden Feldern entfernt: #1',
   'Image'                       => 'Grafik',
   'Import'                      => 'Import',
   'Import CSV'                  => 'CSV-Import',
@@ -1029,6 +1041,8 @@ $self->{texts} = {
   'Increase'                    => 'Erhöhen',
   'Individual Items'            => 'Einzelteile',
   'Information'                 => 'Information',
+  'Insert with new customer/vendor number' => 'Mit neuer Kunden-/Lieferantennummer anlegen',
+  'Insert with new database ID' => 'Neu anlegen mit neuer Datenbank-ID',
   'Insert with new part number' => 'Mit neuer Artikelnummer einfügen',
   'Interest'                    => 'Zinsen',
   'Interest Rate'               => 'Zinssatz',
@@ -1111,6 +1125,7 @@ $self->{texts} = {
   'Languages and translations'  => 'Sprachen und Ãœbersetzungen',
   'Last Action'                 => 'Letzte Aktivität',
   'Last Article Number'         => 'Letzte Artikelnummer',
+  'Last Assembly Number'        => 'Letzte Erzeugnisnummer',
   'Last Cost'                   => 'Einkaufspreis',
   'Last Credit Note Number'     => 'Letzte Gutschriftnummer',
   'Last Customer Number'        => 'Letzte Kundennummer',
@@ -1207,7 +1222,7 @@ $self->{texts} = {
   'Missing Method!'             => 'Fehlender Voranmeldungszeitraum',
   'Missing Tax Authoritys Preferences' => 'Fehlende Angaben zum Finanzamt!',
   'Missing amount'              => 'Fehlbetrag',
-  'Missing parameter #1 in call to sub #2.' => 'Fehlernder Parameter \'#1\' in Funktionsaufruf \'#2\'.',
+  'Missing parameter #1 in call to sub #2.' => 'Fehlender Parameter \'#1\' in Funktionsaufruf \'#2\'.',
   'Missing parameter (at least one of #1) in call to sub #2.' => 'Fehlernder Parameter (mindestens einer aus \'#1\') in Funktionsaufruf \'#2\'.',
   'Missing taxkeys in invoices with taxes.' => 'Fehlende Steuerschl&uuml;ssel in Rechnungen mit Steuern',
   'Missing user id!'            => 'Benutzer ID fehlt!',
@@ -1243,6 +1258,7 @@ $self->{texts} = {
   'New bank account'            => 'Neues Bankkonto',
   'New contact'                 => 'Neue Ansprechperson',
   'New customer'                => 'Neuer Kunde',
+  'New filter for tax accounts' => 'Neue Filter für Steuerkonten',
   'New invoice'                 => 'Neue Rechnung',
   'New part'                    => 'Neue Ware',
   'New sales order'             => 'Neuer Auftrag',
@@ -1389,6 +1405,7 @@ $self->{texts} = {
   'Part Notes'                  => 'Bemerkungen',
   'Part Number'                 => 'Artikelnummer',
   'Part Number missing!'        => 'Artikelnummer fehlt!',
+  'Partnumber'                  => 'Artikelnummer',
   'Partnumber must not be set to empty!' => 'Die Artikelnummer darf nicht auf leer ge&auml;ndert werden.',
   'Partnumber not unique!'      => 'Artikelnummer bereits vorhanden!',
   'Parts'                       => 'Waren',
@@ -1436,6 +1453,8 @@ $self->{texts} = {
   'Please Check the bank information for each customer:' => 'Bitte Ã¼berprüfen Sie die Bankinformationen der Kunden:',
   'Please Check the bank information for each vendor:' => 'Bitte Ã¼berprüfen Sie die Kontoinformationen der Lieferanten:',
   'Please ask your administrator to create warehouses and bins.' => 'Bitten Sie Ihren Administrator, dass er Lager und Lagerpl&auml;tze anlegt.',
+  'Please change the partnumber of the following parts and run the update again:' => 'Bitte Ã¤ndern Sie daher die Artikelnumer folgender Artikel:',
+  'Please choose for which categories the taxes should be displayed:' => 'Bitte wählen Sie für welche Kontoarten die Steuer angezeigt werden soll:',
   'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.',
   'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
   'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:',
@@ -1736,6 +1755,7 @@ $self->{texts} = {
   'Service Items'               => 'Dienstleistungen',
   'Service Number missing!'     => 'Dienstleistungsnummer fehlt!',
   'Service unit'                => 'Dienstleistungseinheit',
+  'Service, assembly or part'   => 'Dienstleistung, Erzeugnis oder Ware',
   'Services'                    => 'Dienstleistungen',
   'Set Language Values'         => 'Spracheinstellungen',
   'Set eMail text'              => 'eMail Text eingeben',
@@ -1791,9 +1811,10 @@ $self->{texts} = {
   'Single values in item mode, cumulated values in invoice mode' => 'Einzelwerte im Artikelmodus, kumulierte Werte im Rechnungsmodus',
   'Skip'                        => 'Überspringen',
   'Skip entry'                  => 'Eintrag Ã¼berspringen',
-  'Skipping due to existing entry in database' => 'Übersprungen, wegen existierender Artikelnummer',
+  'Skipping due to existing entry in database' => 'Wegen existierendem Eintrag mit selber Nummer Ã¼bersprungen',
   'Skonto'                      => 'Skonto',
   'Skonto Terms'                => 'Zahlungsziel Skonto',
+  'So far you could use one partnumber for severel parts, for example a service and an article.' => 'Bisher war es möglich eine Artikelnummer für mehrere Artikel zu verwenden, zum Beispiel eine Artikelnummer für eine Dienstleistung, eine Ware und ein Erzeugnis.',
   'Sold'                        => 'Verkauft',
   'Solution'                    => 'Lösung',
   'Sort By'                     => 'Sortiert nach',
@@ -1876,6 +1897,7 @@ $self->{texts} = {
   'Tax deleted!'                => 'Steuer gelöscht!',
   'Tax number'                  => 'Steuernummer',
   'Tax paid'                    => 'Vorsteuer',
+  'Tax rate'                    => 'Steuersatz',
   'Tax saved!'                  => 'Steuer gespeichert!',
   'Tax-O-Matic'                 => 'Steuer',
   'Tax-o-matic Account'         => 'Automatikbuchung auf Konto',
@@ -2086,6 +2108,7 @@ $self->{texts} = {
   'There are #1 more open invoices from this vendor with other currencies.' => 'Es gibt #1 weitere offene Rechnungen von diesem Lieferanten, die in anderen Währungen ausgestellt wurden.',
   'There are #1 unfinished follow-ups of which #2 are due.' => 'Es gibt #1 Wiedervorlage(n), von denen #2 fällig ist/sind.',
   '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.' => 'Das Konto 3803 wurde nach dem 01.01.2007 bebucht. Falls Sie dieses Konto nicht manuell auf 19% gestellt haben sind die Buchungen wahrscheinlich mit falscher Umsatzsteuer gebucht worden.',
+  'There are double partnumbers in your database.' => 'In ihrer Datenbank befinden sich mehrfach vergebene Artikelnummern.',
   'There are entries in tax where taxkey is NULL.' => 'In der Datenbank sind Steuern ohne Steuerschlüssel vorhanden (in der Tabelle tax Spalte taxkey).',
   'There are four tax zones.'   => 'Es gibt vier Steuerzonen.',
   'There are invalid taxnumbers in use.' => 'Es werden ungültige Steuerautomatik-Konten benutzt.',
@@ -2116,6 +2139,7 @@ $self->{texts} = {
   'This corresponds to kivitendo\'s behavior prior to version 2.4.4.' => 'Dies entspricht kivitendos Verhalten vor Version 2.4.4.',
   'This could have happened for two reasons:' => 'Dies kann aus zwei Gründen geschehen sein:',
   'This customer number is already in use.' => 'Diese Kundennummer wird bereits verwendet.',
+  'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.',
   'This group will be called &quot;Full Access&quot;.' => 'Diese Gruppe wird &quot;Vollzugriff&quot; genannt.',
   'This installation uses an unknown chart of accounts (&quot;#1&quot;). This database upgrade cannot create standard buchungsgruppen automatically.' => 'Diese Installation benutzt einen unbekannten Kontenrahmen (&quot;#1&quot;). Dieses Datenbankupgrade kann die Standardbuchungsgruppen nicht automatisch anlegen.',
   'This is a preliminary check for existing sources. Nothing will be created or deleted at this stage!' => 'In diesem Schritt werden bestehende Datenbanken gesucht. Es werden noch keine &Auml;nderungen vorgenommen!',
@@ -2222,8 +2246,10 @@ $self->{texts} = {
   'Update complete'             => 'Update beendet.',
   'Update prices'               => 'Preise aktualisieren',
   'Update prices of existing entries' => 'Preise von vorhandenen Artikeln aktualisieren',
+  'Update properties of existing entries' => 'Eigenschaften von existierenden Einträgen aktualisieren',
   'Update?'                     => 'Aktualisieren?',
   'Updated'                     => 'Erneuert am',
+  'Updating existing entry in database' => 'Existierenden Eintrag in Datenbank aktualisieren',
   'Updating prices of existing entry in database' => 'Preis des Eintrags in der Datenbank wird aktualisiert',
   'Uploaded on #1, size #2 kB'  => 'Am #1 hochgeladen, Größe #2 kB',
   'Use As New'                  => 'Als neu verwenden',
@@ -2311,6 +2337,7 @@ $self->{texts} = {
   'You are logged out!'         => 'Auf Wiedersehen!',
   'You can also create new units now.' => 'Sie k&ouml;nnen jetzt auch neue Einheiten anlegen.',
   'You can also delete this transaction and re-enter it manually.' => 'Alternativ können Sie die Buchung auch mit löschen lassen und sie anschließend neu eingeben.',
+  'You can choose account categories for taxes. Depending on these categories taxes will be displayed for transfers in the general ledger or not.' => 'Sie können Kontoarten für Steuern auswählen. Abhängig von diesen Kontoarten werden dann Steuern bei Dialogbuchungen angezeigt oder nicht.',
   'You can correct this transaction by chosing the correct taxkeys from the drop down boxes and hitting the button "Fix transaction" afterwards.' => 'Sie haben die Möglichkeit, die Buchung zu korrigieren, indem Sie in den Drop-Down-Boxen die richtigen Steuerschlüssel auswählen und anschließend auf den Button "Buchung korrigieren" drücken.',
   'You can create a missing dataset by going back and chosing &quot;Create Dataset&quot;.' => 'Sie k&ouml;nnen eine fehlende Datenbank erstellen, indem Sie jetzt zu&uuml;ck gehen und den Punkt &quot;Neue Datenbank anlegen&quot; w&auml;hlen.',
   'You can create warehouses and bins via the menu "System -> Warehouses".' => 'Sie k&ouml;nnen Lager und Lagerpl&auml;tze &uuml;ber das Men&uuml; "System -> Lager" anlegen.',
@@ -2318,6 +2345,7 @@ $self->{texts} = {
   'You can either create a new database or chose an existing database.' => 'Sie können entweder eine neue Datenbank erstellen oder eine existierende auswählen.',
   'You can find information on the migration in the upgrade chapter of the documentation.' => 'Informationen Ã¼ber die Migration sind in der Upgrade Kapitel in der Dokumentation zu finden.',
   'You can only delete datasets that are not in use.' => 'Sie k&ouml;nnen nur Datenbanken l&ouml;schen, die momentan nicht in Benutzung sind.',
+  'You can update existing contacts by providing the \'cp_id\' column with their database IDs. Otherwise: ' => 'Sie können existierende Einträge aktualisieren, indem Sie eine Spalte \'cp_id\' mit der Datenbank-ID des Eintrags mitgeben. Andernfalls: ',
   '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.' => 'Sie können die folgenden Begriffe in den Langtexten und allen Ãœbersetzungen benutzen. Sie werden von kivitendo vor der Ausgabe durch ihren tatsächlichen Wert ersetzt.',
   'You cannot adjust the price for pricegroup "#1" by a negative percentage.' => 'Sie können den Preis für Preisgruppe "#1" um einen negativen Prozentwert anpassen.',
   'You cannot continue before all required modules are installed.' => 'Sie k&ouml;nnen nicht fortfahren, bevor alle ben&ouml;tigten Pakete installiert sind.',
@@ -2372,6 +2400,7 @@ $self->{texts} = {
   'ap_aging_list'               => 'liste_offene_verbindlichkeiten',
   'ar_aging_list'               => 'liste_offene_forderungen',
   'as at'                       => 'zum Stand',
+  'assembly'                    => 'Erzeugnis',
   'assembly_list'               => 'erzeugnisliste',
   'averaged values, in invoice mode only useful when filtered by a part' => 'gemittelte Werte, im Rechnungsmodus nur sinnvoll wenn nach Artikel gefiltert wird',
   'back'                        => 'zurück',
@@ -2419,6 +2448,7 @@ $self->{texts} = {
   'ea'                          => 'St.',
   'emailed to'                  => 'gemailt an',
   'empty'                       => 'leer',
+  'every third month'           => 'vierteljährlich',
   'every time'                  => 'immer',
   'executed'                    => 'ausgeführt',
   'failed'                      => 'fehlgeschlagen',
@@ -2487,6 +2517,7 @@ $self->{texts} = {
   'order'                       => 'Reihenfolge',
   'our vendor number at customer' => 'Unsere Lieferanten-Nr. beim Kunden',
   'parsing csv'                 => 'Parse CSV Daten',
+  'part'                        => 'Ware',
   'part_list'                   => 'Warenliste',
   'percental'                   => 'prozentual',
   'periodic'                    => 'Aufwandsmethode',
@@ -2505,7 +2536,6 @@ $self->{texts} = {
   'purchase_order'              => 'Auftrag',
   'purchase_order_list'         => 'lieferantenauftragsliste',
   'quarter'                     => 'Vierteljährliche (quartalsweise) Abgabe',
-  'quarterly'                   => 'quartalsweise',
   'quotation_list'              => 'angebotsliste',
   'release_material'            => 'Materialausgabebe',
   'reorder item'                => 'Eintrag umsortieren',
@@ -2527,6 +2557,7 @@ $self->{texts} = {
   'saving data'                 => 'Speichere Daten',
   'sent'                        => 'gesendet',
   'sent to printer'             => 'an Drucker geschickt',
+  'service'                     => 'Dienstleistung',
   'service_list'                => 'dienstleistungsliste',
   'shipped'                     => 'verschickt',
   'singular first char'         => 'S',
diff --git a/scripts/dbconnect.pl b/scripts/dbconnect.pl
new file mode 100755 (executable)
index 0000000..6eed2a0
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+BEGIN {
+  use SL::System::Process;
+  my $exe_dir = SL::System::Process::exe_dir;
+
+  unshift @INC, "${exe_dir}/modules/override"; # Use our own versions of various modules (e.g. YAML).
+  push    @INC, "${exe_dir}/modules/fallback"; # Only use our own versions of modules if there's no system version.
+  unshift @INC, $exe_dir;
+}
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use DBI;
+use List::MoreUtils qw(any);
+use SL::LxOfficeConf;
+
+our %lx_office_conf;
+SL::LxOfficeConf->read;
+
+sub psql {
+  my ($title, %params) = @_;
+  print "Connecting to ${title} database '" . $params{db} . "' on " . $params{host} . ':' . $params{port} . " with PostgreSQL username " . $params{user} . "\n\n";
+  print "If asked for the password use this: " . $params{password} . "\n\n";
+  exec "psql", "-U", $params{user}, "-h", $params{host}, "-p", $params{port}, $params{db};
+}
+
+my $settings = $lx_office_conf{'authentication/database'};
+die "Missing configuration section 'authentication/database'" unless $settings;
+die "Incomplete database settings" if any { !$settings->{$_} } qw (host db user);
+$settings->{port} ||= 5432;
+
+psql("authentication", %{ $settings }) if !@ARGV;
+
+my $dbh = DBI->connect('dbi:Pg:dbname=' . $settings->{db} . ';host=' . $settings->{host} . ($settings->{port} ? ';port=' . $settings->{port} : ''), $settings->{user}, $settings->{password})
+  or die "Database connection to authentication database failed: " . $DBI::errstr;
+
+my $user_id = $dbh->selectrow_array(qq|SELECT id FROM auth.user WHERE login = ?|, undef, $ARGV[0])
+  or do {
+    $dbh->disconnect;
+    die "No such user in authentication database: " . $ARGV[0];
+  };
+
+my $href = $dbh->selectall_hashref(qq|SELECT cfg_key, cfg_value FROM auth.user_config WHERE user_id = ?|, 'cfg_key', undef, $user_id);
+$dbh->disconnect;
+
+my %params = (
+  host     => $href->{dbhost}->{cfg_value},
+  db       => $href->{dbname}->{cfg_value},
+  port     => $href->{dbport}->{cfg_value} || 5432,
+  user     => $href->{dbuser}->{cfg_value},
+  password => $href->{dbpasswd}->{cfg_value},
+);
+
+die "Incomplete database settings for user " . $ARGV[0] if any { !$settings->{$_} } qw (host db user);
+
+psql($ARGV[0] . "'s", %params);
index 7fa7baf95ec113f9a9d804525b1ac85bf936f35a..46032667f17927cf0ae16f09a3e24ae0ab8c8bd1 100644 (file)
@@ -16,8 +16,10 @@ function eval_json_result(data) {
   if (data.error)
     return display_flash('error', data.error);
 
-  $('#flash_error').hide();
-  $('#flash_error_content').empty();
+  $(['info', 'warning', 'error']).each(function(idx, category) {
+    $('#flash_' + category).hide();
+    $('#flash_' + category + '_content').empty();
+  });
 
   if ((data.js || '') != '')
     eval(data.js);
@@ -32,6 +34,12 @@ function eval_json_result(data) {
   // console.log("current_content_type " + $('#current_content_type').val() + ' ID ' + $('#current_content_id').val());
 }
 
+function submit_ajax_form(url, form_selector, additional_data) {
+  var separator = /\?/.test(url) ? '&' : '?';
+  $.post(url + separator + $(form_selector).serialize(), additional_data, eval_json_result);
+  return true;
+}
+
 // Local Variables:
 // mode: js
 // End:
index b6665588abd2ce102ea897c490baa663758f1ba0..8105abd9a76b67fb46dd0c081b0ee72cf695e0fc 100755 (executable)
@@ -519,88 +519,91 @@ sub scanhtmlfile {
 
   my $file = shift;
 
-  if (!defined $cached{$file}) {
-    my %plugins = ( 'loaded' => { }, 'needed' => { } );
-
-    open(IN, $file) || die $file;
+  return if defined $cached{$file};
 
-    my $copying  = 0;
-    my $issubmit = 0;
-    my $text     = "";
-    while (my $line = <IN>) {
-      chomp($line);
+  my %plugins = ( 'loaded' => { }, 'needed' => { } );
 
-      while ($line =~ m/\[\%[^\w]*use[^\w]+(\w+)[^\w]*?\%\]/gi) {
-        $plugins{loaded}->{$1} = 1;
-      }
+  if (!open(IN, $file)) {
+    print "E: template file '$file' not found\n";
+    return;
+  }
 
-      while ($line =~ m/\[\%[^\w]*(\w+)\.\w+\(/g) {
-        my $plugin = $1;
-        $plugins{needed}->{$plugin} = 1 if (first { $_ eq $plugin } qw(HTML LxERP JavaScript JSON L P));
-      }
+  my $copying  = 0;
+  my $issubmit = 0;
+  my $text     = "";
+  while (my $line = <IN>) {
+    chomp($line);
 
-      $plugins{needed}->{T8} = 1 if $line =~ m/\[\%.*\|.*\$T8/;
-
-      while ($line =~ m/(?:             # Start von Variante 1: LxERP.t8('...'); ohne darumliegende [% ... %]-Tags
-                          (LxERP\.t8)\( #   LxERP.t8(                             ::Parameter $1::
-                          ([\'\"])      #   Anfang des zu Ã¼bersetzenden Strings   ::Parameter $2::
-                          (.*?)         #   Der zu Ã¼bersetzende String            ::Parameter $3::
-                          (?<!\\)\2     #   Ende des zu Ã¼bersetzenden Strings
-                        |               # Start von Variante 2: [% '...' | $T8 %]
-                          \[\%          #   Template-Start-Tag
-                          [\-~#]?       #   Whitespace-Unterdrückung
-                          \s*           #   Optional beliebig viele Whitespace
-                          ([\'\"])      #   Anfang des zu Ã¼bersetzenden Strings   ::Parameter $4::
-                          (.*?)         #   Der zu Ã¼bersetzende String            ::Parameter $5::
-                          (?<!\\)\4     #   Ende des zu Ã¼bersetzenden Strings
-                          \s*\|\s*      #   Pipe-Zeichen mit optionalen Whitespace davor und danach
-                          (\$T8)        #   Filteraufruf                          ::Parameter $6::
-                          .*?           #   Optionale Argumente für den Filter
-                          \s*           #   Whitespaces
-                          [\-~#]?       #   Whitespace-Unterdrückung
-                          \%\]          #   Template-Ende-Tag
-                        )
-                       /ix) {
-        my $module = $1 || $6;
-        my $string = $3 || $5;
-        print "Found filter >>>$string<<<\n" if $debug;
-        substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';
-
-        $string                         = unescape_template_string($string);
-        $cached{$file}{all}{$string}    = 1;
-        $cached{$file}{html}{$string}   = 1;
-        $cached{$file}{submit}{$string} = 1 if $PREMATCH =~ /$submitsearch/;
-        $plugins{needed}->{T8}          = 1 if $module eq '$T8';
-        $plugins{needed}->{LxERP}       = 1 if $module eq 'LxERP.t8';
-      }
+    while ($line =~ m/\[\%[^\w]*use[^\w]+(\w+)[^\w]*?\%\]/gi) {
+      $plugins{loaded}->{$1} = 1;
+    }
 
-      while ($line =~ m/\[\%          # Template-Start-Tag
-                        [\-~#]?       # Whitespace-Unterdrückung
-                        \s*           # Optional beliebig viele Whitespace
-                        (?:           # Die erkannten Template-Direktiven
-                          PROCESS
-                        |
-                          INCLUDE
-                        )
-                        \s+           # Mindestens ein Whitespace
-                        [\'\"]?       # Anfang des Dateinamens
-                        ([^\s]+)      # Beliebig viele Nicht-Whitespaces -- Dateiname
-                        \.html        # Endung ".html", ansonsten kann es der Name eines Blocks sein
-                       /ix) {
-        my $new_file_name = "$basedir/templates/webpages/$1.html";
-        $cached{$file}{scanh}{$new_file_name} = 1;
-        substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';
-      }
+    while ($line =~ m/\[\%[^\w]*(\w+)\.\w+\(/g) {
+      my $plugin = $1;
+      $plugins{needed}->{$plugin} = 1 if (first { $_ eq $plugin } qw(HTML LxERP JavaScript JSON L P));
     }
 
-    close(IN);
+    $plugins{needed}->{T8} = 1 if $line =~ m/\[\%.*\|.*\$T8/;
+
+    while ($line =~ m/(?:             # Start von Variante 1: LxERP.t8('...'); ohne darumliegende [% ... %]-Tags
+                        (LxERP\.t8)\( #   LxERP.t8(                             ::Parameter $1::
+                        ([\'\"])      #   Anfang des zu Ã¼bersetzenden Strings   ::Parameter $2::
+                        (.*?)         #   Der zu Ã¼bersetzende String            ::Parameter $3::
+                        (?<!\\)\2     #   Ende des zu Ã¼bersetzenden Strings
+                      |               # Start von Variante 2: [% '...' | $T8 %]
+                        \[\%          #   Template-Start-Tag
+                        [\-~#]?       #   Whitespace-Unterdrückung
+                        \s*           #   Optional beliebig viele Whitespace
+                        ([\'\"])      #   Anfang des zu Ã¼bersetzenden Strings   ::Parameter $4::
+                        (.*?)         #   Der zu Ã¼bersetzende String            ::Parameter $5::
+                        (?<!\\)\4     #   Ende des zu Ã¼bersetzenden Strings
+                        \s*\|\s*      #   Pipe-Zeichen mit optionalen Whitespace davor und danach
+                        (\$T8)        #   Filteraufruf                          ::Parameter $6::
+                        .*?           #   Optionale Argumente für den Filter
+                        \s*           #   Whitespaces
+                        [\-~#]?       #   Whitespace-Unterdrückung
+                        \%\]          #   Template-Ende-Tag
+                      )
+                     /ix) {
+      my $module = $1 || $6;
+      my $string = $3 || $5;
+      print "Found filter >>>$string<<<\n" if $debug;
+      substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';
+
+      $string                         = unescape_template_string($string);
+      $cached{$file}{all}{$string}    = 1;
+      $cached{$file}{html}{$string}   = 1;
+      $cached{$file}{submit}{$string} = 1 if $PREMATCH =~ /$submitsearch/;
+      $plugins{needed}->{T8}          = 1 if $module eq '$T8';
+      $plugins{needed}->{LxERP}       = 1 if $module eq 'LxERP.t8';
+    }
 
-    foreach my $plugin (keys %{ $plugins{needed} }) {
-      next if ($plugins{loaded}->{$plugin});
-      print "E: " . strip_base($file) . " requires the Template plugin '$plugin', but is not loaded with '[\% USE $plugin \%]'.\n";
+    while ($line =~ m/\[\%          # Template-Start-Tag
+                      [\-~#]?       # Whitespace-Unterdrückung
+                      \s*           # Optional beliebig viele Whitespace
+                      (?:           # Die erkannten Template-Direktiven
+                        PROCESS
+                      |
+                        INCLUDE
+                      )
+                      \s+           # Mindestens ein Whitespace
+                      [\'\"]?       # Anfang des Dateinamens
+                      ([^\s]+)      # Beliebig viele Nicht-Whitespaces -- Dateiname
+                      \.html        # Endung ".html", ansonsten kann es der Name eines Blocks sein
+                     /ix) {
+      my $new_file_name = "$basedir/templates/webpages/$1.html";
+      $cached{$file}{scanh}{$new_file_name} = 1;
+      substr $line, $LAST_MATCH_START[1], $LAST_MATCH_END[0] - $LAST_MATCH_START[0], '';
     }
   }
 
+  close(IN);
+
+  foreach my $plugin (keys %{ $plugins{needed} }) {
+    next if ($plugins{loaded}->{$plugin});
+    print "E: " . strip_base($file) . " requires the Template plugin '$plugin', but is not loaded with '[\% USE $plugin \%]'.\n";
+  }
+
   # copy back into global arrays
   $alllocales{$_} = 1            for keys %{$cached{$file}{all}};
   $locale{$_}     = 1            for keys %{$cached{$file}{html}};
index 78726cc679aef21874ae923df04840df9d7fb7a6..3cb411b3b681efca92edb85360716c97445367c1 100644 (file)
@@ -1,30 +1,23 @@
-#!/usr/bin/perl
 # @tag: auth_schema_normalization_1
 # @description: Auth-Datenbankschema Normalisierungen Teil 1
 # @depends:
+package SL::DBUpgrade2::auth_schema_normalization_1;
 
 use strict;
+use utf8;
 
-sub do_one {
-  my ($dbh, $query) = @_;
+use parent qw(SL::DBUpgrade2::Base);
 
-  if ($dbh->do($query)) {
-    $dbh->commit();
-  } else {
-    $dbh->rollback();
-  }
-}
-
-sub do_all {
-  my $dbh = $::auth->dbconnect();
+sub run {
+  my ($self) = @_;
 
   my @queries = ( qq|ALTER TABLE auth.group_rights ADD PRIMARY KEY (group_id, "right");|,
                   qq|ALTER TABLE auth.user_config  ADD PRIMARY KEY (user_id,  cfg_key);|,
                   qq|ALTER TABLE auth.user_group   ADD PRIMARY KEY (user_id,  group_id);|);
 
-  do_one($dbh, $_) for @queries;
-}
+  $self->db_query($_, 1) for @queries;
 
-do_all();
+  return 1;
+}
 
 1;
index 23630765f12d38def833ba326b2b86b0123ac66f..e11d7928ed27377bf62330becf8c52d76219733b 100644 (file)
@@ -1,33 +1,18 @@
 # @tag: SKR04-3804-addition
 # @description: Konto 3804 zu SKR04 hinzufügen: Umsatzsteuer 19% für Steuerschlüssel 13 (Umsatzsteuer aus EG-Erwerb)
 # @depends:
-# @charset: UTF-8
+package SL::DBUpgrade2::SKR04_3804_addition;
 
 use utf8;
 use strict;
 
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-
-sub do_update {
+sub run {
+  my ($self) = @_;
 
   # 1. Ãœberprüfen ob Kontenrahmen SKR04 ist, wenn nicht alles Ã¼berspringen
-  my ($kontenrahmen) = $dbh->selectrow_array("select coa from defaults");
+  my ($kontenrahmen) = $self->dbh->selectrow_array("select coa from defaults");
 
   unless ( $kontenrahmen eq 'Germany-DATEV-SKR04EU' ) {
     print "Kontenrahmen ist nicht SKR04, &uuml;berspringen<br>";
@@ -39,41 +24,43 @@ sub do_update {
   # mit der falschen MwSt (16%) gebucht worden, wenn dies nicht manuell
   # geändert worden ist
 
-  my ($anzahl_buchungen) = $dbh->selectrow_array("select count (*) from acc_trans where taxkey=13 and transdate >= '2007-01-01';");
+  my ($anzahl_buchungen) = $self->dbh->selectrow_array("select count (*) from acc_trans where taxkey=13 and transdate >= '2007-01-01';");
   if ( $anzahl_buchungen > 0 ) {
-    if ($main::form->{bookings_exist} ) {
+    if ($::form->{bookings_exist} ) {
       # Benutzer hat Meldung bestätigt
       print "Buchungen nach dem 01.01.2007 existierten, Upgrade &uuml;berspringen";
-      return 1;  
-    } else {
-      # Meldung anzeigen und auf Rückgabe warten
-      print_past_booking_warning();
-      return 2;
-    };
-  } else {  # es gibt keine Buchungen mit taxkey 13 nach 01.01.2007
-  
-    # prüfen ob Konto 3804 schon existiert
-    my ($konto_existiert) = $dbh->selectrow_array("select count (*) from chart where accno = '3804'");
-    if ( $konto_existiert ) {
-      # 3804 existiert, wir gehen davon aus, daß der Benutzer das Konto schon selber angelegt hat und
-      # ordnungsgemäß benutzt
-
-      if ($main::form->{account_exists} ) {
+      return 1;
+    }
+
+    # Meldung anzeigen und auf Rückgabe warten
+    print_past_booking_warning();
+    return 2;
+  }
+
+  # es gibt keine Buchungen mit taxkey 13 nach 01.01.2007
+
+  # prüfen ob Konto 3804 schon existiert
+  my ($konto_existiert) = $self->dbh->selectrow_array("select count (*) from chart where accno = '3804'");
+  if ( $konto_existiert ) {
+    # 3804 existiert, wir gehen davon aus, daß der Benutzer das Konto schon selber angelegt hat und
+    # ordnungsgemäß benutzt
+
+    if ($::form->{account_exists} ) {
       # Benutzer hat Meldung bestätigt
-        print "Konto existiert, Upgrade &uuml;berspringen\n";
-        return 1;  
-      } else {
-        # Meldung anzeigen und auf Rückgabe warten
-        print_3804_already_exists();
-        return 2;
-      };
-    } else {
+      print "Konto existiert, Upgrade &uuml;berspringen\n";
+      return 1;
+    }
+
+    # Meldung anzeigen und auf Rückgabe warten
+    print_3804_already_exists();
+    return 2;
+  }
 
     # noch keine Buchungen mit taxkey 13 und Konto 3804 existiert noch nicht,
     # also legen wir es an und machen noch die nötigen Einstellungen in tax und
     # taxkeys
 
-        my $insert_chart = <<SQL;
+  my $insert_chart = <<SQL;
 INSERT INTO chart (
   accno, description,
   charttype,   category,  link,
@@ -89,49 +76,48 @@ WHERE EXISTS ( -- update only for SKR04, aber eigentlich schon Ã¼berprüft
 );
 SQL
 
-        do_query($insert_chart);
-
-        my $konto_anlegen = $dbh->prepare($insert_chart) || mydberror($insert_chart);
-
+  $self->db_query($insert_chart);
 
-        # 13-1 (16%) korrigieren:
-        my $edit_taxkey_13 = qq|UPDATE tax SET taxdescription = 'Steuerpflichtige EG-Lieferung zum vollen Steuersatz', rate = '0.16', chart_id = (select id FROM chart where accno = '3803'), taxnumber = 3803 WHERE taxkey = '13'|;
-        do_query($edit_taxkey_13);
+  my $konto_anlegen = $self->dbh->prepare($insert_chart) || $self->db_error($insert_chart);
 
-        # Sicherstellen, daß 3803 die richtige Bezeichnung hat
-        my $update_3803 = qq|update chart set description = 'Umsatzsteuer aus EG-Erwerb 16%' where accno = '3803'|;
-        do_query($update_3803);
+  # 13-1 (16%) korrigieren:
+  my $edit_taxkey_13 = qq|UPDATE tax SET taxdescription = 'Steuerpflichtige EG-Lieferung zum vollen Steuersatz', rate = '0.16', chart_id = (select id FROM chart where accno = '3803'), taxnumber = 3803 WHERE taxkey = '13'|;
+  $self->db_query($edit_taxkey_13);
 
-        # Zweiter  Eintrag für taxkey 13 in key: 19%
-        my $insert_taxkey_13_2 = qq|INSERT INTO tax ( taxkey, taxdescription, rate, chart_id, taxnumber ) VALUES ('13', 'Steuerpflichtige EG-Lieferung zum vollen Steuersatz', '0.19', (select id from chart where accno = '3804'), '3804')|;
+  # Sicherstellen, daß 3803 die richtige Bezeichnung hat
+  my $update_3803 = qq|update chart set description = 'Umsatzsteuer aus EG-Erwerb 16%' where accno = '3803'|;
+  $self->db_query($update_3803);
 
-        do_query($insert_taxkey_13_2);
+  # Zweiter  Eintrag für taxkey 13 in key: 19%
+  my $insert_taxkey_13_2 = qq|INSERT INTO tax ( taxkey, taxdescription, rate, chart_id, taxnumber ) VALUES ('13', 'Steuerpflichtige EG-Lieferung zum vollen Steuersatz', '0.19', (select id from chart where accno = '3804'), '3804')|;
 
-        # alle Konten finden, bei denen 3803 das Steuerautomatikkonto ist,
-        # und dort den zweiten Eintrag ab 1.1.2007 für 19% einstellen
-        my $sth_query  = $dbh->prepare(qq|select c.id from chart c join taxkeys t on (c.id = t.chart_id) where tax_id = (select id from tax where taxnumber = '3803')|);
-        my $sth_insert = $dbh->prepare(qq|INSERT INTO taxkeys ( taxkey_id, chart_id, tax_id, pos_ustva, startdate )
-                                                       VALUES (13, ?, (select id from tax where taxkey = 13 and rate = '0.19'),
-            (select pos_ustva from taxkeys where tax_id = (select id from tax where taxnumber = '3803') and pos_ustva > 0 limit 1),
-            '01.01.2007')|);
-        $sth_query->execute();
+  $self->db_query($insert_taxkey_13_2);
 
-        while (my $ref = $sth_query->fetchrow_hashref()) {
-          $sth_insert->execute($ref->{id});
-        }
-        $sth_query->finish();
-        $sth_insert->finish();
+  # alle Konten finden, bei denen 3803 das Steuerautomatikkonto ist,
+  # und dort den zweiten Eintrag ab 1.1.2007 für 19% einstellen
+  my $sth_query  = $self->dbh->prepare(qq|select c.id from chart c join taxkeys t on (c.id = t.chart_id) where tax_id = (select id from tax where taxnumber = '3803')|);
+  my $sth_insert = $self->dbh->prepare(<<SQL);
+    INSERT INTO taxkeys ( taxkey_id, chart_id, tax_id, pos_ustva, startdate )
+    VALUES              ( 13, ?, (select id from tax where taxkey = 13 and rate = '0.19'),
+                          (SELECT pos_ustva FROM taxkeys WHERE tax_id = (SELECT id FROM tax WHERE taxnumber = '3803') AND pos_ustva > 0 LIMIT 1),
+                         '01.01.2007' )
+SQL
+  $sth_query->execute;
 
-      }; # end code update
-  }; # end check if 3804 exists
+  while (my $ref = $sth_query->fetchrow_hashref) {
+    $sth_insert->execute($ref->{id});
+  }
+  $sth_query->finish;
+  $sth_insert->finish;
 
-}; # end do_update
+} # end run
 
 sub print_past_booking_warning {
-  print $main::form->parse_html_template("dbupgrade/SKR04_3804_update");
-};
+  print $::form->parse_html_template("dbupgrade/SKR04_3804_update");
+}
+
 sub print_3804_already_exists {
-  print $main::form->parse_html_template("dbupgrade/SKR04_3804_already_exists");
-};
+  print $::form->parse_html_template("dbupgrade/SKR04_3804_already_exists");
+}
 
-return do_update();
+1;
index 24dbd42edd7c04e7de1bf8a01af88c0296550b71..a1b4a9caa39773c6096cb40fcebc1dde0c7c13e8 100644 (file)
@@ -1,6 +1,12 @@
 # @tag: USTVA_abstraction
 # @description: Abstraktion der USTVA Report Daten. Dies vereinfacht die Integration von Steuerberichten anderer Nationen in kivitendo.
 # @depends: release_2_4_2
+package SL::DBUpgrade2::USTVA_abstraction;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
 
 # Abstraktionlayer between general Taxreports and USTVA
 # Most of the data and structures are not used yet, but maybe in future,
@@ -8,28 +14,8 @@
 
 ###################
 
-use strict;
-
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
-
-
 sub create_tables {
+  my ($self) = @_;
 
   # Watch out, SCHEMAs are new in Lx!
   my @queries = ( # Watch out, it's a normal array!
@@ -61,14 +47,15 @@ sub create_tables {
       },
   );
 
-  do_query("DROP SCHEMA tax CASCADE;", 1);
-  map({ do_query($_, 0); } @queries);
+  $self->db_query("DROP SCHEMA tax CASCADE;", 1);
+  map({ $self->db_query($_, 0); } @queries);
 
   return 1;
 
 }
 
 sub do_copy {
+  my ($self) = @_;
 
   my @copy_statements = (
     "INSERT INTO tax.report_categorys (id, description, subdescription) VALUES (?, ?, ?)",
@@ -78,85 +65,89 @@ sub do_copy {
 
   my @copy_data = (
     [ "0;;",
-      "1;Lieferungen und sonstige Leistungen;(einschließlich unentgeltlicher Wertabgaben)",
+      "1;Lieferungen und sonstige Leistungen;(einschließlich unentgeltlicher Wertabgaben)",
       "2;Innergemeinschaftliche Erwerbe;",
-      "3;Ergänzende Angaben zu Umsätzen;",
+      "3;Ergänzende Angaben zu Umsätzen;",
       "99;Summe;",
     ],
     ["0;0;;;",
-     "1;1;received;Steuerfreie Umsätze mit Vorsteuerabzug;",
-     "2;1;recieved;Steuerfreie Umsätze ohne Vorsteuerabzug;",
-     "3;1;recieved;Steuerpflichtige Umsätze;(Lieferungen und sonstige Leistungen einschl. unentgeltlicher Wertabgaben)",
+     "1;1;received;Steuerfreie Umsätze mit Vorsteuerabzug;",
+     "2;1;recieved;Steuerfreie Umsätze ohne Vorsteuerabzug;",
+     "3;1;recieved;Steuerpflichtige Umsätze;(Lieferungen und sonstige Leistungen einschl. unentgeltlicher Wertabgaben)",
      "4;2;recieved;Steuerfreie innergemeinschaftliche Erwerbe;",
      "5;2;recieved;Steuerpflichtige innergemeinschaftliche Erwerbe;",
-     "6;3;recieved;Umsätze, für die als Leistungsempfänger die Steuer nach Â§ 13b Abs. 2 UStG geschuldet wird;",
+     "6;3;recieved;Umsätze, für die als Leistungsempfänger die Steuer nach Ã‚§ 13b Abs. 2 UStG geschuldet wird;",
      "66;3;recieved;;",
-     "7;3;paied;Abziehbare Vorsteuerbeträge;",
-     "8;3;paied;Andere Steuerbeträge;",
+     "7;3;paied;Abziehbare Vorsteuerbeträge;",
+     "8;3;paied;Andere Steuerbeträge;",
      "99;99;;Summe;",
     ],
     ["0;keine;0;< < < keine UStVa Position > > >;;;19700101",
-     "1;41;1;Innergemeinschaftliche Lieferungen (§ 4 Nr. 1 Buchst. b UStG) an Abnehmer mit USt-IdNr.;0;0;19700101",
+     "1;41;1;Innergemeinschaftliche Lieferungen (§ 4 Nr. 1 Buchst. b UStG) an Abnehmer mit USt-IdNr.;0;0;19700101",
      "2;44;1;neuer Fahrzeuge an Abnehmer ohne USt-IdNr.;0;0;19700101",
-     "3;49;1;neuer Fahrzeuge außerhalb eines Unternehmens (§ 2a UStG);0;0;19700101",
-     "4;43;1;Weitere steuerfreie Umsätze mit Vorsteuerabzug;0;0;19700101",
-     "5;48;2;Umsätze nach Â§ 4 Nr. 8 bis 28 UStG;0;0;19700101",
+     "3;49;1;neuer Fahrzeuge auÃ\9ferhalb eines Unternehmens (§ 2a UStG);0;0;19700101",
+     "4;43;1;Weitere steuerfreie Umsätze mit Vorsteuerabzug;0;0;19700101",
+     "5;48;2;Umsätze nach Ã‚§ 4 Nr. 8 bis 28 UStG;0;0;19700101",
      "6;51;3;zum Steuersatz von 16 %;0;0;19700101",
      "7;511;3;;6;2;19700101",
      "8;81;3;zum Steuersatz von 19 %;0;0;19700101",
      "9;811;3;;8;2;19700101",
      "10;86;3;zum Steuersatz von 7 %;0;0;19700101",
      "11;861;3;;10;2;19700101",
-     "12;35;3;Umsätze, die anderen Steuersätzen unterliegen;0;0;19700101",
+     "12;35;3;Umsätze, die anderen Steuersätzen unterliegen;0;0;19700101",
      "13;36;3;;12;2;19700101",
-     "14;77;3;Lieferungen in das Ã¼brige Gemeinschaftsgebiet an Abnehmer mit USt-IdNr.;0;0;19700101",
-     "15;76;3;Umsätze, für die eine Steuer nach Â§ 24 UStG zu entrichten ist;0;0;19700101",
+     "14;77;3;Lieferungen in das Ã¼brige Gemeinschaftsgebiet an Abnehmer mit USt-IdNr.;0;0;19700101",
+     "15;76;3;Umsätze, für die eine Steuer nach Ã‚§ 24 UStG zu entrichten ist;0;0;19700101",
      "16;80;3;;15;2;19700101",
-     "17;91;4;Erwerbe nach Â§ 4b UStG;0;0;19700101",
+     "17;91;4;Erwerbe nach Ã‚§ 4b UStG;0;0;19700101",
      "18;97;5;zum Steuersatz von 16 %;0;0;19700101",
      "19;971;5;;18;2;19700101",
      "20;89;5;zum Steuersatz von 19 %;0;0;19700101",
      "21;891;5;;20;2;19700101",
      "22;93;5;zum Steuersatz von 7 %;0;0;19700101",
      "23;931;5;;22;2;19700101",
-     "24;95;5;zu anderen Steuersätzen;0;0;19700101",
+     "24;95;5;zu anderen Steuersätzen;0;0;19700101",
      "25;98;5;;24;2;19700101",
      "26;94;5;neuer Fahrzeuge von Lieferern ohne USt-IdNr. zum allgemeinen Steuersatz;0;0;19700101",
      "27;96;5;;26;2;19700101",
-     "28;42;66;Lieferungen des ersten Abnehmers bei innergemeinschaftlichen Dreiecksgeschäften (§ 25b Abs. 2 UStG);0;0;19700101",
-     "29;60;66;Steuerpflichtige Umsätze im Sinne des Â§ 13b Abs. 1 Satz 1 Nr. 1 bis 5 UStG, für die der Leistungsempfänger die Steuer schuldet;0;0;19700101",
-     "30;45;66;Nicht steuerbare Umsätze (Leistungsort nicht im Inland);0;0;19700101",
-     "31;52;6;Leistungen eines im Ausland ansässigen Unternehmers (§ 13b Abs. 1 Satz 1 Nr. 1 und 5 UStG);0;0;19700101",
+     "28;42;66;Lieferungen des ersten Abnehmers bei innergemeinschaftlichen Dreiecksgeschäften (§ 25b Abs. 2 UStG);0;0;19700101",
+     "29;60;66;Steuerpflichtige Umsätze im Sinne des Â§ 13b Abs. 1 Satz 1 Nr. 1 bis 5 UStG, für die der Leistungsempfänger die Steuer schuldet;0;0;19700101",
+     "30;45;66;Nicht steuerbare Umsätze (Leistungsort nicht im Inland);0;0;19700101",
+     "31;52;6;Leistungen eines im Ausland ansässigen Unternehmers (§ 13b Abs. 1 Satz 1 Nr. 1 und 5 UStG);0;0;19700101",
      "32;53;6;;31;2;19700101",
-     "33;73;6;Lieferungen sicherungsübereigneter Gegenstände und Umsätze, die unter das GrEStG fallen (§ 13b Abs. 1 Satz 1 Nr. 2 und 3 UStG);0;0;19700101",
+     "33;73;6;Lieferungen sicherungsübereigneter Gegenstände und Umsätze, die unter das GrEStG fallen (§ 13b Abs. 1 Satz 1 Nr. 2 und 3 UStG);0;0;19700101",
      "34;74;6;;33;2;19700101",
-     "35;84;6;Bauleistungen eines im Inland ansässigen Unternehmers (§ 13b Abs. 1 Satz 1 Nr. 4 UStG);0;0;19700101",
+     "35;84;6;Bauleistungen eines im Inland ansässigen Unternehmers (§ 13b Abs. 1 Satz 1 Nr. 4 UStG);0;0;19700101",
      "36;85;6;;35;2;19700101",
-     "37;65;6;Steuer infolge Wechsels der Besteuerungsform sowie Nachsteuer auf versteuerte Anzahlungen u. Ã¤. wegen Steuersatzänderung;;2;19700101",
-     "38;66;7;Vorsteuerbeträge aus Rechnungen von anderen Unternehmern (§ 15 Abs. 1 Satz 1 Nr. 1 UStG), aus Leistungen im Sinne des Â§ 13a Abs. 1 Nr. 6 UStG (§ 15 Abs. 1 Satz 1 Nr. 5 UStG) und aus innergemeinschaftlichen Dreiecksgeschäften (§ 25b Abs. 5 UStG);;2;19700101",
-     "39;61;7;Vorsteuerbeträge aus dem innergemeinschaftlichen Erwerb von Gegenständen (§ 15 Abs. 1 Satz 1 Nr. 3 UStG);;2;19700101",
-     "40;62;7;Entrichtete Einfuhrumsatzsteuer (§ 15 Abs. 1 Satz 1 Nr. 2 UStG);;2;19700101",
-     "41;67;7;Vorsteuerbeträge aus Leistungen im Sinne des Â§ 13b Abs. 1 UStG (§ 15 Abs. 1 Satz 1 Nr. 4 UStG);;2;19700101",
-     "42;63;7;Vorsteuerbeträge, die nach allgemeinen Durchschnittssätzen berechnet sind (§§ 23 und 23a UStG);;2;19700101",
-     "43;64;7;Berichtigung des Vorsteuerabzugs (§ 15a UStG);;2;19700101",
-     "44;59;7;Vorsteuerabzug für innergemeinschaftliche Lieferungen neuer Fahrzeuge außerhalb eines Unternehmens (§ 2a UStG) sowie von Kleinunternehmern im Sinne des Â§ 19 Abs. 1 UStG (§ 15 Abs. 4a UStG);;2;19700101",
-     "45;69;8;in Rechnungen unrichtig oder unberechtigt ausgewiesene Steuerbeträge (§ 14c UStG) sowie Steuerbeträge, die nach Â§ 4 Nr. 4a Satz 1 Buchst. a Satz 2, Â§ 6a Abs. 4 Satz 2, Â§ 17 Abs. 1 Satz 6 oder Â§ 25b Abs. 2 UStG geschuldet werden;;2;19700101",
-     "46;39;8;Anrechnung (Abzug) der festgesetzten Sondervorauszahlung für Dauerfristverlängerung (nur auszufüllen in der letzten Voranmeldung des Besteuerungszeitraums, in der Regel Dezember);;2;19700101",
+     "37;65;6;Steuer infolge Wechsels der Besteuerungsform sowie Nachsteuer auf versteuerte Anzahlungen u. Ã¤. wegen Steuersatzänderung;;2;19700101",
+     "38;66;7;Vorsteuerbeträge aus Rechnungen von anderen Unternehmern (§ 15 Abs. 1 Satz 1 Nr. 1 UStG), aus Leistungen im Sinne des Ã‚§ 13a Abs. 1 Nr. 6 UStG (§ 15 Abs. 1 Satz 1 Nr. 5 UStG) und aus innergemeinschaftlichen Dreiecksgeschäften (§ 25b Abs. 5 UStG);;2;19700101",
+     "39;61;7;Vorsteuerbeträge aus dem innergemeinschaftlichen Erwerb von Gegenständen (§ 15 Abs. 1 Satz 1 Nr. 3 UStG);;2;19700101",
+     "40;62;7;Entrichtete Einfuhrumsatzsteuer (§ 15 Abs. 1 Satz 1 Nr. 2 UStG);;2;19700101",
+     "41;67;7;Vorsteuerbeträge aus Leistungen im Sinne des Ã‚§ 13b Abs. 1 UStG (§ 15 Abs. 1 Satz 1 Nr. 4 UStG);;2;19700101",
+     "42;63;7;Vorsteuerbeträge, die nach allgemeinen Durchschnittssätzen berechnet sind (§§ 23 und 23a UStG);;2;19700101",
+     "43;64;7;Berichtigung des Vorsteuerabzugs (§ 15a UStG);;2;19700101",
+     "44;59;7;Vorsteuerabzug für innergemeinschaftliche Lieferungen neuer Fahrzeuge auÃ\9ferhalb eines Unternehmens (§ 2a UStG) sowie von Kleinunternehmern im Sinne des Ã‚§ 19 Abs. 1 UStG (§ 15 Abs. 4a UStG);;2;19700101",
+     "45;69;8;in Rechnungen unrichtig oder unberechtigt ausgewiesene Steuerbeträge (§ 14c UStG) sowie Steuerbeträge, die nach Ã‚§ 4 Nr. 4a Satz 1 Buchst. a Satz 2, Ã‚§ 6a Abs. 4 Satz 2, Ã‚§ 17 Abs. 1 Satz 6 oder Ã‚§ 25b Abs. 2 UStG geschuldet werden;;2;19700101",
+     "46;39;8;Anrechnung (Abzug) der festgesetzten Sondervorauszahlung für Dauerfristverlängerung (nur auszufüllen in der letzten Voranmeldung des Besteuerungszeitraums, in der Regel Dezember);;2;19700101",
   ],
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
-    my $sth   = $dbh->prepare($query) || mydberror($query);
+    my $query = $copy_statements[$statement];
+    my $sth   = $self->dbh->prepare($query) || $self->db_error($query);
 
     for my $copy_line ( 0 .. $#{$copy_data[$statement]} ) {
       #print $copy_data[$statement][$copy_line] . "<br />"
-      $sth->execute(split m/;/, $iconv->convert($copy_data[$statement][$copy_line]), -1) || mydberror($query);
+      $sth->execute(split m/;/, $copy_data[$statement][$copy_line], -1) || $self->db_error($query);
     }
     $sth->finish();
   }
   return 1;
 }
 
+sub run {
+  my ($self) = @_;
+  return $self->create_tables && $self->do_copy;
+}
 
-return create_tables() && do_copy();
+1;
index 1cafb2de9e20a7eee369b414bbfd8f2a82738464..2cffc3d787dfe317b12e4d71f785c226e1f66d7e 100644 (file)
@@ -1,71 +1,46 @@
 # @tag: USTVA_at
 # @description: USTVA Report Daten fuer Oesterreich. Vielen Dank an Gerhard Winkler..
 # @depends: USTVA_abstraction
+package SL::DBUpgrade2::USTVA_at;
 
 use strict;
+use utf8;
 
-use SL::DBUtils;
+use parent qw(SL::DBUpgrade2::Base);
 
-unless ( $main::form ) {
-  die("This script cannot be run from the command line.");
-}
+sub run {
+  my ($self) = @_;
 
-if ( check_coa('Austria') ){
-
-  if ( coa_is_empty() )  {
-    print qq|Eine leere Datenbank mit Kontenrahmen Ã–sterreich vorgefunden. <br />
-             Die Aktualisierungen werden eingespielt...<br />
-             <b>Achtung: Dieses Update ist ungetestet und bedarf weiterer Konfiguration</b>|;
-
-    return 1
-      && clear_tables(( 'tax.report_variables', 'tax.report_headings',
-                        'tax.report_categorys', 'taxkeys',
-                        'tax',                  'chart',
-                        'buchungsgruppen',
-                     ))
-      && do_copy_tax_report_structure()
-      && do_insert_chart()
-      && do_insert_tax()
-      && do_insert_taxkeys()
-      && do_insert_buchungsgruppen()
-    ;
+  if (!$self->check_coa('Austria')) {
+    print qq|Nichts zu tun in diesem Kontenrahmen.|;
+    return 1;
   }
-  else {
-    print qq|Eine Ã¶sterreichische Datenbank in der bereits Buchungssätze enthalten sind, kann nicht aktualisiert werden.<br />
+
+  if (!$self->is_coa_empty)  {
+    print qq|Eine Ã¶sterreichische Datenbank in der bereits Buchungssätze enthalten sind, kann nicht aktualisiert werden.<br />
              Bitte eine neue Datenbank mit Kontenrahmen 'Austria' anlegen.|;
     return 1;
   }
 
+  print qq|Eine leere Datenbank mit Kontenrahmen Ã–sterreich vorgefunden. <br />
+           Die Aktualisierungen werden eingespielt...<br />
+           <b>Achtung: Dieses Update ist ungetestet und bedarf weiterer Konfiguration</b>|;
+
+  return
+       $self->clear_tables('tax.report_variables', 'tax.report_headings',
+                           'tax.report_categorys', 'taxkeys',
+                           'tax',                  'chart',
+                           'buchungsgruppen')
+    && $self->do_copy_tax_report_structure()
+    && $self->do_insert_chart()
+    && $self->do_insert_tax()
+    && $self->do_insert_taxkeys()
+    && $self->do_insert_buchungsgruppen()
+      ;
 }
-else {
-  print qq|Nichts zu tun in diesem Kontenrahmen.|;
-  return 1;
-}
-
-return 0;
-
-######################################################
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
-
 
 sub clear_tables {
-
-  my @clear = @_;
+  my ($self, @clear) = @_;
 
   my @queries = (
       q{ DELETE FROM tax.report_categorys; },
@@ -73,44 +48,21 @@ sub clear_tables {
       q{ DELETE FROM tax.report_variables; },
   );
 
-  map({ do_query("DELETE FROM $_ ;", 0); } @clear);
+  map({ $self->db_query("DELETE FROM $_ ;", 0); } @clear);
 
   return 1;
 
 }
 
-sub check_coa {
-
-  my ( $want_coa ) = @_;
-
-  my $query = q{ SELECT count(*) FROM defaults WHERE coa = ? };
-  my ($have_coa) = selectrow_query($main::form, $dbh, $query, $want_coa);
-
-  return $have_coa;
-
-}
-
-sub coa_is_empty {
-
-  my $query = q{ SELECT count(*)
-                 FROM ar, ap, gl, invoice, acc_trans, customer, vendor, parts
-               };
-  my ($empty) = selectrow_query($main::form, $dbh, $query);
-
-  $empty = !$empty;
-
-  return $empty;
-}
-
-
 sub do_copy_tax_report_structure {
+  my ($self) = @_;
 
   my @queries = (
         "INSERT INTO tax.report_categorys (id, description, subdescription) VALUES (0, NULL, NULL)",
         "INSERT INTO tax.report_headings (id, category_id, type, description, subdescription) VALUES (0, 0, NULL, NULL, NULL)",
   );
 
-  map({ do_query($_); } @queries);
+  map({ $self->db_query($_); } @queries);
 
 
   my @copy_statements = (
@@ -120,66 +72,66 @@ sub do_copy_tax_report_structure {
 
   my @copy_data = (
     [
-      "1;000;0;a) Gesamtbetrag der Bemessungsgrundlage für Lieferungen und sonstige Leistungen (ohne den nachstehend angeführten Eigenverbrauch) einschließlich Anzahlungen (jeweils ohne Umsatzsteuer);2;1970-01-01",
-      "2;001;0;zuzüglich Eigenverbrauch (§1 Abs. 1 Z 2, Â§ 3 Abs. 2 und Â§ 3a Abs. 1a);2;1970-01-01",
-      "3;021;0;abzüglich Umsätze für die die Steuerschuld gemäߠ§ 19 Abs. 1 zweiter Satz sowie gemäߠ§ 19 Abs. 1a, Abs. 1b, Abs. 1c auf den Leistungsempfänger Ã¼bergegangen ist.;2;1970-01-01",
-      "4;011;0;a) Â§6 Abs. 1 Z 1 iVm Â§ 7 (Ausfuhrlieferungen);2;1970-01-01",
-      "5;012;0;b) Â§6 Abs. 1 Z 1 iVm Â§ 8 (Lohnveredelungen);2;1970-01-01",
-      "6;015;0;c) Â§6 Abs. 1 Z 2 bis 6 sowie Â§ 23 Abs. 5 (Seeschifffahrt, Luftfahrt, grenzüberschreitende Personenbeförderung, Diplomaten, Reisevorleistungen im Drittlandsgebiet usw.);2;1970-01-01",
-      "7;017;0;d) Art. 6 Abs. 1 (innergemeinschaftliche Lieferungen ohne die nachstehend gesondert anzuführenden Fahrzeuglieferungen);2;1970-01-01",
-      "8;018;0;e) Art. 6 Abs. 1, sofern Lieferungen neuer Fahrzeuge an Abnehmer ohne UID-Nummer bzw. durch Fahrzeuglieferer gemäß Art. 2 erfolgen.;2;1970-01-01",
-      "9;019;0;a) Â§ 6 Abs. 1 Z 9 lit. a (Grundstücksumsätze);2;1970-01-01",
-      "10;016;0;b) Â§6 Abs. 1 Z 27 (Kleinunternehmer);2;1970-01-01",
-      "11;020;0;c) Â§ 6 Abs. 1 Z ___ (übrige steuerfreie Umsätze ohne Vorsteuerabzug);2;1970-01-01",
+      "1;000;0;a) Gesamtbetrag der Bemessungsgrundlage für Lieferungen und sonstige Leistungen (ohne den nachstehend angeführten Eigenverbrauch) einschließlich Anzahlungen (jeweils ohne Umsatzsteuer);2;1970-01-01",
+      "2;001;0;zuzüglich Eigenverbrauch (§1 Abs. 1 Z 2, Ã‚§ 3 Abs. 2 und Ã‚§ 3a Abs. 1a);2;1970-01-01",
+      "3;021;0;abzüglich Umsätze für die die Steuerschuld gemäߠ§ 19 Abs. 1 zweiter Satz sowie gemäߠ§ 19 Abs. 1a, Abs. 1b, Abs. 1c auf den Leistungsempfänger Ã¼bergegangen ist.;2;1970-01-01",
+      "4;011;0;a) Ã‚§6 Abs. 1 Z 1 iVm Ã‚§ 7 (Ausfuhrlieferungen);2;1970-01-01",
+      "5;012;0;b) Ã‚§6 Abs. 1 Z 1 iVm Ã‚§ 8 (Lohnveredelungen);2;1970-01-01",
+      "6;015;0;c) Â§6 Abs. 1 Z 2 bis 6 sowie Â§ 23 Abs. 5 (Seeschifffahrt, Luftfahrt, grenzüberschreitende Personenbeförderung, Diplomaten, Reisevorleistungen im Drittlandsgebiet usw.);2;1970-01-01",
+      "7;017;0;d) Art. 6 Abs. 1 (innergemeinschaftliche Lieferungen ohne die nachstehend gesondert anzuführenden Fahrzeuglieferungen);2;1970-01-01",
+      "8;018;0;e) Art. 6 Abs. 1, sofern Lieferungen neuer Fahrzeuge an Abnehmer ohne UID-Nummer bzw. durch Fahrzeuglieferer gemäß Art. 2 erfolgen.;2;1970-01-01",
+      "9;019;0;a) Â§ 6 Abs. 1 Z 9 lit. a (Grundstücksumsätze);2;1970-01-01",
+      "10;016;0;b) Ã‚§6 Abs. 1 Z 27 (Kleinunternehmer);2;1970-01-01",
+      "11;020;0;c) Â§ 6 Abs. 1 Z ___ (übrige steuerfreie Umsätze ohne Vorsteuerabzug);2;1970-01-01",
       "12;022_links;0;20% Nominalsteuersatz;2;1970-01-01",
       "13;022_rechts;0;20% Nominalsteuersatz;2;1970-01-01",
-      "14;029_links;0;10% ermäßigter Steuersatz;2;1970-01-01",
-      "15;029_rechts;0;10% ermäßigter Steuersatz;2;1970-01-01",
-      "16;025_links;0;12% für Weinumsätze durch landwirtschaftliche Betriebe;2;1970-01-01",
-      "17;025_rechts;0;12% für Weinumsätze durch landwirtschaftliche Betriebe;2;1970-01-01",
-      "18;035_links;0;16% für Jungholz und Mittelberg;2;1970-01-01",
-      "19;035_rechts;0;16% für Jungholz und Mittelberg;2;1970-01-01",
-      "20;052_links;0;10% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
-      "21;052_rechts;0;10% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
-      "22;038_links;0;8% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
-      "23;038_rechts;0;8% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
-      "24;056;0;Steuerschuld gemäߠ§ 11 Abs. 12 und 14, Â§ 16 Abs. 2 sowie gemäß Art. 7 Abs. 4;2;1970-01-01",
-      "25;057;0;Steuerschuld gemäߠ§ 19 Abs. 1 zweiter Satz, Â§ 19 Abs. 1c sowie gemäß Art. 25 Abs. 5;2;1970-01-01",
-      "26;048;0;Steuerschuld gemäߠ§ 19 Abs. 1a (Bauleistungen);2;1970-01-01",
-      "27;044;0;Steuerschuld gemäߠ§ 19 Abs. 1b (Sicherungseigentum, Vorbehaltseigentum und Grundstücke im Zwangsversteigerungsverfahren);2;1970-01-01",
-      "28;070;0;Gesamtbetrag der Bemessungsgrundlagen für innergemeinschaftliche Erwerbe;2;1970-01-01",
-      "29;071;0;Davon Steuerfrei gemäß Art. 6 Abs 2;2;1970-01-01",
+      "14;029_links;0;10% ermäßigter Steuersatz;2;1970-01-01",
+      "15;029_rechts;0;10% ermäßigter Steuersatz;2;1970-01-01",
+      "16;025_links;0;12% für Weinumsätze durch landwirtschaftliche Betriebe;2;1970-01-01",
+      "17;025_rechts;0;12% für Weinumsätze durch landwirtschaftliche Betriebe;2;1970-01-01",
+      "18;035_links;0;16% für Jungholz und Mittelberg;2;1970-01-01",
+      "19;035_rechts;0;16% für Jungholz und Mittelberg;2;1970-01-01",
+      "20;052_links;0;10% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
+      "21;052_rechts;0;10% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
+      "22;038_links;0;8% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
+      "23;038_rechts;0;8% Zusatzsteuer für pauschalierte land- und forstwirtschaftliche Betriebe;2;1970-01-01",
+      "24;056;0;Steuerschuld gemäߠ§ 11 Abs. 12 und 14, Â§ 16 Abs. 2 sowie gemäß Art. 7 Abs. 4;2;1970-01-01",
+      "25;057;0;Steuerschuld gemäߠ§ 19 Abs. 1 zweiter Satz, Â§ 19 Abs. 1c sowie gemäß Art. 25 Abs. 5;2;1970-01-01",
+      "26;048;0;Steuerschuld gemäÃ\9f Ã‚§ 19 Abs. 1a (Bauleistungen);2;1970-01-01",
+      "27;044;0;Steuerschuld gemäߠ§ 19 Abs. 1b (Sicherungseigentum, Vorbehaltseigentum und Grundstücke im Zwangsversteigerungsverfahren);2;1970-01-01",
+      "28;070;0;Gesamtbetrag der Bemessungsgrundlagen für innergemeinschaftliche Erwerbe;2;1970-01-01",
+      "29;071;0;Davon Steuerfrei gemäß Art. 6 Abs 2;2;1970-01-01",
       "30;072_links;0;20% Nominalsteuersatz;2;1970-01-01",
       "31;072_rechts;0;20% Nominalsteuersatz;2;1970-01-01",
-      "32;073_links;0;10% ermäßigter Steuersatz;2;1970-01-01",
-      "33;073_rechts;0;10% ermäßigter Steuersatz;2;1970-01-01",
-      "34;075_links;0;16% für Jungholz und Mittelberg;2;1970-01-01",
-      "35;075_rechts;0;16% für Jungholz und Mittelberg;2;1970-01-01",
-      "36;076;0;Erwerbe gemäß Art. 3 Abs. 8 zweiter Satz, die im Mitgliedstaat des Bestimmungslandes besteuert worden sind;2;1970-01-01",
-      "37;077;0;Erwerbe gemäß Art. 3 Abs. 8 zweiter Satz, die gemäß Art. 25 Abs. 2 im Inland als besteuert gelten;2;1970-01-01",
-      "38;060;0;Gesamtbetrag der Vorsteuern (ohne die nachstehend gesondert anzuführenden Beträge);2;1970-01-01",
-      "39;061;0;Vorsteuern betreffend die entrichtete Einfuhrumsatzsteuer (§12 Abs. 1 Z 2 lit.a);2;1970-01-01",
-      "40;083;0;Vorsteuern betreffend die am Abgabenkonto verbuchte Einfuhrumsatzsteuer (§12 Abs. 1 Z 2 lit.b);2;1970-01-01",
+      "32;073_links;0;10% ermäßigter Steuersatz;2;1970-01-01",
+      "33;073_rechts;0;10% ermäßigter Steuersatz;2;1970-01-01",
+      "34;075_links;0;16% für Jungholz und Mittelberg;2;1970-01-01",
+      "35;075_rechts;0;16% für Jungholz und Mittelberg;2;1970-01-01",
+      "36;076;0;Erwerbe gemäß Art. 3 Abs. 8 zweiter Satz, die im Mitgliedstaat des Bestimmungslandes besteuert worden sind;2;1970-01-01",
+      "37;077;0;Erwerbe gemäß Art. 3 Abs. 8 zweiter Satz, die gemäß Art. 25 Abs. 2 im Inland als besteuert gelten;2;1970-01-01",
+      "38;060;0;Gesamtbetrag der Vorsteuern (ohne die nachstehend gesondert anzuführenden Beträge);2;1970-01-01",
+      "39;061;0;Vorsteuern betreffend die entrichtete Einfuhrumsatzsteuer (§12 Abs. 1 Z 2 lit.a);2;1970-01-01",
+      "40;083;0;Vorsteuern betreffend die am Abgabenkonto verbuchte Einfuhrumsatzsteuer (§12 Abs. 1 Z 2 lit.b);2;1970-01-01",
       "41;065;0;Vorsteuern aus dem innergemeinschaftlichen Erwerb;2;1970-01-01",
-      "42;066;0;Vorsteuern betreffend der Steuerschuld gemäߠ§ 19 Abs. 1c sowie gemäß Art. 25 Abs. 5;2;1970-01-01",
-      "43;082;0;Vorsteuern betreffend der Steuerschuld gemäߠ§ 19 Abs. 1a (Bauleistungen);2;1970-01-01",
-      "44;087;0;Vorsteuern betreffend die Steuerschuld gemäߠ§ 19 Abs. 1b (Sicherungseigentum, Vorbehaltseigentum und Grundstücke im Zwangsversteigerungsverfahren);2;1970-01-01",
-      "45;064;0;Vorsteuern gemäߠ§12 Abs. 16 und Vorsteuern für innergemeinschaftliche Lieferungen neuer Fahrzeuge von Fahrzeuglieferanten gemäß Art. 2;2;1970-01-01",
-      "46;062;0;Davon gemäߠ§ 12 Abs. 3 iVm Abs. 4 und 5;2;1970-01-01",
-      "47;063;0;Berichtigung gemäߠ§ 12 Abs. 10 und 11;2;1970-01-01",
-      "48;067;0;Berichtigung gemäߠ§ 16;2;1970-01-01",
+      "42;066;0;Vorsteuern betreffend der Steuerschuld gemäߠ§ 19 Abs. 1c sowie gemäß Art. 25 Abs. 5;2;1970-01-01",
+      "43;082;0;Vorsteuern betreffend der Steuerschuld gemäÃ\9f Ã‚§ 19 Abs. 1a (Bauleistungen);2;1970-01-01",
+      "44;087;0;Vorsteuern betreffend die Steuerschuld gemäߠ§ 19 Abs. 1b (Sicherungseigentum, Vorbehaltseigentum und Grundstücke im Zwangsversteigerungsverfahren);2;1970-01-01",
+      "45;064;0;Vorsteuern gemäߠ§12 Abs. 16 und Vorsteuern für innergemeinschaftliche Lieferungen neuer Fahrzeuge von Fahrzeuglieferanten gemäß Art. 2;2;1970-01-01",
+      "46;062;0;Davon gemäÃ\9f Ã‚§ 12 Abs. 3 iVm Abs. 4 und 5;2;1970-01-01",
+      "47;063;0;Berichtigung gemäÃ\9f Ã‚§ 12 Abs. 10 und 11;2;1970-01-01",
+      "48;067;0;Berichtigung gemäÃ\9f Ã‚§ 16;2;1970-01-01",
       "49;090;0;Sonstige Berichtigungen;2;1970-01-01",
       "50;095;0;Zahllast/Gutschrift;2;1970-01-01",
     ],
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
-    my $sth   = $dbh->prepare($query) || mydberror($query);
+    my $query = $copy_statements[$statement];
+    my $sth   = $self->dbh->prepare($query) || $self->db_error($query);
 
     for my $copy_line ( 0 .. $#{$copy_data[$statement]} ) {
       #print $copy_data[$statement][$copy_line] . "<br />"
-      $sth->execute(split m/;/, $iconv->convert($copy_data[$statement][$copy_line]), -1) || mydberror($query);
+      $sth->execute(split m/;/, $copy_data[$statement][$copy_line], -1) || $self->db_error($query);
     } #/
     $sth->finish();
   }
@@ -187,39 +139,40 @@ sub do_copy_tax_report_structure {
 }
 
 sub do_insert_chart {
+  my ($self) = @_;
 
   my @copy_statements = (
-      "INSERT INTO chart VALUES (1, '0000', 'AUFWENDUNGEN FÜR INGANGSETZEN UND ERWEITERN DES BETRIEBES', 'H', 'A', '', '00', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.276724', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (1, '0000', 'AUFWENDUNGEN FÜR INGANGSETZEN UND ERWEITERN DES BETRIEBES', 'H', 'A', '', '00', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.276724', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (2, '0010', 'Firmenwert', 'A', 'A', 'AP_amount', '015', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.28365', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (3, '0100', 'IMMATERIELLE VERMÖGENSGEGENSTÄNDE', 'H', 'A', '', '01', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.288542', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (3, '0100', 'IMMATERIELLE VERMÖGENSGEGENSTÄNDE', 'H', 'A', '', '01', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.288542', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (4, '0110', 'Rechte', 'A', 'A', 'AP_amount', '011', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.291937', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (5, '0200', 'GRUNDSTÜCKE', 'H', 'A', '', '02-03', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.294929', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (6, '0210', 'unbebaute Grundstücke', 'A', 'A', 'AP_amount', '020', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.297958', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (7, '0220', 'bebaute Grundstücke', 'A', 'A', 'AP_amount', '021', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.300987', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (8, '0229', 'kum. Abschreibung bebaute Grundstücke', 'A', 'A', '', '039', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.304114', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (5, '0200', 'GRUNDSTÜCKE', 'H', 'A', '', '02-03', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.294929', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (6, '0210', 'unbebaute Grundstücke', 'A', 'A', 'AP_amount', '020', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.297958', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (7, '0220', 'bebaute Grundstücke', 'A', 'A', 'AP_amount', '021', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.300987', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (8, '0229', 'kum. Abschreibung bebaute Grundstücke', 'A', 'A', '', '039', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.304114', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (10, '0410', 'Maschinen', 'A', 'A', 'AP_amount', '041', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.312216', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (11, '0419', 'kum. Abschreibung Maschinen', 'A', 'A', '', '069', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.316198', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (12, '0500', 'FAHRZEUGE', 'H', 'A', '', '06', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.319978', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (13, '0510', 'Fahrzeuge', 'A', 'A', 'AP_amount', '063', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.323002', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (14, '0519', 'kum. Abschreibung Fahrzeuge', 'A', 'A', '', '069', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.326041', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (15, '0600', 'BETRIEBS- UND GESCHÄFTSAUSSTATTUNG', 'H', 'A', '', '06', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.330691', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (16, '0620', 'Büroeinrichtungen', 'A', 'A', 'AP_amount', '066', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.33373', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (17, '0625', 'kum. Abschreibung Betriebs- und Geschäftsausstattung', 'A', 'A', '', '069', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.336939', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (15, '0600', 'BETRIEBS- UND GESCHÄFTSAUSSTATTUNG', 'H', 'A', '', '06', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.330691', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (16, '0620', 'Büroeinrichtungen', 'A', 'A', 'AP_amount', '066', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.33373', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (17, '0625', 'kum. Abschreibung Betriebs- und Geschäftsausstattung', 'A', 'A', '', '069', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.336939', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (18, '0700', 'GELEISTETE ANZAHLUNGEN', 'H', 'A', '', '07', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.340614', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (19, '0800', 'FINANZANLAGEN', 'H', 'A', '', '08-09', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.3436', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (20, '0810', 'Beteiligungen', 'A', 'A', 'AP_amount', '081', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.346638', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (21, '0820', 'Wertpapiere', 'A', 'A', 'AP_amount', '080', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.351452', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (22, '1100', 'ROHSTOFFE', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.354419', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (23, '1120', 'Vorräte - Rohstoffe', 'A', 'A', 'IC', '110-119', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.357447', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (23, '1120', 'Vorräte - Rohstoffe', 'A', 'A', 'IC', '110-119', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.357447', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (24, '1200', 'BEZOGENE TEILE', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.360423', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (25, '1220', 'Vorräte - bezogene Teile', 'A', 'A', 'IC', '120-129', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.363627', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (25, '1220', 'Vorräte - bezogene Teile', 'A', 'A', 'IC', '120-129', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.363627', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (26, '1300', 'HILFS- UND BETRIEBSSTOFFE', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.368083', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (27, '1320', 'Hilfsstoffe', 'A', 'A', 'IC', '130-134', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.372229', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (28, '1350', 'Betriebssstoffe', 'A', 'A', 'IC', '135-139', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.375303', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (29, '1400', 'UNFERTIGE ERZEUGNISSE', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.378277', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (30, '1420', 'Vorräte - unfertige Erzeugnisse', 'A', 'A', 'IC', '140-149', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.381463', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (30, '1420', 'Vorräte - unfertige Erzeugnisse', 'A', 'A', 'IC', '140-149', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.381463', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (31, '1500', 'FERTIGE ERZEUGNISSE', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.384434', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (34, '1540', 'Vorräte - Gruppe C', 'A', 'A', 'IC', '150-159', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.395426', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (34, '1540', 'Vorräte - Gruppe C', 'A', 'A', 'IC', '150-159', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.395426', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (35, '1600', 'WAREN', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.39872', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (36, '1700', 'NOCH NICHT ABGERECHNETE LEISTUNGEN', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.401807', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (37, '1800', 'GELEISTETE ANZAHLUNGEN', 'H', 'A', '', '1', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.404851', NULL, NULL, NULL);",
@@ -230,7 +183,7 @@ sub do_insert_chart {
       "INSERT INTO chart VALUES (43, '2320', 'sonstige Forderungen', 'A', 'A', 'AP_amount', '23-24', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.428868', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (44, '2500', 'FORDERUNGEN AUS ABGABENVERRECHNUNG', 'H', 'A', '', '2', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.432042', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (46, '2600', 'WERTPAPIERE UND ANTEILE', 'H', 'A', '', '2', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.438205', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (47, '2620', 'Wertpapiere Umlaufvermögen', 'A', 'A', 'AP_amount', '26', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.441382', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (47, '2620', 'Wertpapiere Umlaufvermögen', 'A', 'A', 'AP_amount', '26', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.441382', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (48, '2700', 'KASSABESTAND', 'H', 'A', '', '2', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.444391', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (50, '2800', 'SCHECKS, GUTHABEN BEI KREDITINSTITUTEN', 'H', 'A', '', '2', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.45237', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (53, '3100', 'LANGFRISTIGE VERBINDLICHKEITEN', 'H', 'L', '', '3', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.461985', NULL, NULL, NULL);",
@@ -239,7 +192,7 @@ sub do_insert_chart {
       "INSERT INTO chart VALUES (58, '3500', 'VERBINDLICHKEITEN FINANZAMT', 'H', 'L', '', '35', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.480487', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (105, '7611', 'Reinigungsmaterial', 'A', 'E', 'AP_amount', '', 9, NULL, 11, 3, NULL, false, '2006-01-28 18:22:52.649072', '2006-02-03 15:26:38.591173', NULL, NULL);",
       "INSERT INTO chart VALUES (163, '7340', 'Reisekosten', 'A', 'E', 'AP_amount', '', 9, NULL, NULL, 3, NULL, false, '2006-02-03 15:38:11.636188', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (74, '4600', 'SONSTIGE ERLÖSE', 'H', 'I', '', '4', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.539718', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (74, '4600', 'SONSTIGE ERLÖSE', 'H', 'I', '', '4', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.539718', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (95, '7120', 'Grundsteuer', 'A', 'E', '', '', 0, NULL, 12, 3, NULL, false, '2006-01-28 18:22:52.612982', '2006-02-03 15:07:18.076256', NULL, NULL);",
       "INSERT INTO chart VALUES (94, '7110', 'Ertragssteuern', 'A', 'E', '', '', 0, NULL, 12, 3, NULL, false, '2006-01-28 18:22:52.60961', '2006-02-03 15:07:57.018877', NULL, NULL);",
       "INSERT INTO chart VALUES (159, '7130', 'Gewerbl. Sozialversicherung', 'A', 'E', 'AP_amount', '', 0, NULL, 12, 3, NULL, false, '2006-02-03 15:16:10.635938', NULL, NULL, NULL);",
@@ -253,86 +206,86 @@ sub do_insert_chart {
       "INSERT INTO chart VALUES (89, '6710', 'freiwilliger Sozialaufwand', 'A', 'E', '', '660-665', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.592541', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (90, '7000', 'ABSCHREIBUNGEN', 'H', 'E', '', '7', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.595566', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (91, '7010', 'Abschreibungen', 'A', 'E', '', '700', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.598657', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (92, '7020', 'geringwertige Wirtschaftsgüter', 'A', 'E', 'AP_amount', '701-708', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.601829', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (92, '7020', 'geringwertige Wirtschaftsgüter', 'A', 'E', 'AP_amount', '701-708', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.601829', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (93, '7100', 'SONSTIGE STEUERN', 'H', 'E', '', '71', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.604871', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (96, '7200', 'INSTANDHALTUNGSAUFWAND', 'H', 'E', '', '7', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.6171', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (98, '7300', 'TRANSPORTKOSTEN', 'H', 'L', '', '73', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.623721', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (100, '7400', 'MIET-,PACHT-,LEASING-, LIZENZAUFWAND', 'H', 'E', '', '74', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.631869', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (113, '7710', 'Sachversicherung', 'A', 'E', 'AP_amount', '', 0, NULL, 13, NULL, NULL, false, '2006-01-28 18:22:52.677258', '2006-02-03 15:19:30.793109', NULL, NULL);",
       "INSERT INTO chart VALUES (103, '7600', 'VERWALTUNGSKOSTEN', 'H', 'E', '', '76', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.641023', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (161, '7780', 'Beiträge zur Berufsvertretung', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, 3, NULL, false, '2006-02-03 15:33:11.055578', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (161, '7780', 'Beiträge zur Berufsvertretung', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, 3, NULL, false, '2006-02-03 15:33:11.055578', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (102, '7420', 'Betriebsk. und ant. AfA Garage + Werkst.', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, 3, NULL, false, '2006-01-28 18:22:52.637918', '2006-02-03 15:41:13.126408', NULL, NULL);",
-      "INSERT INTO chart VALUES (112, '7700', 'VERSICHERUNGEN UND ÃœBRIGE AUFWÄNDUNGEN', 'H', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.674186', '2006-02-03 15:44:43.301845', NULL, NULL);",
-      "INSERT INTO chart VALUES (114, '8000', 'FINANZERTRÄGE UND FINANZAUFWÄNDUNGEN', 'H', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.680743', '2006-02-03 15:45:08.299546', NULL, NULL);",
-      "INSERT INTO chart VALUES (33, '1530', 'Vorräte Gruppe B', 'A', 'A', 'IC', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.392343', '2006-02-03 16:25:53.167131', NULL, NULL);",
+      "INSERT INTO chart VALUES (112, '7700', 'VERSICHERUNGEN UND ÃœBRIGE AUFWÄNDUNGEN', 'H', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.674186', '2006-02-03 15:44:43.301845', NULL, NULL);",
+      "INSERT INTO chart VALUES (114, '8000', 'FINANZERTRÄGE UND FINANZAUFWÄNDUNGEN', 'H', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.680743', '2006-02-03 15:45:08.299546', NULL, NULL);",
+      "INSERT INTO chart VALUES (33, '1530', 'Vorräte Gruppe B', 'A', 'A', 'IC', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.392343', '2006-02-03 16:25:53.167131', NULL, NULL);",
       "INSERT INTO chart VALUES (120, '9020', 'nicht einbezahltes Kapital', 'A', 'Q', '', '919', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.700926', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (121, '9200', 'KAPITALRÜCKLAGEN', 'H', 'Q', '', '9', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.703925', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (122, '9210', 'freie Rücklage', 'A', 'Q', '', '920-929', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.708819', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (121, '9200', 'KAPITALRÜCKLAGEN', 'H', 'Q', '', '9', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.703925', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (122, '9210', 'freie Rücklage', 'A', 'Q', '', '920-929', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.708819', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (123, '9300', 'GEWINN', 'H', 'Q', '', '939', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.712247', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (124, '9310', 'Gewinnvortrag Vorjahr', 'A', 'Q', '', '980', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.716177', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (125, '9320', 'Jahresgewinn', 'A', 'Q', '', '985', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.719991', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (126, '9400', 'RÜCKSTELLUNGEN', 'H', 'L', '', '3', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.723021', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (127, '9420', 'Abfertigungsrückstellung', 'A', 'L', '', '300', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.726006', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (128, '9430', 'Urlaubsrückstellung', 'A', 'L', '', '304-309', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.730698', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (126, '9400', 'RÜCKSTELLUNGEN', 'H', 'L', '', '3', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.723021', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (127, '9420', 'Abfertigungsrückstellung', 'A', 'L', '', '300', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.726006', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (128, '9430', 'Urlaubsrückstellung', 'A', 'L', '', '304-309', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.730698', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (129, '9700', 'EINLAGEN STILLER GESELLSCHAFTER', 'H', 'Q', '', '9', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.73381', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (71, '4300', 'UMSATZ DIENSTLEISTUNGEN', 'H', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.529746', '2006-01-28 18:34:28.843136', NULL, NULL);",
-      "INSERT INTO chart VALUES (9, '0300', 'BETRIEBS- UND GESCHÄFTSGEBÄUDE', 'H', 'A', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.308885', '2006-02-02 09:31:17.849895', NULL, NULL);",
+      "INSERT INTO chart VALUES (9, '0300', 'BETRIEBS- UND GESCHÄFTSGEBÄUDE', 'H', 'A', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.308885', '2006-02-02 09:31:17.849895', NULL, NULL);",
       "INSERT INTO chart VALUES (45, '2530', 'sonstige Forderungen aus Abgebenverrechnung', 'A', 'A', 'AP_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.435243', '2006-02-02 09:59:42.729713', NULL, NULL);",
-      "INSERT INTO chart VALUES (67, '4000', 'BETRIEBLICHE ERTRÄGE', 'H', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.513926', '2006-02-02 10:05:21.278993', NULL, NULL);",
-      "INSERT INTO chart VALUES (75, '4630', 'Erlöse aus Abgang vom Anlagevermögen', 'A', 'I', 'AR_amount:IC_income', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.542817', '2006-02-02 10:09:41.959462', NULL, NULL);",
-      "INSERT INTO chart VALUES (131, '4450', 'Erlösschmälerung durch Skontoaufwand', 'A', 'I', 'AR_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.740526', '2006-02-02 10:20:51.822294', NULL, NULL);",
-      "INSERT INTO chart VALUES (144, '4640', 'Erträge aus Abgang vom Anlagevermögen', 'A', 'I', 'AR_amount:IC_income', '', 0, NULL, NULL, NULL, NULL, false, '2006-02-02 10:24:49.118289', '2006-02-02 10:25:34.716838', NULL, NULL);",
-      "INSERT INTO chart VALUES (118, '9000', 'KAPITAL, UNVERSTEUERTE RÜCKLAGEN, ABSCHLUSS- UND EVIDENZKONTEN', 'H', 'Q', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.694841', '2006-02-02 10:28:27.424046', NULL, NULL);",
+      "INSERT INTO chart VALUES (67, '4000', 'BETRIEBLICHE ERTRÄGE', 'H', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.513926', '2006-02-02 10:05:21.278993', NULL, NULL);",
+      "INSERT INTO chart VALUES (75, '4630', 'Erlöse aus Abgang vom Anlagevermögen', 'A', 'I', 'AR_amount:IC_income', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.542817', '2006-02-02 10:09:41.959462', NULL, NULL);",
+      "INSERT INTO chart VALUES (131, '4450', 'Erlösschmälerung durch Skontoaufwand', 'A', 'I', 'AR_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.740526', '2006-02-02 10:20:51.822294', NULL, NULL);",
+      "INSERT INTO chart VALUES (144, '4640', 'Erträge aus Abgang vom Anlagevermögen', 'A', 'I', 'AR_amount:IC_income', '', 0, NULL, NULL, NULL, NULL, false, '2006-02-02 10:24:49.118289', '2006-02-02 10:25:34.716838', NULL, NULL);",
+      "INSERT INTO chart VALUES (118, '9000', 'KAPITAL, UNVERSTEUERTE RÜCKLAGEN, ABSCHLUSS- UND EVIDENZKONTEN', 'H', 'Q', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.694841', '2006-02-02 10:28:27.424046', NULL, NULL);",
       "INSERT INTO chart VALUES (147, '9410', 'Privatentnahme', 'A', 'Q', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-02-02 11:52:04.383364', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (76, '5000', 'MATERIALAUFWAND', 'H', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.545768', '2006-02-02 12:02:53.065559', NULL, NULL);",
       "INSERT INTO chart VALUES (160, '7140', 'Fremdenverkehrsabgabe', 'A', 'E', 'AP_amount', '', 0, NULL, 12, 3, NULL, false, '2006-02-03 15:16:52.380825', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (99, '7310', 'Frachtaufwand durch Dritte', 'A', 'E', 'AP_amount:IC_expense', '', 9, NULL, NULL, 3, NULL, false, '2006-01-28 18:22:52.628717', '2006-02-03 15:22:49.082217', NULL, NULL);",
       "INSERT INTO chart VALUES (152, '7320', 'KFZ-Aufwand', 'A', 'E', 'AP_amount:IC_expense', '', 9, NULL, NULL, 3, NULL, false, '2006-02-02 12:22:18.511562', '2006-02-03 15:23:31.584235', NULL, NULL);",
       "INSERT INTO chart VALUES (80, '5600', 'VERBRAUCH BRENN- UND TREIBSTOFFE, ENERGIE UND WASSER', 'H', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.56006', '2006-02-02 12:17:24.198896', NULL, NULL);",
-      "INSERT INTO chart VALUES (109, '7390', 'Porto und Postgebühren', 'A', 'E', 'AP_amount:IC_expense', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.661848', '2006-02-02 12:28:47.456197', NULL, NULL);",
+      "INSERT INTO chart VALUES (109, '7390', 'Porto und Postgebühren', 'A', 'E', 'AP_amount:IC_expense', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.661848', '2006-02-02 12:28:47.456197', NULL, NULL);",
       "INSERT INTO chart VALUES (101, '7410', 'Miete und Pachtaufwand', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.634888', '2006-02-02 12:29:27.184902', NULL, NULL);",
       "INSERT INTO chart VALUES (107, '7620', 'Zeitungen und Zeitschriften', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.655683', '2006-02-02 12:32:43.287819', NULL, NULL);",
       "INSERT INTO chart VALUES (106, '7670', 'Werbung und Marketing', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.652584', '2006-02-02 12:33:37.934111', NULL, NULL);",
-      "INSERT INTO chart VALUES (110, '7680', 'Repräsentationsaufwand', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.665034', '2006-02-02 12:35:16.950252', NULL, NULL);",
+      "INSERT INTO chart VALUES (110, '7680', 'Repräsentationsaufwand', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.665034', '2006-02-02 12:35:16.950252', NULL, NULL);",
       "INSERT INTO chart VALUES (111, '7750', 'Rechtsberatung', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.671109', '2006-02-02 12:36:56.116865', NULL, NULL);",
       "INSERT INTO chart VALUES (153, '7755', 'Steuerberatung', 'A', 'E', 'AP_amount', '', 0, NULL, NULL, NULL, NULL, false, '2006-02-02 12:37:35.558667', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (115, '8280', 'Bankzinsen und Gebühren', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.683783', '2006-02-02 12:41:44.274229', NULL, NULL);",
-      "INSERT INTO chart VALUES (117, '8110', 'Erträge aus Zinsen', 'A', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.691802', '2006-02-02 12:42:41.520779', NULL, NULL);",
-      "INSERT INTO chart VALUES (132, '8050', 'Erträge aus Wertpapieren', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.743602', '2006-02-02 12:44:29.033245', NULL, NULL);",
-      "INSERT INTO chart VALUES (104, '7610', 'Büromaterial', 'A', 'E', 'AP_amount', '', 9, NULL, 11, 3, NULL, false, '2006-01-28 18:22:52.644151', '2006-02-03 15:25:38.53287', NULL, NULL);",
-      "INSERT INTO chart VALUES (116, '8010', 'Erträge aus Beteiligungen', 'A', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.688643', '2006-02-02 12:47:19.930787', NULL, NULL);",
-      "INSERT INTO chart VALUES (119, '9010', 'Kapital, Geschäftsanteile', 'A', 'Q', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.69788', '2006-02-02 12:48:41.514201', NULL, NULL);",
+      "INSERT INTO chart VALUES (115, '8280', 'Bankzinsen und Gebühren', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.683783', '2006-02-02 12:41:44.274229', NULL, NULL);",
+      "INSERT INTO chart VALUES (117, '8110', 'Erträge aus Zinsen', 'A', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.691802', '2006-02-02 12:42:41.520779', NULL, NULL);",
+      "INSERT INTO chart VALUES (132, '8050', 'Erträge aus Wertpapieren', 'A', 'E', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.743602', '2006-02-02 12:44:29.033245', NULL, NULL);",
+      "INSERT INTO chart VALUES (104, '7610', 'Büromaterial', 'A', 'E', 'AP_amount', '', 9, NULL, 11, 3, NULL, false, '2006-01-28 18:22:52.644151', '2006-02-03 15:25:38.53287', NULL, NULL);",
+      "INSERT INTO chart VALUES (116, '8010', 'Erträge aus Beteiligungen', 'A', 'I', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.688643', '2006-02-02 12:47:19.930787', NULL, NULL);",
+      "INSERT INTO chart VALUES (119, '9010', 'Kapital, Geschäftsanteile', 'A', 'Q', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.69788', '2006-02-02 12:48:41.514201', NULL, NULL);",
       "INSERT INTO chart VALUES (108, '7380', 'Telefonkosten, Internetkosten', 'A', 'E', 'AP_amount', '', 9, NULL, 11, 3, NULL, false, '2006-01-28 18:22:52.658721', '2006-02-03 15:24:27.553821', NULL, NULL);",
-      "INSERT INTO chart VALUES (32, '1520', 'Vorräte Gruppe A', 'A', 'A', 'IC', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.389168', '2006-02-03 16:26:08.72507', NULL, NULL);",
+      "INSERT INTO chart VALUES (32, '1520', 'Vorräte Gruppe A', 'A', 'A', 'IC', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.389168', '2006-02-03 16:26:08.72507', NULL, NULL);",
       "INSERT INTO chart VALUES (52, '2820', 'Bankguthaben', 'A', 'A', 'AR_paid:AP_paid', '', 0, NULL, NULL, 1, NULL, false, '2006-01-28 18:22:52.458922', '2006-02-04 15:00:18.424069', NULL, NULL);",
-      "INSERT INTO chart VALUES (59, '3550', 'Finanzamt Verrechnung Körperschaftssteuer', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.483655', '2006-02-08 20:09:47.697565', NULL, NULL);",
+      "INSERT INTO chart VALUES (59, '3550', 'Finanzamt Verrechnung Körperschaftssteuer', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.483655', '2006-02-08 20:09:47.697565', NULL, NULL);",
       "INSERT INTO chart VALUES (60, '3540', 'Finanzamt Verrechnung Umsatzsteuer', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.48865', '2006-02-08 20:15:23.622013', NULL, NULL);",
       "INSERT INTO chart VALUES (78, '5030', 'Warengruppe 1 10 %', 'A', 'E', 'AP_amount:IC_cogs', '', 7, NULL, 4, 2, NULL, false, '2006-01-28 18:22:52.553586', '2006-02-08 20:31:31.539794', NULL, NULL);",
       "INSERT INTO chart VALUES (79, '5040', 'Warengruppe 2 20%', 'A', 'E', 'AP_amount:IC_cogs', '', 9, NULL, 4, 2, NULL, false, '2006-01-28 18:22:52.55679', '2006-02-03 14:44:38.100283', NULL, NULL);",
       "INSERT INTO chart VALUES (155, '5210', 'Sonst. Verbrauchsmaterial', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 3, NULL, false, '2006-02-03 14:49:06.01478', '2006-02-03 14:54:51.813269', NULL, NULL);",
       "INSERT INTO chart VALUES (146, '9850', 'Schlussbilanz', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-02-02 10:36:45.059659', '2006-02-02 10:38:05.014595', NULL, NULL);",
-      "INSERT INTO chart VALUES (150, '5640', 'Verbrauch von sonstigen Ã–len und Schmierstoffen', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 1, 19, false, '2006-02-02 12:07:52.512006', '2006-02-03 15:01:09.867763', NULL, NULL);",
-      "INSERT INTO chart VALUES (130, '9810', 'Eröffnungsbilanz', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.736825', '2006-02-02 10:37:49.001565', NULL, NULL);",
+      "INSERT INTO chart VALUES (150, '5640', 'Verbrauch von sonstigen Ã–len und Schmierstoffen', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 1, 19, false, '2006-02-02 12:07:52.512006', '2006-02-03 15:01:09.867763', NULL, NULL);",
+      "INSERT INTO chart VALUES (130, '9810', 'Eröffnungsbilanz', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.736825', '2006-02-02 10:37:49.001565', NULL, NULL);",
       "INSERT INTO chart VALUES (164, '9800', 'BILANZKONTEN', 'H', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-03-06 22:23:47.795675', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (148, '5620', 'Verbrauch von Treibstoffen (Diesel)', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, 17, false, '2006-02-02 11:59:26.297394', '2006-02-03 15:00:02.362976', NULL, NULL);",
-      "INSERT INTO chart VALUES (149, '5630', 'Verbrauch von Treib- und Schmierstoffen für Motorsägen', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 1, 19, false, '2006-02-02 12:01:05.969406', '2006-02-03 15:00:30.512596', NULL, NULL);",
-      "INSERT INTO chart VALUES (158, '5650', 'gasförmige Brennstoffe', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, 12, false, '2006-02-03 15:02:36.649746', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (149, '5630', 'Verbrauch von Treib- und Schmierstoffen für Motorsägen', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 1, 19, false, '2006-02-02 12:01:05.969406', '2006-02-03 15:00:30.512596', NULL, NULL);",
+      "INSERT INTO chart VALUES (158, '5650', 'gasförmige Brennstoffe', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, 12, false, '2006-02-03 15:02:36.649746', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (156, '5710', 'Warenbezugskosten', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, 8, false, '2006-02-03 14:56:21.395879', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (157, '5750', 'Fremdarbeit', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, NULL, false, '2006-02-03 14:58:23.887944', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (154, '5310', 'Arbeitskleidung', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 1, NULL, false, '2006-02-03 14:48:10.349391', '2006-02-03 16:23:46.154559', NULL, NULL);",
       "INSERT INTO chart VALUES (151, '5510', 'Verbrauchswerkzeug', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, NULL, false, '2006-02-02 12:19:18.193535', '2006-02-03 14:51:29.573924', NULL, NULL);",
       "INSERT INTO chart VALUES (81, '5610', 'Energie (Strom und Wasser)', 'A', 'E', 'AP_amount', '', 9, NULL, 4, 2, NULL, false, '2006-01-28 18:22:52.563249', '2006-02-03 14:59:32.292173', NULL, NULL);",
       "INSERT INTO chart VALUES (97, '7210', 'Reparatur und Instandhaltung', 'A', 'E', 'AP_amount', '', 9, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.620315', '2006-02-03 15:14:08.186799', NULL, NULL);",
-      "INSERT INTO chart VALUES (72, '4320', 'Erlöse Beratung', 'A', 'I', 'AR_amount:IC_sale:IC_income', '', 3, 51, 1, 1, 2, false, '2006-01-28 18:22:52.533226', '2006-02-04 23:05:45.241847', NULL, NULL);",
-      "INSERT INTO chart VALUES (73, '4330', 'Erlöse Programmierung', 'A', 'I', 'AR_amount:IC_sale:IC_income', '', 3, 51, 1, 1, 1, false, '2006-01-28 18:22:52.536409', '2006-02-04 23:06:03.959353', NULL, NULL);",
-      "INSERT INTO chart VALUES (69, '4030', 'Erlöse - Softwareverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 2, 86, 1, 1, 1, false, '2006-01-28 18:22:52.521819', '2006-02-02 10:06:58.91888', NULL, NULL);",
-      "INSERT INTO chart VALUES (70, '4040', 'Erlöse - Ersatzteilverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 3, 51, 1, 1, 1, false, '2006-01-28 18:22:52.524987', '2006-02-02 10:07:34.327738', NULL, NULL);",
+      "INSERT INTO chart VALUES (72, '4320', 'Erlöse Beratung', 'A', 'I', 'AR_amount:IC_sale:IC_income', '', 3, 51, 1, 1, 2, false, '2006-01-28 18:22:52.533226', '2006-02-04 23:05:45.241847', NULL, NULL);",
+      "INSERT INTO chart VALUES (73, '4330', 'Erlöse Programmierung', 'A', 'I', 'AR_amount:IC_sale:IC_income', '', 3, 51, 1, 1, 1, false, '2006-01-28 18:22:52.536409', '2006-02-04 23:06:03.959353', NULL, NULL);",
+      "INSERT INTO chart VALUES (69, '4030', 'Erlöse - Softwareverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 2, 86, 1, 1, 1, false, '2006-01-28 18:22:52.521819', '2006-02-02 10:06:58.91888', NULL, NULL);",
+      "INSERT INTO chart VALUES (70, '4040', 'Erlöse - Ersatzteilverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 3, 51, 1, 1, 1, false, '2006-01-28 18:22:52.524987', '2006-02-02 10:07:34.327738', NULL, NULL);",
       "INSERT INTO chart VALUES (57, '3310', 'Verbindlichkeiten aus Lieferungen & Leistungen', 'A', 'L', 'AP', '', 0, NULL, NULL, 1, NULL, false, '2006-01-28 18:22:52.477485', '2006-02-02 18:12:21.634302', NULL, NULL);",
       "INSERT INTO chart VALUES (51, '2810', 'Schecks', 'A', 'A', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.455807', NULL, NULL, NULL);",
-      "INSERT INTO chart VALUES (55, '3120', 'Kredite von Eigentümern', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.471098', NULL, NULL, NULL);",
+      "INSERT INTO chart VALUES (55, '3120', 'Kredite von Eigentümern', 'A', 'L', '', '', 0, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.471098', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (49, '2710', 'Kassa', 'A', 'A', 'AR_paid', '', 0, NULL, NULL, 1, NULL, false, '2006-01-28 18:22:52.449148', '2006-02-04 14:59:14.410329', NULL, NULL);",
       "INSERT INTO chart VALUES (77, '5020', 'Warengruppe 0', 'A', 'E', 'IC:IC_cogs:AP_amount', '', 7, NULL, NULL, NULL, 8, false, '2006-01-28 18:22:52.550381', '2006-02-08 20:30:42.871241', NULL, NULL);",
-      "INSERT INTO chart VALUES (68, '4020', 'Erlöse - Hardwareverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 2, 86, 1, 1, 1, false, '2006-01-28 18:22:52.51796', '2006-02-04 23:05:12.810823', NULL, NULL);",
+      "INSERT INTO chart VALUES (68, '4020', 'Erlöse - Hardwareverkauf', 'A', 'I', 'AR_amount:IC_sale', '', 2, 86, 1, 1, 1, false, '2006-01-28 18:22:52.51796', '2006-02-04 23:05:12.810823', NULL, NULL);",
       "INSERT INTO chart VALUES (40, '2010', 'Forderungen Lieferung & Leistung', 'A', 'A', 'AR', '200-207', NULL, NULL, NULL, NULL, NULL, false, '2006-01-28 18:22:52.41697', NULL, NULL, NULL);",
       "INSERT INTO chart VALUES (65, '2510', 'Vorsteuer 10%', 'A', 'E', 'AR_tax:AP_tax:IC_taxpart:IC_taxservice', NULL, 0, 66, NULL, NULL, NULL, false, '2006-01-28 18:22:52.505337', '2006-02-02 17:38:40.373624', NULL, NULL);",
       "INSERT INTO chart VALUES (64, '2512', 'Vorsteuer 12%', 'A', 'E', 'AR_tax:AP_tax:IC_taxpart:IC_taxservice', NULL, 0, 66, NULL, NULL, NULL, false, '2006-01-28 18:22:52.502023', '2006-02-08 20:14:19.543049', NULL, NULL);",
@@ -343,22 +296,23 @@ sub do_insert_chart {
       "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('0400','MASCHINEN','H','04-05','A','');",
       "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7411','Lizenzen','A','748-749','E','AP_amount');",
       "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7631','Internetkosten','A','738-739','E','AP_amount:IC_expense');",
-      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7632','Reise- und Repräsentationsaufwand','A','734-735','E','');",
-      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7634','Registrierungsgebühren','A','748-749','E','AP_amount');",
-      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('8020','Bankzinsen und Gebühren','A','80-83','E','');",
+      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7632','Reise- und Repräsentationsaufwand','A','734-735','E','');",
+      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('7634','Registrierungsgebühren','A','748-749','E','AP_amount');",
+      "insert into chart (accno,description,charttype,gifi_accno,category,link) values ('8020','Bankzinsen und Gebühren','A','80-83','E','');",
 
 
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
+    my $query = $copy_statements[$statement];
       #print $query . "<br />";  # Diagnose only!
-      do_query($query, 0);
+      $self->db_query($query, 0);
   }
 
   return 1;
 }
 sub do_insert_tax {
+  my ($self) = @_;
 
   my @copy_statements = (
       "INSERT INTO tax (chart_id, taxnumber, taxkey, taxdescription, itime, mtime, rate, id) VALUES (65, '2510', 7, 'Vorsteuer 10%', '2006-01-30 11:08:23.332857', '2006-02-08 20:28:09.63567', 0.10000, 173);",
@@ -374,14 +328,15 @@ sub do_insert_tax {
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
+    my $query = $copy_statements[$statement];
       #print $query . "<br />";  # Diagnose only!
-      do_query($query, 0);
+      $self->db_query($query, 0);
   }
   return 1;
 }
 
 sub do_insert_taxkeys {
+  my ($self) = @_;
 
   my @copy_statements = (
       "INSERT INTO taxkeys VALUES (230, 69, 177, 2, NULL, '1970-01-01');",
@@ -496,9 +451,9 @@ sub do_insert_taxkeys {
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
+    my $query = $copy_statements[$statement];
       #print $query . "<br />";  # Diagnose only!
-      do_query($query, 0);
+      $self->db_query($query, 0);
   }
 
 return 1;
@@ -506,21 +461,21 @@ return 1;
 }
 
 sub do_insert_buchungsgruppen {
+  my ($self) = @_;
 
   my @copy_statements = (
-      "INSERT INTO buchungsgruppen VALUES (256, 'Erlöse aus Dienstleistungen', 23, 72, 99, 72, 77, 72, 77, 72, 77, 3);",
-      "INSERT INTO buchungsgruppen VALUES (254, 'Erlöse aus Warenlieferungen', 23, 68, 77, 72, 77, 72, 77, 72, 77, 2);",
-      "INSERT INTO buchungsgruppen VALUES (255, 'Erlöse aus Dienstleistungen', 23, 72, 77, 72, 77, 72, 77, 72, 77, 1);",
+      "INSERT INTO buchungsgruppen VALUES (256, 'Erlöse aus Dienstleistungen', 23, 72, 99, 72, 77, 72, 77, 72, 77, 3);",
+      "INSERT INTO buchungsgruppen VALUES (254, 'Erlöse aus Warenlieferungen', 23, 68, 77, 72, 77, 72, 77, 72, 77, 2);",
+      "INSERT INTO buchungsgruppen VALUES (255, 'Erlöse aus Dienstleistungen', 23, 72, 77, 72, 77, 72, 77, 72, 77, 1);",
   );
 
   for my $statement ( 0 .. $#copy_statements ) {
-    my $query = $iconv->convert($copy_statements[$statement]);
+    my $query = $copy_statements[$statement];
       #print $query . "<br />";  # Diagnose only!
-      do_query($query, 0);
+      $self->db_query($query, 0);
   }
 
-return 1;
+  return 1;
 }
 
-
-
+1;
diff --git a/sql/Pg-upgrade2/acc_trans_booleans_not_null.sql b/sql/Pg-upgrade2/acc_trans_booleans_not_null.sql
new file mode 100644 (file)
index 0000000..983e53d
--- /dev/null
@@ -0,0 +1,14 @@
+-- @tag: acc_trans_booleans_not_null
+-- @description: Alte acc_trans boolean-Einträge mit NULL-Werten auf false setzen
+-- @depends: release_3_0_0
+-- @charset: utf-8
+
+UPDATE acc_trans SET cleared        = 'f' where cleared        IS NULL;
+UPDATE acc_trans SET ob_transaction = 'f' where ob_transaction IS NULL;
+UPDATE acc_trans SET cb_transaction = 'f' where cb_transaction IS NULL;
+UPDATE acc_trans SET fx_transaction = 'f' where fx_transaction IS NULL;
+
+ALTER TABLE acc_trans ALTER cleared        SET NOT NULL;
+ALTER TABLE acc_trans ALTER ob_transaction SET NOT NULL;
+ALTER TABLE acc_trans ALTER cb_transaction SET NOT NULL;
+ALTER TABLE acc_trans ALTER fx_transaction SET NOT NULL;
index 7d57efbc1cad7d2c6f508d4a672486d507a3b35d..a7e17481aa05bc8fd8e422224a94f59093eb2d62 100644 (file)
@@ -1,36 +1,23 @@
 # @tag: acc_trans_constraints
 # @description: Fügt NOT-NULL-Constraints ein für die Spalten
 # @depends:
-# @charset: UTF-8
+package SL::DBUpgrade2::acc_trans_constraints;
 
 use utf8;
 use strict;
 
-die("This script cannot be run from the command line.") unless ($main::form);
+use parent qw(SL::DBUpgrade2::Base);
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   my $query = qq|SELECT count(*) FROM acc_trans WHERE chart_id IS NULL|;
-  my ($no_chart_id) = $dbh->selectrow_array($query);
+  my ($no_chart_id) = $self->dbh->selectrow_array($query);
   $query = qq|SELECT count(*) FROM acc_trans WHERE trans_id IS NULL|;
-  my ($no_trans_id) = $dbh->selectrow_array($query);
+  my ($no_trans_id) = $self->dbh->selectrow_array($query);
 
-  $form->{no_chart_id}=$no_chart_id;
-  $form->{no_trans_id}=$no_trans_id;
+  $::form->{no_chart_id}=$no_chart_id;
+  $::form->{no_trans_id}=$no_trans_id;
 
   if ($no_chart_id > 0 or $no_trans_id > 0){
     #list all invalid transactions where only chart_id is null:
@@ -97,13 +84,12 @@ sub do_update {
                 LEFT JOIN project p ON (p.id=acc.project_id)
                 WHERE acc.chart_id IS NULL;|;
 
-    my $sth = $dbh->prepare($query);
-    $sth->execute || $main::form->dberror($query);
+    my $sth = $self->dbh->prepare($query);
+    $sth->execute || $::form->dberror($query);
 
-    $main::form->{NO_CHART_ID} = [];
+    $::form->{NO_CHART_ID} = [];
     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-      map {$ref->{$_} = $::locale->{iconv_utf8}->convert($ref->{$_})} keys %$ref;
-      push @{ $main::form->{NO_CHART_ID} }, $ref;
+      push @{ $::form->{NO_CHART_ID} }, $ref;
     }
     $sth->finish;
 
@@ -123,12 +109,11 @@ sub do_update {
                 LEFT JOIN project p ON (p.id=acc.project_id)
                 WHERE acc.trans_id IS NULL;|;
 
-    $sth = $dbh->prepare($query);
-    $sth->execute || $main::form->dberror($query);
+    $sth = $self->dbh->prepare($query);
+    $sth->execute || $::form->dberror($query);
 
-    $main::form->{NO_TRANS_ID} = [];
+    $::form->{NO_TRANS_ID} = [];
     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-      map {$ref->{$_} = $::locale->{iconv_utf8}->convert($ref->{$_})} keys %$ref;
       $ref->{category} = ($ref->{category} eq 'A') ? $::locale->text('Account Category A')
         : ($ref->{category} eq 'E') ? $::locale->text('Account Category E')
         : ($ref->{category} eq 'L') ? $::locale->text('Account Category L')
@@ -137,7 +122,7 @@ sub do_update {
         : ($ref->{category} eq 'C') ? $::locale->text('Account Category C')
         : ($ref->{category} eq 'G') ? $::locale->text('Account Category G')
         : $::locale->text('Unknown Category') . ': ' . $ref->{category};
-      push @{ $main::form->{NO_TRANS_ID} }, $ref;
+      push @{ $::form->{NO_TRANS_ID} }, $ref;
     }
     $sth->finish;
 
@@ -148,12 +133,12 @@ sub do_update {
   $query = qq|ALTER TABLE acc_trans ALTER COLUMN chart_id SET NOT NULL;|;
   $query .= qq|ALTER TABLE acc_trans ALTER COLUMN trans_id SET NOT NULL;|;
 
-  do_query($query);
+  $self->db_query($query);
   return 1;
 }
 
 sub print_error_message {
-  print $main::form->parse_html_template("dbupgrade/acc_trans_constraints");
+  print $::form->parse_html_template("dbupgrade/acc_trans_constraints");
 }
 
-return do_update();
+1;
index 1b8e5cadb02bcb2f49ab985ba3247163b062ebc7..f90d731574be31e7a3217310e084891090198f78 100644 (file)
@@ -1,30 +1,18 @@
 # @tag: acc_trans_id_uniqueness
 # @description: Sorgt dafür, dass acc_trans.acc_trans_id eindeutig ist
 # @depends: release_2_6_1
-# @charset: utf-8
+package SL::DBUpgrade2::acc_trans_id_uniqueness;
 
 use utf8;
 use strict;
-use Data::Dumper;
 
-die "This script cannot be run from the command line." unless $::form;
+use parent qw(SL::DBUpgrade2::Base);
 
-sub mydberror {
-  my ($msg) = @_;
-  die $dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr;
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
+use SL::DBUtils;
 
-  return if $dbh->do($query);
-
-  mydberror($query) unless ($may_fail);
-  $dbh->rollback();
-  $dbh->begin_work();
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   my $query = <<SQL;
     SELECT acc_trans_id, trans_id, itime, mtime
     FROM acc_trans
@@ -34,7 +22,7 @@ sub do_update {
     ORDER BY trans_id, itime, mtime NULLS FIRST
 SQL
 
-  my @entries = selectall_hashref_query($form, $dbh, $query);
+  my @entries = selectall_hashref_query($::form, $self->dbh, $query);
 
   return 1 unless @entries;
 
@@ -45,7 +33,7 @@ SQL
     ))
 SQL
 
-  do_query($query, 0);
+  $self->db_query($query, 0);
 
   my %skipped_acc_trans_ids;
   foreach my $entry (@entries) {
@@ -62,11 +50,11 @@ SQL
           AND (mtime $mtime)
 SQL
 
-      do_query($query, 0);
+      $self->db_query($query, 0);
     }
   }
 
   return 1;
 }
 
-return do_update();
+1;
index c9c720b0c609ab17b526e4001c9a8df9420b4c3b..67e8e8d68b95f2714dcf6c0fbd3035d21a901ffe 100644 (file)
@@ -1,50 +1,38 @@
 # @tag: add_more_constraints_fibu_projekt_xplace3
 # @description: Falls der Datenbestand es unproblematisch hergibt, ein paar 'schärfere' Constraints für die acc_trans gesetzt. Keine acc_trans-Eintrag ohne trans_id oder chart_id. Ferner project_id in acc_trans als Fremdschlüssel für project definiert.
 # @depends: release_2_6_0 fix_acc_trans_ap_taxkey_bug
-# @charset: utf-8
+package SL::DBUpgrade2::add_more_constraints_fibu_projekt_xplace3;
 
-use utf8;
 use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
+use utf8;
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_query {
-  my ($query, $may_fail) = @_;
+use SL::DBUtils;
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   my @queries;
 
-  # die project_id in der acc_trans ist auch zwingend fremdschlüssel in project 
+  # die project_id in der acc_trans ist auch zwingend fremdschlüssel in project
   push @queries, "ALTER TABLE acc_trans ADD FOREIGN KEY (project_id) REFERENCES project(id)";
 
   my $query = qq|select count(*) from acc_trans where chart_id is NULL|;
-  my $sth_all_groups = prepare_execute_query($form, $dbh, $query);
+  my $sth_all_groups = prepare_execute_query($::form, $self->dbh, $query);
   while (my $hash_ref = $sth_all_groups->fetchrow_hashref()) {  # Schleife
     if ($hash_ref->{count} eq 0){
-      # Falls wir keine alte buggy Installation haben, ist es super die 
+      # Falls wir keine alte buggy Installation haben, ist es super die
       # Gewissheit zu haben, dass kein acc_trans-Eintrag ohne chart_id vorhanden ist
       push @queries, "ALTER TABLE acc_trans ALTER COLUMN chart_id SET NOT NULL";
     }
   }
   $sth_all_groups->finish();
   my $query = qq|select count(*) from acc_trans where trans_id is NULL|;
-  my $sth_all_groups = prepare_execute_query($form, $dbh, $query);
+  my $sth_all_groups = prepare_execute_query($::form, $self->dbh, $query);
   while (my $hash_ref = $sth_all_groups->fetchrow_hashref()) {  # Schleife
     if ($hash_ref->{count} eq 0){
-      # Falls wir keine alte buggy Installation haben, ist es super die 
+      # Falls wir keine alte buggy Installation haben, ist es super die
       # Gewissheit zu haben, dass kein acc_trans-Eintrag ohne trans_id vorhanden ist
       push @queries, "ALTER TABLE acc_trans ALTER COLUMN trans_id SET NOT NULL";
     }
@@ -53,13 +41,12 @@ sub do_update {
 
   # if in doubt use brute force ;-) jb
   foreach my $query (@queries){
-    my $sth   = prepare_query($form, $dbh, $query);
-    do_statement($form,$sth,$query);
+    my $sth   = prepare_query($::form, $self->dbh, $query);
+    do_statement($::form,$sth,$query);
     $sth->finish();
   }
-  $dbh ->commit();
+  $self->dbh ->commit();
   return 1;
 }
 
-return do_update();
-
+1;
diff --git a/sql/Pg-upgrade2/ap_deliverydate.sql b/sql/Pg-upgrade2/ap_deliverydate.sql
new file mode 100644 (file)
index 0000000..9d06aa1
--- /dev/null
@@ -0,0 +1,5 @@
+-- @tag: ap_deliverydate
+-- @description: deliverydate zu Einkaufsrechnung hinzufügen
+-- @depends: release_3_0_0
+-- @charset: utf-8
+ALTER TABLE ap ADD COLUMN deliverydate date;
index 0579675cbaf5e8e4ca4aa9ed7031ff7a22de6755..040d9b64e8ac5e7222fcba634bcfaa46a6916c2d 100644 (file)
@@ -2,21 +2,19 @@
 # @description: Zusätzliches Recht alle Kunden / Lieferanten editieren, war bisher standardmäßig IMMER so und kann jetzt deaktiviert werden
 #               falls es deaktiviert wird, kann ich den Kunden / Lieferanten nur editieren wenn ich selber als Verkäufer eingetragen bin
 # @depends: release_2_6_3
-# @charset: utf-8
+package SL::DBUpgrade2::auth_enable_ct_all_edit;
 
-use utf8;
 use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
+use utf8;
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_update {
-  my $dbh   = $main::auth->dbconnect();
+use SL::DBUtils;
+
+sub run {
+  my ($self) = @_;
+
+  $self->dbh($::auth->dbconnect);
   my $query = <<SQL;
     SELECT id
     FROM auth."group"
@@ -28,24 +26,23 @@ sub do_update {
     )
 SQL
 
-  my @group_ids = selectall_array_query($form, $dbh, $query);
+  my @group_ids = selectall_array_query($::form, $self->dbh, $query);
   if (@group_ids) {
     $query = <<SQL;
       INSERT INTO auth.group_rights (group_id, "right",          granted)
       VALUES                        (?,        'customer_vendor_all_edit', TRUE)
 SQL
-    my $sth = prepare_query($form, $dbh, $query);
+    my $sth = prepare_query($::form, $self->dbh, $query);
 
     foreach my $id (@group_ids) {
-      do_statement($form, $sth, $query, $id);
+      do_statement($::form, $sth, $query, $id);
     }
 
     $sth->finish();
-    $dbh->commit();
+    $self->dbh->commit();
   }
 
   return 1;
 }
 
-return do_update();
-
+1;
index dbe3eb32bae2899cf01fd5ae3b49da731ccfc6a4..726dd1b9b8ca9d5d39d8da0935c8f5490d8ee9fa 100644 (file)
@@ -1,21 +1,19 @@
 # @tag: auth_enable_edit_prices
 # @description: Zusätzliches Recht readonly für das Attribut readonly bei Preisen und Rabatten im Textfeld. Das Skript hakt standardmässig dieses Recht an, sodass es keinen Unterschied zu vorhergehenden Version gibt.
 # @depends: release_2_6_3
-# @charset: utf-8
+package SL::DBUpgrade2::auth_enable_edit_prices;
 
-use utf8;
 use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
+use utf8;
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_update {
-  my $dbh   = $main::auth->dbconnect();
+use SL::DBUtils;
+
+sub run {
+  my ($self) = @_;
+
+  $self->dbh($::auth->dbconnect);
   my $query = <<SQL;
     SELECT id
     FROM auth."group"
@@ -27,24 +25,23 @@ sub do_update {
     )
 SQL
 
-  my @group_ids = selectall_array_query($form, $dbh, $query);
+  my @group_ids = selectall_array_query($::form, $self->dbh, $query);
   if (@group_ids) {
     $query = <<SQL;
       INSERT INTO auth.group_rights (group_id, "right",          granted)
       VALUES                        (?,        'edit_prices', TRUE)
 SQL
-    my $sth = prepare_query($form, $dbh, $query);
+    my $sth = prepare_query($::form, $self->dbh, $query);
 
     foreach my $id (@group_ids) {
-      do_statement($form, $sth, $query, $id);
+      do_statement($::form, $sth, $query, $id);
     }
 
     $sth->finish();
-    $dbh->commit();
+    $self->dbh->commit();
   }
 
   return 1;
 }
 
-return do_update();
-
+1;
index c6101e664624a460323c2a980fab61478b06edac..2a8595c613e917a1816362efb332fcfe621dd8da 100644 (file)
@@ -1,21 +1,19 @@
 # @tag: auth_enable_sales_all_edit
 # @description: Neues gruppenbezogenes Recht für den Bereich Verkauf hinzugefügt (sales_all_edit := Nur wenn angehakt, können Verkaufsdokumente von anderen Bearbeitern eingesehen werden) Das Skript hakt standardmässig dieses Recht an, sodass es keinen Unterschied zu vorhergehenden Version gibt.
 # @depends: release_2_6_0
-# @charset: utf-8
+package SL::DBUpgrade2::auth_enable_sales_all_edit;
 
-use utf8;
 use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
+use utf8;
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_update {
-  my $dbh   = $main::auth->dbconnect();
+use SL::DBUtils;
+
+sub run {
+  my ($self) = @_;
+
+  $self->dbh($::auth->dbconnect);
   my $query = <<SQL;
     SELECT id
     FROM auth."group"
@@ -27,24 +25,23 @@ sub do_update {
     )
 SQL
 
-  my @group_ids = selectall_array_query($form, $dbh, $query);
+  my @group_ids = selectall_array_query($::form, $self->dbh, $query);
   if (@group_ids) {
     $query = <<SQL;
       INSERT INTO auth.group_rights (group_id, "right",          granted)
       VALUES                        (?,        'sales_all_edit', TRUE)
 SQL
-    my $sth = prepare_query($form, $dbh, $query);
+    my $sth = prepare_query($::form, $self->dbh, $query);
 
     foreach my $id (@group_ids) {
-      do_statement($form, $sth, $query, $id);
+      do_statement($::form, $sth, $query, $id);
     }
 
     $sth->finish();
-    $dbh->commit();
+    $self->dbh->commit();
   }
 
   return 1;
 }
 
-return do_update();
-
+1;
diff --git a/sql/Pg-upgrade2/background_job_change_create_periodic_invoices_to_daily.pl b/sql/Pg-upgrade2/background_job_change_create_periodic_invoices_to_daily.pl
new file mode 100644 (file)
index 0000000..ace35be
--- /dev/null
@@ -0,0 +1,23 @@
+# @tag: background_job_change_create_periodic_invoices_to_daily
+# @description: Hintergrundjob zum Erzeugen periodischer Rechnungen täglich ausführen
+# @depends: release_3_0_0
+package SL::DBUpgrade2::background_job_change_create_periodic_invoices_to_daily;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+use SL::DB::BackgroundJob;
+
+sub run {
+  my ($self) = @_;
+
+  foreach my $job (@{ SL::DB::Manager::BackgroundJob->get_all(where => [ package_name => 'CreatePeriodicInvoices' ]) }) {
+    $job->update_attributes(cron_spec => '0 3 * * *', next_run_at => undef);
+  }
+
+  return 1;
+}
+
+1;
old mode 100644 (file)
new mode 100755 (executable)
index 0236cd5..65d07c9
@@ -1,13 +1,18 @@
-#!/usr/bin/perl
 # @tag: background_jobs_3
 # @description: Backgroundjob Cleanup einrichten
 # @depends: emmvee_background_jobs_2
-# @charset: utf-8
+package SL::DBUpgrade2::background_jobs_3;
 
 use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
 
 use SL::BackgroundJob::BackgroundJobCleanup;
 
-SL::BackgroundJob::BackgroundJobCleanup->create_job;
+sub run {
+  SL::BackgroundJob::BackgroundJobCleanup->create_job;
+  return 1;
+}
 
 1;
index 5b7893b47bf5a8fd5e12f7c890c8d86f35d8c5ea..ff95ff0a673b0d1a9682e638a8032f24684052fc 100644 (file)
@@ -1,64 +1,49 @@
 # @tag: charts_without_taxkey
 # @description: Fügt für jedes Konto, was keinen Steuerschlüssel hat, den Steuerschlüssel 0 hinzu
 # @depends:
-# @charset: UTF-8
+package SL::DBUpgrade2::charts_without_taxkey;
 
-use utf8;
 use strict;
-use SL::Locale;
-
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
+use utf8;
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
 
+sub run {
+  my ($self) = @_;
 
-sub do_update {
-  my ($taxkey0_with_taxes_exists) = $dbh->selectrow_array("SELECT COUNT(*) FROM tax WHERE taxkey=0 AND NOT rate=0;"); 
+  my ($taxkey0_with_taxes_exists) = $self->dbh->selectrow_array("SELECT COUNT(*) FROM tax WHERE taxkey=0 AND NOT rate=0;");
 
   if ($taxkey0_with_taxes_exists > 0){
     print_error_message();
     return 0;
   }
 
-  my ($taxkey0_exists) = $dbh->selectrow_array("SELECT COUNT(*) FROM tax WHERE taxkey=0");
+  my ($taxkey0_exists) = $self->dbh->selectrow_array("SELECT COUNT(*) FROM tax WHERE taxkey=0");
 
   if ($taxkey0_exists == 0){
     my $insert_taxkey0 = <<SQL;
-INSERT INTO tax 
+INSERT INTO tax
   (rate, taxkey, taxdescription)
   VALUES
   (0, 0, 'Keine Steuer');
 SQL
-    do_query($insert_taxkey0);
+    $self->db_query($insert_taxkey0);
     print $::locale->text("taxkey 0 with taxrate 0 was created.");
   };
-  
+
   my $insert_taxkeys = <<SQL;
-INSERT INTO taxkeys 
-  (chart_id, tax_id, taxkey_id, startdate) 
-  SELECT 
-  c.id, (SELECT id FROM tax WHERE taxkey=0), 0, '1970-01-01' 
+INSERT INTO taxkeys
+  (chart_id, tax_id, taxkey_id, startdate)
+  SELECT
+  c.id, (SELECT id FROM tax WHERE taxkey=0), 0, '1970-01-01'
   FROM chart c WHERE c.id NOT IN (SELECT chart_id FROM taxkeys);
 SQL
-    do_query($insert_taxkeys);
+    $self->db_query($insert_taxkeys);
     return 1;
-}; # end do_update
+} # end run
 
 sub print_error_message {
-  print $main::form->parse_html_template("dbupgrade/taxkey_update");
-};
+  print $::form->parse_html_template("dbupgrade/taxkey_update");
+}
 
-return do_update();
+1;
index bd54707775d2bd81adee4b3b6d16321260cd86e7..892284799d655ddd1e06f196748547fe653d108d 100644 (file)
@@ -1,18 +1,19 @@
 # @tag: contacts_add_cp_position
 # @description: Feld 'Funktion/Position' zu Kontakten
 # @depends: release_3_0_0
-# @charset: utf-8
+package SL::DBUpgrade2::contacts_add_cp_position;
 
-package contacts_add_cp_position;
 use strict;
+use utf8;
 
-die 'This script cannot be run from the command line.' if !$::form;
+use parent qw(SL::DBUpgrade2::Base);
 
-my $query = 'ALTER TABLE contacts ADD COLUMN cp_position VARCHAR(75)';
+sub run {
+  my ($self) = @_;
 
-if (!$dbh->do($query)) {
-  $dbh->rollback;
-  $dbh->begin_work;
+  $self->db_query('ALTER TABLE contacts ADD COLUMN cp_position VARCHAR(75)', 1);
+
+  return 1;
 }
 
 1;
index f1dde25bf6e129e64d283394a147cdf5bfd29307..f830e7c96af0419c64f044768db2b4c70b03f6da 100644 (file)
@@ -1,24 +1,25 @@
 # @tag: contacts_add_street_and_zipcode_and_city
 # @description: Spalten hinzuf&uuml;gen.
 # @depends: release_2_7_0
-# @charset: utf-8
+package SL::DBUpgrade2::contacts_add_street_and_zipcode_and_city;
 
-use utf8;
 use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
 
-my @queries = (
-  'ALTER TABLE contacts ADD COLUMN cp_street text;',
-  'ALTER TABLE contacts ADD COLUMN cp_zipcode text;',
-  'ALTER TABLE contacts ADD COLUMN cp_city text;',
-);
+  my @queries = (
+    'ALTER TABLE contacts ADD COLUMN cp_street text;',
+    'ALTER TABLE contacts ADD COLUMN cp_zipcode text;',
+    'ALTER TABLE contacts ADD COLUMN cp_city text;',
+  );
 
-foreach my $query (@queries) {
-  if ( $dbh->do($query) ) {
-    next;
-  }
+  $self->db_query($_, 1) for @queries;
 
-  $dbh->rollback();
-  $dbh->begin_work();
+  return 1;
 }
 
-return 1;
+1;
index 2f10670c459618a0556a404040b50bbbb5a304ef..bb45209e20a5bf12ab9801f66998c5d5d6fd9cbe 100644 (file)
@@ -1,23 +1,27 @@
 # @tag: contacts_convert_cp_birthday_to_date
 # @description: Umstellung cp_birthday von Freitext auf Datumsfeld
 # @depends: release_2_7_0
-package contacts_convert_cp_birthday_to_date;
+package SL::DBUpgrade2::contacts_convert_cp_birthday_to_date;
+
 use strict;
+use utf8;
 
-die 'This script cannot be run from the command line.' if !$::form;
+use parent qw(SL::DBUpgrade2::Base);
 
 sub convert_to_date {
-  my ($str) = @_;
+  my ($self, $str) = @_;
 
   return '' if !$str;
 
-  my $sth = $dbh->prepare('SELECT ?::date AS date') or return undef;
+  my $sth = $self->dbh->prepare('SELECT ?::date AS date') or return undef;
   $sth->execute($str)                               or return undef;
 
   return $sth->fetchrow_hashref->{date};
 }
 
-sub update {
+sub run {
+  my ($self) = @_;
+
   my @data      = ();
   my @auto_data = ();
   my $sql       = <<SQL;
@@ -30,13 +34,13 @@ sub update {
     ORDER BY cp_id;
 SQL
 
-  my $sth = $dbh->prepare($sql) or die $dbh->errstr;
-  $sth->execute or die $dbh->errstr;
+  my $sth = $self->dbh->prepare($sql) or die $self->dbh->errstr;
+  $sth->execute or die $self->dbh->errstr;
 
   my $i = -1;
   while (my $row = $sth->fetchrow_hashref) {
     $i++;
-    $row->{cp_birthday} = convert_to_date($::form->{form_submitted} ? $::form->{'cp_birthday_'. $i} : $row->{cp_birthday_old});
+    $row->{cp_birthday} = $self->convert_to_date($::form->{form_submitted} ? $::form->{'cp_birthday_'. $i} : $row->{cp_birthday_old});
     $row->{row_index}   = $i;
 
     if ( defined($row->{cp_birthday}) ) {
@@ -59,7 +63,7 @@ SQL
       ALTER TABLE contacts ADD COLUMN cp_birthday date;
 SQL
 
-    $dbh->do($sql);
+    $self->dbh->do($sql);
 
     $sql = <<SQL;
       UPDATE contacts
@@ -67,14 +71,14 @@ SQL
       WHERE cp_id = ?
 SQL
 
-    $sth = $dbh->prepare($sql) or die $dbh->errstr;
+    $sth = $self->dbh->prepare($sql) or die $self->dbh->errstr;
 
     foreach (grep { $_->{cp_birthday} ne '' } @auto_data) {
-      $sth->execute($_->{cp_birthday}, $_->{cp_id}) or die $dbh->errstr;
+      $sth->execute($_->{cp_birthday}, $_->{cp_id}) or die $self->dbh->errstr;
     }
 
     return 1;
   }
 }
 
-return update();
+1;
index 176516572fbe8064e3d1e3068434a74778eda1e0..cb2ed66cff9972f8e5b4af1c5d7ec4d1986dcc3c 100644 (file)
@@ -1,43 +1,30 @@
 # @tag: cp_greeting_migration
 # @description: Migration of cp_greeting to cp_gender
 # @depends: generic_translations
+package SL::DBUpgrade2::cp_greeting_migration;
 
 use strict;
+use utf8;
 
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
-
+use parent qw(SL::DBUpgrade2::Base);
 
 sub query_result {
+  my ($self) = @_;
 
   # list of all entries where cp_greeting is empty, meaning can't determine gender from parsing Herr/Frau/...
   # this assumes cp_greeting still exists, i.e. gender.sql was not run yet
   my ($gender_table, $mchecked, $fchecked);
 
   my $sql2 = "select cp_id,cp_givenname,cp_name,cp_title,cp_greeting from contacts where not (cp_greeting ILIKE '%frau%' OR cp_greeting ILIKE '%herr%' or cp_greeting ILIKE '%mrs.%' or cp_greeting ILIKE '%miss%') ";
-  my $sth2 = $dbh->prepare($sql2) or die $dbh->errstr();
-  $sth2->execute() or die $dbh->errstr();
+  my $sth2 = $self->dbh->prepare($sql2) or die $self->dbh->errstr();
+  $sth2->execute() or die $self->dbh->errstr();
 
   my $i = 1;
   $gender_table .= '<table border="1"><tr><th>cp_givenname</th><th>cp_name</th><th>cp_title</th><th>cp_greeting</th><th><translate>male/female</th></tr>';
   $gender_table .= "\n";
 
   while (my $row = $sth2->fetchrow_hashref()) {
-    if ($main::form->{"gender_$i"} eq "f" ) {
+    if ($::form->{"gender_$i"} eq "f" ) {
       $mchecked = "";
       $fchecked = "checked";
     } else {
@@ -52,14 +39,14 @@ sub query_result {
   $gender_table .= "<input type=hidden name=\"number_of_gender_entries\" value=\"$i\">";
   $gender_table .= "</table>";
 
-  $main::form->{gender_table} = $gender_table;
+  $::form->{gender_table} = $gender_table;
 
   my $title_table;
 
   my $sql3 = "select cp_id,cp_givenname,cp_name,cp_title,cp_greeting from contacts where not ( (cp_greeting ILIKE '%frau%' OR cp_greeting ILIKE '%herr%' or cp_greeting ILIKE '%mrs.%' or cp_greeting ILIKE '%miss%')) and not (cp_greeting like ''); ";
 
-  my $sth3 = $dbh->prepare($sql3) or die $dbh->errstr();
-  $sth3->execute() or die $dbh->errstr();
+  my $sth3 = $self->dbh->prepare($sql3) or die $self->dbh->errstr();
+  $sth3->execute() or die $self->dbh->errstr();
 
   $title_table = '<table border="1"><tr><th>cp_givenname</th><th>cp_name</th><th>cp_title</th><th>cp_greeting</th><th>cp_title new</th></tr>';
 
@@ -76,32 +63,38 @@ sub query_result {
 
   $title_table .= "<input type=hidden name=\"number_of_title_entries\" value=\"$j\">";
   $title_table .= "</table>";
-  $main::form->{title_table} = $title_table;
+  $::form->{title_table} = $title_table;
 }
 
 sub print_question {
-  query_result();
+  my ($self) = @_;
+
+  $self->query_result;
   # parse html form in /templates/webpages/dbupgrade/cp_greeting_update_form
-  print $main::form->parse_html_template("dbupgrade/cp_greeting_update_form");
+  print $::form->parse_html_template("dbupgrade/cp_greeting_update_form");
 }
 
 sub alter_schema_only {
+  my ($self) = @_;
+
   my $sqlcode = <<SQL;
     ALTER TABLE contacts ADD COLUMN cp_gender char(1);
     ALTER TABLE contacts DROP COLUMN cp_greeting;
 SQL
 
-  $dbh->do($sqlcode);
+  $self->dbh->do($sqlcode);
 }
 
-sub do_update {
+sub run {
+  my ($self) = @_;
+
   # main function
 
   # Do not ask the user anything if there are no entries in the
   # contacts table.
-  my ($data_exists) = $dbh->selectrow_array("SELECT * FROM contacts LIMIT 1");
+  my ($data_exists) = $self->dbh->selectrow_array("SELECT * FROM contacts LIMIT 1");
   if (!$data_exists) {
-    alter_schema_only();
+    $self->alter_schema_only;
     return 1;
   }
 
@@ -110,29 +103,30 @@ sub do_update {
   # without doing anything
 
   my $column_exists = 1;
-  if (!$dbh->do("SELECT cp_gender FROM contacts LIMIT 1")) {
-    $dbh->rollback();
-    $dbh->begin_work();
+  if (!$self->dbh->do("SELECT cp_gender FROM contacts LIMIT 1")) {
+    $self->dbh->rollback();
+    $self->dbh->begin_work();
     $column_exists = 0;
   }
   return 1 if $column_exists;
 
 
-  if (!$main::form->{do_migrate}) {
+  if (!$::form->{do_migrate}) {
     # case 1: first call of page
-    set_default_greetings();
-    print_question();
+    $self->set_default_greetings;
+    $self->print_question;
     return 2;
-  } else {
-    # case 2: submit button was pressed, hidden field do_migrate was set
-    migrate_data();
   }
 
+  # case 2: submit button was pressed, hidden field do_migrate was set
+  $self->migrate_data;
+
   return 1;
 
 }
 
 sub migrate_data {
+  my ($self) = @_;
 
   my $sqlcode = <<EOF
 ALTER TABLE contacts ADD COLUMN cp_gender char(1);
@@ -144,37 +138,38 @@ UPDATE contacts SET cp_gender = 'f'
 EOF
 ;
 
-  for (my $i = 1; $i <= $main::form->{number_of_gender_entries}; $i++ ) {
-    next unless $main::form->{"cp_id_$i"};
-    if ( $main::form->{"gender_$i"} eq "f" ) {
-      $sqlcode .= "UPDATE contacts SET cp_gender = \'f\' WHERE cp_id = $main::form->{\"cp_id_$i\"};\n";
+  for (my $i = 1; $i <= $::form->{number_of_gender_entries}; $i++ ) {
+    next unless $::form->{"cp_id_$i"};
+    if ( $::form->{"gender_$i"} eq "f" ) {
+      $sqlcode .= "UPDATE contacts SET cp_gender = \'f\' WHERE cp_id = $::form->{\"cp_id_$i\"};\n";
     }
   }
 
-  for (my $i = 1; $i <= $main::form->{number_of_title_entries}; $i++ ) {
-    next unless $main::form->{"cp_id_title_$i"} and $main::form->{"cp_id_$i"};
-    $sqlcode .= "UPDATE contacts SET cp_title = \'$main::form->{\"cp_name_$i\"}\' WHERE cp_id = $main::form->{\"cp_id_$i\"};\n";
+  for (my $i = 1; $i <= $::form->{number_of_title_entries}; $i++ ) {
+    next unless $::form->{"cp_id_title_$i"} and $::form->{"cp_id_$i"};
+    $sqlcode .= "UPDATE contacts SET cp_title = \'$::form->{\"cp_name_$i\"}\' WHERE cp_id = $::form->{\"cp_id_$i\"};\n";
   }
   $sqlcode .= "ALTER TABLE contacts DROP COLUMN cp_greeting;";
 
   # insert chosen default values
-  $sqlcode .= "INSERT INTO generic_translations (translation_type, translation) VALUES ('greetings::male','$main::form->{default_male}');";
-  $sqlcode .= "INSERT INTO generic_translations (translation_type, translation) VALUES ('greetings::female','$main::form->{default_female}');";
+  $sqlcode .= "INSERT INTO generic_translations (translation_type, translation) VALUES ('greetings::male','$::form->{default_male}');";
+  $sqlcode .= "INSERT INTO generic_translations (translation_type, translation) VALUES ('greetings::female','$::form->{default_female}');";
 
   my $query  = $sqlcode;
-  do_query($query);
+  $self->db_query($query);
 }
 
 sub set_default_greetings {
+  my ($self) = @_;
+
   # add html input boxes to template so user can specify default greetings
 
    my $default_male                            = "Herr";
    my $default_female                          = "Frau";
    my $default_greeting_text_male              = "<input type=\"text\" id=\"default_male\" name=\"default_male\" value=\"$default_male\"><br>";
    my $default_greeting_text_female            = "<input type=\"text\" id=\"default_female\" name=\"default_female\" value=\"$default_female\"><br>";
-   $main::form->{default_greeting_text_male}   = $default_greeting_text_male;
-   $main::form->{default_greeting_text_female} = $default_greeting_text_female;
+   $::form->{default_greeting_text_male}   = $default_greeting_text_male;
+   $::form->{default_greeting_text_female} = $default_greeting_text_female;
 }
 
-return do_update();
-
+1;
index 50772810b7f11ea5dc848794b241c1c88b973625..e213e622de6433be077133f0d469e3ac9655c200 100644 (file)
@@ -1,37 +1,22 @@
 # @tag: defaults_datev_check
 # @description: Einstellung für DATEV-Überprüfungen (datev_check) vom Config-File in die DB verlagern.
 # @depends: release_2_7_0
-# @charset: utf-8
+package SL::DBUpgrade2::defaults_datev_check;
 
 use utf8;
-use strict;
-
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
+use strict;
 
-sub do_update {
+sub run {
+  my ($self) = @_;
 
   # this query will fail if column already exist (new database)
-  do_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_sales_invoice boolean    DEFAULT true|, 1);
-  do_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_purchase_invoice boolean DEFAULT true|, 1);
-  do_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_ar_transaction boolean   DEFAULT true|, 1);
-  do_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_ap_transaction boolean   DEFAULT true|, 1);
-  do_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_gl_transaction boolean   DEFAULT true|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_sales_invoice boolean    DEFAULT true|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_purchase_invoice boolean DEFAULT true|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_ar_transaction boolean   DEFAULT true|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_ap_transaction boolean   DEFAULT true|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN datev_check_on_gl_transaction boolean   DEFAULT true|, 1);
 
   # check current configuration and set default variables accordingly, so that
   # kivitendo's behaviour isn't changed by this update
@@ -43,12 +28,10 @@ sub do_update {
     }
 
     my $update_column = "UPDATE defaults SET datev_$check = '$check_set';";
-    do_query($update_column);
+    $self->db_query($update_column);
   }
 
-
   return 1;
 }
 
-return do_update();
-
+1;
index 042976efedcbe3910e6edc152ab6909062393c4c..c638494f7168a8a02416bf0b2ea6509c48cbef6d 100644 (file)
@@ -1,33 +1,18 @@
 # @tag: defaults_posting_config
 # @description: Einstellung, ob und wann Zahlungen Ã¤nderbar sind, vom Config-File in die DB verlagern.
 # @depends: release_2_7_0
-# @charset: utf-8
+package SL::DBUpgrade2::defaults_posting_config;
 
-use utf8;
 use strict;
+use utf8;
 
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_update {
+sub run {
+  my ($self) = @_;
 
   # this query will fail if column already exist (new database)
-  do_query(qq|ALTER TABLE defaults ADD COLUMN payments_changeable integer NOT NULL DEFAULT 0|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN payments_changeable integer NOT NULL DEFAULT 0|, 1);
 
   # check current configuration and set default variables accordingly, so that
   # kivitendo behaviour isn't changed by this update
@@ -40,10 +25,9 @@ sub do_update {
   }
 
   my $update_column = "UPDATE defaults SET payments_changeable = '$payments_changeable';";
-  do_query($update_column);
+  $self->db_query($update_column);
 
   return 1;
 }
 
-return do_update();
-
+1;
index cb1966154cd85e4417f4390c000d9260c7f45e71..44f3cf96d6ed03556ea394e52c4fc0da2f35d5c2 100644 (file)
@@ -1,33 +1,18 @@
 # @tag: defaults_show_bestbefore
 # @description: Einstellung, ob Mindesthaltbarkeitsdatum angezeigt wird, vom Config-File in die DB verlagern.
 # @depends: release_2_7_0
-# @charset: utf-8
+package SL::DBUpgrade2::defaults_show_bestbefore;
 
-use utf8;
 use strict;
+use utf8;
 
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_update {
+sub run {
+  my ($self) = @_;
 
   # this query will fail if column already exist (new database)
-  do_query(qq|ALTER TABLE defaults ADD COLUMN show_bestbefore boolean DEFAULT false|, 1);
+  $self->db_query(qq|ALTER TABLE defaults ADD COLUMN show_bestbefore boolean DEFAULT false|, 1);
 
   # check current configuration and set default variables accordingly, so that
   # kivitendo behaviour isn't changed by this update
@@ -38,10 +23,9 @@ sub do_update {
   }
 
   my $update_column = "UPDATE defaults SET show_bestbefore = '$show_bestbefore';";
-  do_query($update_column);
+  $self->db_query($update_column);
 
   return 1;
 }
 
-return do_update();
-
+1;
index 7d997eff3a5be9187ce73b903ff73d51b00b2c8e..a7b9e6dc35a1342526d69046c270f1a67db8282c 100644 (file)
@@ -1,13 +1,18 @@
-#!/usr/bin/perl
 # @tag: emmvee_background_jobs_2
 # @description: Hintergrundjobs einrichten
 # @depends: emmvee_background_jobs
-# @charset: utf-8
+package SL::DBUpgrade2::emmvee_background_jobs_2;
 
 use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
 
 use SL::BackgroundJob::CleanBackgroundJobHistory;
 
-SL::BackgroundJob::CleanBackgroundJobHistory->create_job;
+sub run {
+  SL::BackgroundJob::CleanBackgroundJobHistory->create_job;
+  return 1;
+}
 
 1;
diff --git a/sql/Pg-upgrade2/erzeugnisnummern.pl b/sql/Pg-upgrade2/erzeugnisnummern.pl
new file mode 100644 (file)
index 0000000..81c0241
--- /dev/null
@@ -0,0 +1,58 @@
+# @tag: erzeugnisnummern
+# @description: Erzeugnisnummern und Artikelnummern sollen eindeutig sein.
+# @depends: release_3_0_0
+package SL::DBUpgrade2::erzeugnisnummern;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  if ( $::form->{'continued'} ) {
+    my $update_query;
+    foreach my $i (1 .. $::form->{rowcount}) {
+      $update_query = qq|UPDATE parts SET partnumber = '| . $::form->{"partnumber_$i"} . qq|' WHERE id = | . $::form->{"partid_$i"};
+      $self->db_query($update_query);
+      print FH $i;
+    }
+    $self->dbh->commit();
+  }
+
+  my $query = qq|SELECT id, partnumber, description, unit, notes, assembly, ean, inventory_accno_id
+                   FROM parts pa
+                   WHERE (SELECT COUNT(*)
+                          FROM parts p
+                          WHERE p.partnumber=pa.partnumber)
+                          > 1
+                   ORDER BY partnumber;|;
+
+  my $sth = $self->dbh->prepare($query);
+  $sth->execute || $::form->dberror($query);
+
+  $::form->{PARTS} = [];
+  while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
+    map {$ref->{$_} = $::locale->{iconv_utf8}->convert($ref->{$_})} keys %$ref;
+    push @{ $::form->{PARTS} }, $ref;
+  }
+
+  if ( scalar @{ $::form->{PARTS} } > 0 ) {
+    &print_error_message;
+    return 2;
+  }
+
+  $query = qq|ALTER TABLE parts ADD UNIQUE (partnumber)|;
+  $self->db_query($query);
+
+  $query = qq|ALTER TABLE defaults ADD assemblynumber TEXT|;
+  $self->db_query($query);
+  return 1;
+} # end run
+
+sub print_error_message {
+  print $::form->parse_html_template("dbupgrade/erzeugnisnummern");
+}
+
+1;
index d0b14dbfa74a652aa7cd809490de8a5d4240a6d9..2f5bc6046b338a3b98b020a5cece5970424b0059 100644 (file)
@@ -1,24 +1,17 @@
 # @tag: finanzamt_update_fa_bufa_nr_hamburg
-# @description: Aktualisiert die fa_bufa_nr für Hamburg
+# @description: Aktualisiert die fa_bufa_nr für Hamburg
 # @depends: release_2_7_0
-# @charset: utf-8
-package finanzamt_update_fa_bufa_nr_hamburg;
-use utf8;
+package SL::DBUpgrade2::finanzamt_update_fa_bufa_nr_hamburg;
+
 use strict;
+use utf8;
 
-if ( !$::form ) {
-  die('This script cannot be run from the command line.');
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub query {
-  my ($query) = @_;
+sub run {
+  my ($self) = @_;
 
-  if ( !$dbh->do($query) ) {
-    die($dbup_locale->text('Database update error:') .'<br>'. $query .'<br>'. $DBI::errstr);
-  }
-}
-
-my @data = (
+  my @data = (
     ['02', '41'],
     ['57', '42'],
     ['71', '43'],
@@ -33,14 +26,17 @@ my @data = (
     ['08', '51'],
   );
 
-foreach my $entry (@data) {
-  query('
+  foreach my $entry (@data) {
+    $self->db_query('
     UPDATE finanzamt
     SET
       fa_bufa_nr = \'22'. $entry->[1] .'\'
     WHERE
           fa_land_nr = \'2\'
-      AND fa_bufa_nr = \'22'. $entry->[0] .'\';');
+      AND fa_bufa_nr = \'22'. $entry->[0] .'\'');
+  }
+
+  return 1;
 }
 
-return 1;
+1;
index 4b1c962ebe18cba3aedc2a1e77af7cecf28b0f7d..f556ff23aa368e5e3a6c51fb79e1773a27ede14b 100644 (file)
@@ -1,28 +1,16 @@
 # @tag: fix_acc_trans_ap_taxkey_bug
-# @description: Korrektur falscher Steuerschlüssel in acc_trans bei Eingangsrechnungen
+# @description: Korrektur falscher Steuerschlüssel in acc_trans bei Eingangsrechnungen
 # @depends: release_2_6_0
+package SL::DBUpgrade2::fix_acc_trans_ap_taxkey_bug;
 
 use strict;
+use utf8;
 
-die "This script cannot be run from the command line." unless $::form;
+use parent qw(SL::DBUpgrade2::Base);
 
-sub mydberror {
-  my $msg = shift;
-  die $dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr;
-}
-
-sub do_query {
-  my $query    = shift;
-  my $may_fail = shift;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless $may_fail;
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   my $q_find = <<SQL;
     SELECT * FROM (
       SELECT
@@ -86,25 +74,23 @@ SQL
     WHERE acc_trans_id = ?
 SQL
 
-  my $h_find   = $dbh->prepare($q_find)   || mydberror($q_find);
-  my $h_change = $dbh->prepare($q_change) || mydberror($q_change);
+  my $h_find   = $self->dbh->prepare($q_find)   || $self->db_error($q_find);
+  my $h_change = $self->dbh->prepare($q_change) || $self->db_error($q_change);
 
-  $h_find->execute() || mydberror($q_find);
+  $h_find->execute() || $self->db_error($q_find);
 
   my $num_changed = 0;
 
   while (my $ref = $h_find->fetchrow_hashref()) {
     # $::lxdebug->dump(0, "ref", $ref);
-    $h_change->execute($ref->{wanted_taxkey}, $ref->{acc_trans_id}) || mydberror($q_change);
+    $h_change->execute($ref->{wanted_taxkey}, $ref->{acc_trans_id}) || $self->db_error($q_change);
     $num_changed++;
   }
 
   $h_find->finish();
   $h_change->finish();
 
-  print $dbup_locale->text('Number of entries changed: #1', $num_changed) . "<br/>\n";
+  print $::locale->text('Number of entries changed: #1', $num_changed) . "<br/>\n";
 }
 
-do_update();
-return 1;
-
+1;
index 608e44207a625aa2cc04a7e91e7588b2fd24c06a..df31c04e48e58ea003e23357826eaf0c6dbb4dea 100644 (file)
@@ -1,28 +1,16 @@
 # @tag: globalprojectnumber_ap_ar_oe
 # @description: Neue Spalte f&uuml;r eine globale Projektnummer in Einkaufs- und Verkaufsbelegen
 # @depends: release_2_4_1
+package SL::DBUpgrade2::globalprojectnumber_ap_ar_oe;
 
 use strict;
+use utf8;
 
-die("This script cannot be run from the command line.") unless ($main::form);
+use parent qw(SL::DBUpgrade2::Base);
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
+sub run {
+  my ($self) = @_;
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
-
-sub do_update {
   my @queries =
     ("ALTER TABLE ap ADD COLUMN globalproject_id integer;",
      "ALTER TABLE ap ADD FOREIGN KEY (globalproject_id) REFERENCES project (id);",
@@ -31,11 +19,10 @@ sub do_update {
      "ALTER TABLE oe ADD COLUMN globalproject_id integer;",
      "ALTER TABLE oe ADD FOREIGN KEY (globalproject_id) REFERENCES project (id);");
 
-  do_query("ALTER TABLE project ADD PRIMARY KEY (id);", 1);
-  map({ do_query($_, 0); } @queries);
+  $self->db_query("ALTER TABLE project ADD PRIMARY KEY (id);", 1);
+  map({ $self->db_query($_, 0); } @queries);
 
   return 1;
 }
 
-return do_update();
-
+1;
index 7db1fef84651d80c748642a5e11e3ced45ff5550..91b3a61eda15c8f07e3207a8998d810701e13503 100644 (file)
@@ -1,12 +1,18 @@
 # @tag: periodic_invoices_background_job
 # @description: Hintergrundjob zum Erzeugen wiederkehrender Rechnungen
 # @depends: periodic_invoices
-# @charset: utf-8
+package SL::DBUpgrade2::periodic_invoices_background_job;
 
 use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
 
 use SL::BackgroundJob::CreatePeriodicInvoices;
 
-SL::BackgroundJob::CreatePeriodicInvoices->create_job;
+sub run {
+  SL::BackgroundJob::CreatePeriodicInvoices->create_job;
+  return 1;
+}
 
 1;
index 1e9b00d08c4a987cc3130e3781910d5223ba6545..a3f0fd209e4965002390b62e286c291c431d0415 100644 (file)
@@ -1,36 +1,24 @@
 # @tag: rundungsfehler_korrigieren_BUG1328-2
 # @description: Die entsprechende Cent-Abweichung die durch den Rundungsfehler in Bug 1328 behoben wurde, entsprechende für alte Buchungen korrigieren.
 # @depends: release_2_6_0
-# @charset: utf-8
+package SL::DBUpgrade2::rundungsfehler_korrigieren_BUG1328_2;
 
-use utf8;
 use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
+use utf8;
 
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
+use parent qw(SL::DBUpgrade2::Base);
 
-sub do_query {
-  my ($query, $may_fail) = @_;
+use SL::DBUtils;
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   my @queries;
 
 
   my $query = qq|select distinct id,acamount from (select ap.id,ap.amount as apamount, ac.amount*-1 as acamount from ap left join acc_trans ac on (ac.trans_id =
 ap.id) where ac.chart_id IN (select id from chart where link ='AP' OR link like '%:AP' OR link like 'AP:%')) as foo where  apamount + 0.01 = abs(acamount)|;
-  my $sth_all_groups = prepare_execute_query($form, $dbh, $query);
+  my $sth_all_groups = prepare_execute_query($::form, $self->dbh, $query);
   while (my $hash_ref = $sth_all_groups->fetchrow_hashref()) {  # Schleife
       push @queries, "UPDATE ap set amount =" . $hash_ref->{acamount} . " WHERE id = " . $hash_ref->{id};
   }
@@ -39,9 +27,9 @@ ap.id) where ac.chart_id IN (select id from chart where link ='AP' OR link like
 
   my $query = qq|select distinct id,acamount from (select ar.id, ar.amount as aramount, ac.amount*-1 as acamount from ar left join acc_trans ac on (ac.trans_id =
 ar.id) where ac.chart_id IN (select id from chart where link ='AR' OR link like '%:AR' OR link like 'AR:%')) as foo where  aramount + 0.01 = abs(acamount)|;
-  my $sth_all_groups = prepare_execute_query($form, $dbh, $query);
+  my $sth_all_groups = prepare_execute_query($::form, $self->dbh, $query);
   while (my $hash_ref = $sth_all_groups->fetchrow_hashref()) {  # Schleife
-      # Falls wir keine alte buggy Installation haben, ist es super die 
+      # Falls wir keine alte buggy Installation haben, ist es super die
       # Gewissheit zu haben, dass kein acc_trans-Eintrag ohne trans_id vorhanden ist
       push @queries, "UPDATE ar set amount =" . $hash_ref->{acamount} . " WHERE id = " . $hash_ref->{id};
   }
@@ -49,13 +37,12 @@ ar.id) where ac.chart_id IN (select id from chart where link ='AR' OR link like
 
   # if in doubt use brute force ;-) jb
   foreach my $query (@queries){
-    my $sth   = prepare_query($form, $dbh, $query);
-    do_statement($form,$sth,$query);
+    my $sth   = prepare_query($::form, $self->dbh, $query);
+    do_statement($::form,$sth,$query);
     $sth->finish();
   }
-  $dbh ->commit();
+  $self->dbh ->commit();
   return 1;
 }
 
-return do_update();
-
+1;
index 796c6c96015507fe781cf56cb8215e48dd232bc3..468b79cc72b943452003b627ec6c6792c6446f1d 100644 (file)
@@ -1,12 +1,18 @@
 # @tag: self_test_background_job
 # @description: Hintergrundjob für tägliche Selbsttests
 # @depends: release_2_7_0
-# @charset: utf-8
+package SL::DBUpgrade2::self_test_background_job;
 
 use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
 
 use SL::BackgroundJob::SelfTest;
 
-SL::BackgroundJob::SelfTest->create_job;
+sub run {
+  SL::BackgroundJob::SelfTest->create_job;
+  return 1;
+}
 
 1;
diff --git a/sql/Pg-upgrade2/steuerfilterung.pl b/sql/Pg-upgrade2/steuerfilterung.pl
new file mode 100644 (file)
index 0000000..165a779
--- /dev/null
@@ -0,0 +1,56 @@
+# @tag: steuerfilterung
+# @description: Steuern in Dialogbuchungen filtern.
+# @depends: release_3_0_0
+package SL::DBUpgrade2::steuerfilterung;
+
+use strict;
+use utf8;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  if ( $::form->{'continued'} ) {
+    my $update_query = qq|ALTER TABLE tax ADD chart_categories TEXT|;
+    $self->db_query($update_query);
+    my $categories;
+    my $tax_id;
+    foreach my $i (1 .. $::form->{rowcount}) {
+      $tax_id = $::form->{"tax_id_$i"};
+      $categories = '';
+      $categories .= 'A' if $::form->{"asset_$i"};
+      $categories .= 'L' if $::form->{"liability_$i"};
+      $categories .= 'Q' if $::form->{"equity_$i"};
+      $categories .= 'C' if $::form->{"costs_$i"};
+      $categories .= 'I' if $::form->{"revenue_$i"};
+      $categories .= 'E' if $::form->{"expense_$i"};
+      $update_query = qq|UPDATE tax SET chart_categories = '$categories' WHERE id=$tax_id|;
+      $self->db_query($update_query);
+    }
+    $update_query = qq|ALTER TABLE tax ALTER COLUMN chart_categories SET NOT NULL|;
+    $self->db_query($update_query);
+    $self->dbh->commit();
+    return 1;
+  }
+
+  my $query = qq|SELECT taxkey, taxdescription, rate, id AS tax_id FROM tax order by taxkey, rate|;
+
+  my $sth = $self->dbh->prepare($query);
+  $sth->execute || $::form->dberror($query);
+
+  $::form->{PARTS} = [];
+  while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
+    $ref->{rate} = $::form->format_amount(\%::myconfig, $::form->round_amount($ref->{rate} * 100));
+    push @{ $::form->{PARTS} }, $ref;
+  }
+
+  &print_message;
+  return 2;
+} # end run
+
+sub print_message {
+  print $::form->parse_html_template("dbupgrade/steuerfilterung");
+}
+
+1;
index 70fe83d3594fa1aa686588a6135fbac2fcb32472..23d9b84b2c0fe4faa83bdaa15b55f39460fdfaec 100644 (file)
@@ -1,30 +1,16 @@
 # @tag: tax_constraints
 # @description: Setzt Fremdschlüssel und andere constraints auf die Tabellen tax und taxkeys
 # @depends: release_3_0_0 charts_without_taxkey
-# @charset: utf-8
+package SL::DBUpgrade2::tax_constraints;
 
-use utf8;
 use strict;
-use SL::Locale;
-
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") . "<br>$msg<br>" . $DBI::errstr);
-}
+use utf8;
 
-sub do_query {
-  my ($query, $may_fail) = @_;
+use parent qw(SL::DBUpgrade2::Base);
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+sub run {
+  my ($self) = @_;
 
-sub do_update {
   #CHECK CONSISTANCY OF tax
   #update tax.rate and tax.taxdescription in order to set later NOT NULL constraints
   my $query= <<SQL;
@@ -32,14 +18,14 @@ sub do_update {
     UPDATE tax SET taxdescription='-' WHERE COALESCE(taxdescription, '') = '';
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #check automatic tax accounts
   $query= <<SQL;
     SELECT count(*) FROM tax WHERE chart_id NOT IN (SELECT id FROM chart);
 SQL
 
-  my ($invalid_tax_account) = $dbh->selectrow_array($query);
+  my ($invalid_tax_account) = $self->dbh->selectrow_array($query);
 
   if ($invalid_tax_account > 0){
     #list all invalid tax accounts
@@ -51,16 +37,16 @@ SQL
       FROM tax WHERE chart_id NOT IN (SELECT id FROM chart);
 SQL
 
-    my $sth = $dbh->prepare($query);
-    $sth->execute || $main::form->dberror($query);
+    my $sth = $self->dbh->prepare($query);
+    $sth->execute || $::form->dberror($query);
 
-    $main::form->{TAX} = [];
+    $::form->{TAX} = [];
     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-      push @{ $main::form->{TAX} }, $ref;
+      push @{ $::form->{TAX} }, $ref;
     }
     $sth->finish;
 
-    $main::form->{invalid_tax_account} = 1;
+    $::form->{invalid_tax_account} = 1;
     print_error_message();
     return 0;
   }
@@ -70,7 +56,7 @@ SQL
     SELECT count(*) FROM tax WHERE taxkey IS NULL;
 SQL
 
-  my ($taxkey_is_null) = $dbh->selectrow_array($query);
+  my ($taxkey_is_null) = $self->dbh->selectrow_array($query);
 
   if ($taxkey_is_null > 0){
     #list all invalid tax accounts
@@ -84,16 +70,16 @@ SQL
       WHERE taxkey IS NULL;
 SQL
 
-    my $sth = $dbh->prepare($query);
-    $sth->execute || $main::form->dberror($query);
+    my $sth = $self->dbh->prepare($query);
+    $sth->execute || $::form->dberror($query);
 
-    $main::form->{TAX} = [];
+    $::form->{TAX} = [];
     while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-      push @{ $main::form->{TAX} }, $ref;
+      push @{ $::form->{TAX} }, $ref;
     }
     $sth->finish;
 
-    $main::form->{taxkey_is_null} = 1;
+    $::form->{taxkey_is_null} = 1;
     print_error_message();
     return 0;
   }
@@ -108,7 +94,7 @@ SQL
     OR startdate IS NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #There are 3 cases for taxkeys.tax_id and taxkeys.taxkey_id
   #taxkeys.taxkey_id is NULL and taxkeys.tax_id is not NULL:
@@ -123,7 +109,7 @@ SQL
     AND tax_id IS NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #taxkeys.taxkey_id and taxkeys.tax_id are NULL:
 
@@ -135,7 +121,7 @@ SQL
     AND tax_id IS NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Last case where taxkeys.taxkey_id is not null and taxkeys.tax_id is null
 
@@ -150,13 +136,13 @@ SQL
     AND tax_id IS NULL;
 SQL
 
-  my $sth = $dbh->prepare($query);
-  $sth->execute || $main::form->dberror($query);
+  my $sth = $self->dbh->prepare($query);
+  $sth->execute || $::form->dberror($query);
 
-  $main::form->{TAXID} = [];
+  $::form->{TAXID} = [];
   my $rowcount = 0;
   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-    push @{ $main::form->{TAXID} }, $ref;
+    push @{ $::form->{TAXID} }, $ref;
     $rowcount++;
   }
   $sth->finish;
@@ -166,22 +152,22 @@ SQL
   my $tax_id;
   for my $i (0 .. $rowcount-1){
     $query= qq|
-      SELECT id FROM tax WHERE rate = 0 and taxkey=| . $main::form->{TAXID}[$i]->{taxkey_id} . qq| LIMIT 1
+      SELECT id FROM tax WHERE rate = 0 and taxkey=| . $::form->{TAXID}[$i]->{taxkey_id} . qq| LIMIT 1
 |;
-    ($tax_id) = $dbh->selectrow_array($query);
+    ($tax_id) = $self->dbh->selectrow_array($query);
     if ( not $tax_id ){
       $insertquery=qq|
-        INSERT INTO tax (rate, taxdescription, taxkey) VALUES (0, '| . $::locale->text('0% tax with taxkey') . $main::form->{TAXID}[$i]->{taxkey_id} .  $::locale->text('. Automatically generated.') .
-        qq|', | . $main::form->{TAXID}[$i]->{taxkey_id} . qq|);
+        INSERT INTO tax (rate, taxdescription, taxkey) VALUES (0, '| . $::locale->text('0% tax with taxkey') . $::form->{TAXID}[$i]->{taxkey_id} .  $::locale->text('. Automatically generated.') .
+        qq|', | . $::form->{TAXID}[$i]->{taxkey_id} . qq|);
 |;
-      do_query($insertquery);
-      ($tax_id) = $dbh->selectrow_array($query);
-      $tax_id || $main::form->dberror($query);
+      $self->db_query($insertquery);
+      ($tax_id) = $self->dbh->selectrow_array($query);
+      $tax_id || $::form->dberror($query);
     }
     $updatequery = qq|
-      UPDATE taxkeys SET tax_id= | . $tax_id . qq| WHERE taxkey_id = | . $main::form->{TAXID}[$i]->{taxkey_id} . qq| AND tax_id IS NULL
+      UPDATE taxkeys SET tax_id= | . $tax_id . qq| WHERE taxkey_id = | . $::form->{TAXID}[$i]->{taxkey_id} . qq| AND tax_id IS NULL
 |;
-    do_query($updatequery);
+    $self->db_query($updatequery);
   }
 
   #The triple taxkey_id, chart_id, startdate in taxkeys has to be unique
@@ -195,13 +181,13 @@ SQL
            AND   tk2.startdate = tk1.startdate) > 1;
 SQL
 
-  $sth = $dbh->prepare($query);
-  $sth->execute || $main::form->dberror($query);
+  $sth = $self->dbh->prepare($query);
+  $sth->execute || $::form->dberror($query);
 
-  $main::form->{TAXKEYS} = [];
+  $::form->{TAXKEYS} = [];
   $rowcount = 0;
   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
-    push @{ $main::form->{TAXKEYS} }, $ref;
+    push @{ $::form->{TAXKEYS} }, $ref;
     $rowcount++;
   }
   $sth->finish;
@@ -215,12 +201,12 @@ SQL
             AND   tk2.startdate = tk1.startdate) > 1
       AND NOT tk1.id = (SELECT id
                         FROM taxkeys
-                        WHERE chart_id  = | . $main::form->{TAXKEYS}[$i]->{chart_id} . qq|
-                        AND   startdate = '| . $main::form->{TAXKEYS}[$i]->{startdate} . qq|'
+                        WHERE chart_id  = | . $::form->{TAXKEYS}[$i]->{chart_id} . qq|
+                        AND   startdate = '| . $::form->{TAXKEYS}[$i]->{startdate} . qq|'
                         LIMIT 1)
 |;
 
-    do_query($query);
+    $self->db_query($query);
   }
 
   #END CHECK OF taxkeys
@@ -233,28 +219,28 @@ SQL
     ALTER TABLE tax ALTER COLUMN rate SET DEFAULT 0;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for tax.description
   $query= <<SQL;
     ALTER TABLE tax ALTER COLUMN taxdescription SET NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create foreign key for tax.chart_id to chart.id
   $query= <<SQL;
     ALTER TABLE tax ADD FOREIGN KEY (chart_id) REFERENCES chart(id);
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for tax.taxkey
   $query= <<SQL;
     ALTER TABLE tax ALTER COLUMN taxkey SET NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for taxkey.chart_id and foreign key for taxkey.chart_id
   $query= <<SQL;
@@ -262,43 +248,43 @@ SQL
     ALTER TABLE taxkeys ADD FOREIGN KEY (chart_id) REFERENCES chart(id);
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for taxkey.startdate
   $query= <<SQL;
     ALTER TABLE taxkeys ALTER COLUMN startdate SET NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for taxkey.taxkey_id
   $query= <<SQL;
     ALTER TABLE taxkeys ALTER COLUMN taxkey_id SET NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #Create NOT NULL constraint for taxkey.tax_id
   $query= <<SQL;
     ALTER TABLE taxkeys ALTER COLUMN tax_id SET NOT NULL;
 SQL
 
-  do_query($query);
+  $self->db_query($query);
 
   #The triple chart_id, taxkey_id, startdate should be unique:
   $query= <<SQL;
     CREATE UNIQUE INDEX taxkeys_chartid_startdate ON taxkeys(chart_id, startdate);
 SQL
 
-  do_query($query);
+  $self->db_query($query);
   #ALL CONSTRAINTS WERE ADDED
 
   return 1;
-}; # end do_update
+} # end run
 
 
 sub print_error_message {
-  print $main::form->parse_html_template("dbupgrade/tax_constraints");
+  print $::form->parse_html_template("dbupgrade/tax_constraints");
 }
 
-return do_update();
+1;
index 438d2ee7f7071210fb62ec3ed23d6efd5333f7ad..0f7606065f5a7588e99eec783aa1c5fd02b35260 100644 (file)
@@ -1,33 +1,18 @@
 # @tag: umstellung_eur
 # @description: Variable eur umstellen: bitte in doc/dokumentation.pdf das entsprechende Kapitel zur Konfiguration von EUR lesen
 # @depends: release_2_6_3
-# @charset: utf-8
-
-# this script relies on $eur still being set in kivitendo.conf, and the
-# variable available in $::lx_office_conf{system}->{eur}
+package SL::DBUpgrade2::umstellung_eur;
 
+use strict;
 use utf8;
-#use strict;
-use Data::Dumper;
-die("This script cannot be run from the command line.") unless ($main::form);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
 
-sub do_query {
-  my ($query, $may_fail) = @_;
+use parent qw(SL::DBUpgrade2::Base);
 
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+# this script relies on $eur still being set in kivitendo.conf, and the
+# variable available in $::lx_office_conf{system}->{eur}
 
-sub do_update {
+sub run {
+  my ($self) = @_;
 
   # check if accounting_method has already been set (new database), if so return
   # only set variables according to eur for update of existing database
@@ -35,7 +20,7 @@ sub do_update {
 
   foreach my $column (qw(accounting_method inventory_system profit_determination)) {
     # this query will fail if columns already exist (new database)
-    do_query(qq|ALTER TABLE defaults ADD COLUMN ${column} TEXT|, 1);
+    $self->db_query(qq|ALTER TABLE defaults ADD COLUMN ${column} TEXT|, 1);
   }
 
   my $accounting_method;
@@ -65,10 +50,9 @@ sub do_update {
   my $update_eur = "UPDATE defaults set accounting_method = '$accounting_method' where accounting_method is null;" .
                    "UPDATE defaults set inventory_system = '$inventory_system' where inventory_system is null; " .
                    "UPDATE defaults set profit_determination = '$profit_determination' where profit_determination is null;";
-  do_query($update_eur);
+  $self->db_query($update_eur);
 
   return 1;
 }
 
-return do_update();
-
+1;
index a9b152f9e8bf2c4c652d817ea2ad0c491591da8c..3c9097176f233eec5bd25a63199cde30450fdbd9 100644 (file)
@@ -1,92 +1,25 @@
 # @tag: warehouse
 # @description:  Diverse neue Tabellen und Spalten zur Mehrlagerf&auml;higkeit inkl. Migration
 # @depends: release_2_4_3
+package SL::DBUpgrade2::warehouse;
 
 use strict;
+use utf8;
 
-die("This script cannot be run from the command line.") unless ($main::form);
-
-my $do_sql_migration = 0;
-my ($check_sql, $sqlcode);
-
-sub mydberror {
-  my ($msg) = @_;
-  die($dbup_locale->text("Database update error:") .
-      "<br>$msg<br>" . $DBI::errstr);
-}
-
-sub do_query {
-  my ($query, $may_fail) = @_;
-
-  if (!$dbh->do($query)) {
-    mydberror($query) unless ($may_fail);
-    $dbh->rollback();
-    $dbh->begin_work();
-  }
-}
+use parent qw(SL::DBUpgrade2::Base);
 
+use SL::DBUtils;
 
 sub print_question {
-  print $main::form->parse_html_template("dbupgrade/warehouse_form");
-}
-
-sub do_update {
-  if (!$main::form->{do_migrate}
-      && (selectfirst_array_query($main::form, $dbh, $check_sql))[0]) { # check if update is needed
-    print_question();
-    return 2;
-  } else {
-    if ($main::form->{do_migrate} eq 'Y') {
-      # if yes, both warehouse and bin must be given
-      if (!$main::form->{import_warehouse} || !$main::form->{bin_default}) {
-        print_question();
-        return 2;
-      }
-      # flag for extra code
-      $do_sql_migration = 1;
-    }
-  }
-  my $warehouse = $main::form->{import_warehouse} ne '' ? $main::form->{import_warehouse} : "Transfer";
-  my $bin       = $main::form->{bin_default}      ne '' ? $main::form->{bin_default}      : "1";
-
-  $warehouse    = $dbh->quote($warehouse);
-  $bin          = $dbh->quote($bin);
-
-  my $migration_code = <<EOF
-
--- Adjust warehouse
-INSERT INTO warehouse (description, sortkey, invalid) VALUES ($warehouse, 1, FALSE);
-
-UPDATE tmp_parts SET bin = NULL WHERE bin = '';
-
--- Restore old onhand
-INSERT INTO bin
- (warehouse_id, description)
- (SELECT DISTINCT warehouse.id, COALESCE(bin, $bin)
-   FROM warehouse, tmp_parts
-   WHERE warehouse.description=$warehouse);
-INSERT INTO inventory
- (warehouse_id, parts_id, bin_id, qty, employee_id, trans_id, trans_type_id, chargenumber)
- (SELECT warehouse.id, tmp_parts.id, bin.id, onhand, (SELECT id FROM employee LIMIT 1), nextval('id'), transfer_type.id, ''
-  FROM transfer_type, warehouse, tmp_parts, bin
-  WHERE warehouse.description = $warehouse
-    AND COALESCE(bin, $bin) = bin.description
-    AND transfer_type.description = 'stock');
-EOF
-;
-
-  # do standard code
-  my $query  = $sqlcode;
-     $query .= $migration_code if $do_sql_migration;
-
-  do_query($query);
-
-  return 1;
+  print $::form->parse_html_template("dbupgrade/warehouse_form");
 }
 
+sub run {
+  my ($self)           = @_;
 
-
-$sqlcode = <<EOF
+  my $do_sql_migration = 0;
+  my $check_sql        = qq|SELECT COUNT(id) FROM parts WHERE onhand > 0;|;
+  my $sqlcode          = <<SQL;
 -- Table "bin" for bins.
 CREATE TABLE bin (
   id integer NOT NULL DEFAULT nextval('id'),
@@ -200,13 +133,59 @@ END;
 CREATE TRIGGER trig_update_onhand
   AFTER INSERT OR UPDATE OR DELETE ON inventory
   FOR EACH ROW EXECUTE PROCEDURE update_onhand();
-EOF
-;
+SQL
+
+  if (!$::form->{do_migrate}
+      && (selectfirst_array_query($::form, $self->dbh, $check_sql))[0]) { # check if update is needed
+    print_question();
+    return 2;
+  } else {
+    if ($::form->{do_migrate} eq 'Y') {
+      # if yes, both warehouse and bin must be given
+      if (!$::form->{import_warehouse} || !$::form->{bin_default}) {
+        print_question();
+        return 2;
+      }
+      # flag for extra code
+      $do_sql_migration = 1;
+    }
+  }
+  my $warehouse = $::form->{import_warehouse} ne '' ? $::form->{import_warehouse} : "Transfer";
+  my $bin       = $::form->{bin_default}      ne '' ? $::form->{bin_default}      : "1";
+
+  $warehouse    = $self->dbh->quote($warehouse);
+  $bin          = $self->dbh->quote($bin);
+
+  my $migration_code = <<EOF
 
+-- Adjust warehouse
+INSERT INTO warehouse (description, sortkey, invalid) VALUES ($warehouse, 1, FALSE);
 
-$check_sql = <<EOF
-SELECT COUNT(id) FROM parts WHERE onhand > 0;
+UPDATE tmp_parts SET bin = NULL WHERE bin = '';
+
+-- Restore old onhand
+INSERT INTO bin
+ (warehouse_id, description)
+ (SELECT DISTINCT warehouse.id, COALESCE(bin, $bin)
+   FROM warehouse, tmp_parts
+   WHERE warehouse.description=$warehouse);
+INSERT INTO inventory
+ (warehouse_id, parts_id, bin_id, qty, employee_id, trans_id, trans_type_id, chargenumber)
+ (SELECT warehouse.id, tmp_parts.id, bin.id, onhand, (SELECT id FROM employee LIMIT 1), nextval('id'), transfer_type.id, ''
+  FROM transfer_type, warehouse, tmp_parts, bin
+  WHERE warehouse.description = $warehouse
+    AND COALESCE(bin, $bin) = bin.description
+    AND transfer_type.description = 'stock');
 EOF
 ;
 
-return do_update();
+  # do standard code
+  my $query  = $sqlcode;
+     $query .= $migration_code if $do_sql_migration;
+
+  $self->db_query($query);
+
+  return 1;
+}
+
+1;
index 93148f4d4e01949476f18f3755e444958c40a421..fbe5ea21b5e3f3a49332f5dc7005acac089f661f 100644 (file)
@@ -37,8 +37,8 @@ find(sub {
 
   $finder->find(\$text);
 
-  }, "."
-);
+  }, "./templates", "./doc",
+  );
 
 if (@fails) {
   ok(0, join "\n", @fails);
diff --git a/t/common.t b/t/common.t
new file mode 100644 (file)
index 0000000..b68cba2
--- /dev/null
@@ -0,0 +1,49 @@
+use strict;
+
+use Test::More;
+
+use lib 't';
+use Support::TestSetup;
+
+Support::TestSetup::login();
+
+use SL::Common;
+
+sub test_truncate {
+  is(Common::truncate('nothing to do', at => -1),  '...',           'truncation length < 0: at least 3');
+  is(Common::truncate('nothing to do', at => 0),   '...',           'truncation length = 0: at least 3');
+  is(Common::truncate('nothing to do', at => 1),   '...',           'truncation length = 1: at least 3');
+  is(Common::truncate('nothing to do', at => 2),   '...',           'truncation length = 2: at least 3');
+  is(Common::truncate('nothing to do', at => 3),   '...',           'truncation length = 3: at least 3');
+  is(Common::truncate('nothing to do', at => 4),   'n...',          'truncation length = 4');
+  is(Common::truncate('nothing to do', at => 9),   'nothin...',     'text length equal to truncation + 4');
+  is(Common::truncate('nothing to do', at => 10),  'nothing...',    'text length equal to truncation + 3');
+  is(Common::truncate('nothing to do', at => 11),  'nothing ...',   'text length equal to truncation + 2');
+  is(Common::truncate('nothing to do', at => 12),  'nothing t...',  'text length equal to truncation + 1');
+  is(Common::truncate('nothing to do', at => 13),  'nothing to do', 'text length equal to truncation');
+  is(Common::truncate('nothing to do', at => 14),  'nothing to do', 'text length equal to truncation - 1');
+  is(Common::truncate('nothing to do', at => 15),  'nothing to do', 'text length equal to truncation - 2');
+  is(Common::truncate('nothing to do', at => 16),  'nothing to do', 'text length equal to truncation - 3');
+  is(Common::truncate('nothing to do', at => 200), 'nothing to do', 'text length smaller than truncation');
+
+  is(Common::truncate('012345678901234567890123456789012345678901234567890123456789'), '01234567890123456789012345678901234567890123456...', 'default truncation length of 50');
+
+  # Test stripping
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 50, strip => 1), "nothing\n\rat\rall", 'strip = 1, at = 50');
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 13, strip => 1), "nothing\n\ra...",    'strip = 1, at = 13');
+
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 50, strip => 'full'), "nothing at all", 'strip = full, at = 50');
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 13, strip => 'full'), "nothing at...",  'strip = full, at = 13');
+
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 50, strip => 'newlines'), "nothing at all", 'strip = newlines, at = 50');
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 13, strip => 'newlines'), "nothing at...",  'strip = newlines, at = 13');
+
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 50, strip => 'newline'), "nothing at all", 'strip = newline, at = 50');
+  is(Common::truncate("nothing\n\rat\rall\n\n", at => 13, strip => 'newline'), "nothing at...",  'strip = newline, at = 13');
+}
+
+test_truncate();
+
+done_testing;
+
+1;
index 0f252bbe2ae779156cf9bce97c54a3cfb875011f..0a15078e003317a9db2b3febc94c87be24d55b09 100644 (file)
@@ -7,6 +7,8 @@ use lib 't';
 use Support::TestSetup;
 
 use SL::Presenter;
+use SL::Controller::Base;
+use SL::Layout::Javascript;
 
 no warnings 'uninitialized';
 
index 2e7b7086473ec4e53af30b7b607d79ed2d9ac344..3b5b570eaf17fa57d2d336ba2df21bc82c252d87 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 51;
+use Test::More;
 use Test::Exception;
 
 use strict;
@@ -9,9 +9,18 @@ use utf8;
 use Data::Dumper;
 use Support::TestSetup;
 
-use_ok 'SL::DB::RequirementSpec';
-use_ok 'SL::DB::RequirementSpecItem';
-use_ok 'SL::DB::RequirementSpecTextBlock';
+eval {
+  require 'SL::DB::RequirementSpec';
+  require 'SL::DB::RequirementSpecItem';
+  require 'SL::DB::RequirementSpecTextBlock';
+  1;
+} or my $skip = 'RequirementSpec is not available for this test';
+
+if ($skip) {
+  plan skip_all => $skip;
+} else {
+  plan tests => 48;
+}
 
 sub reset_state {
   "SL::DB::Manager::${_}"->delete_all(all => 1) for qw(RequirementSpecTextBlock RequirementSpecItem RequirementSpec);
diff --git a/t/helper/camelify.t b/t/helper/camelify.t
new file mode 100644 (file)
index 0000000..0b5f8e3
--- /dev/null
@@ -0,0 +1,16 @@
+use Test::More tests => 8;
+
+use strict;
+
+use lib 't';
+
+use SL::Util qw(camelify);
+
+is(camelify('hello'),                'Hello',             'hello');
+is(camelify('hello_world'),          'HelloWorld',        'hello_world');
+is(camelify('hello_world_'),         'HelloWorld_',       'hello_world_');
+is(camelify('charlie_the_unicorn'),  'CharlieTheUnicorn', 'charlie_the_unicorn');
+is(camelify('_charlie_the_unicorn'), 'CharlieTheUnicorn', '_charlie_the_unicorn');
+is(camelify('hello__world'),         'HelloWorld',        'hello__world');
+is(camelify('hELLO'),                'HELLO',             'hELLO');
+is(camelify('hellO_worlD'),          'HellOWorlD',        'hellO_worlD');
diff --git a/t/helper/hashify.t b/t/helper/hashify.t
new file mode 100644 (file)
index 0000000..cf45ef5
--- /dev/null
@@ -0,0 +1,84 @@
+use Test::More tests => 52;
+
+use strict;
+
+use lib 't';
+
+use_ok 'SL::Util';
+
+sub numtest {
+  my @result = SL::Util::_hashify(@_);
+  return scalar(@result);
+}
+
+sub memtest {
+  my $key    = shift;
+  my $keep   = $_[0];
+  my @result = SL::Util::_hashify(@_);
+  splice @result, 0, $keep;
+
+  return '<empty>'     if !@result;
+  return '<odd-sized>' if scalar(@result) % 2;
+
+  my %hash = @result;
+  return $hash{$key};
+}
+
+my $href = { 42 => 54, unicorn => 'charlie' };
+my %hash = ( 23 => 13, chunky  => 'bacon'   );
+
+is(numtest(0, $href), 4, 'case A1');
+is(numtest(0, %hash), 4, 'case A2');
+is(numtest(1, $href), 1, 'case A3');
+is(numtest(1, %hash), 4, 'case A4');
+is(numtest(2, $href), 1, 'case A5');
+is(numtest(2, %hash), 4, 'case A6');
+is(numtest(3, $href), 1, 'case A7');
+is(numtest(3, %hash), 4, 'case A8');
+is(numtest(4, $href), 1, 'case A9');
+is(numtest(4, %hash), 4, 'case A10');
+is(numtest(5, $href), 1, 'case A11');
+is(numtest(5, %hash), 4, 'case A12');
+
+is(numtest(0, 'dummy1', $href), 2, 'case B1');
+is(numtest(0, 'dummy1', %hash), 5, 'case B2');
+is(numtest(1, 'dummy1', $href), 5, 'case B3');
+is(numtest(1, 'dummy1', %hash), 5, 'case B4');
+is(numtest(2, 'dummy1', $href), 2, 'case B5');
+is(numtest(2, 'dummy1', %hash), 5, 'case B6');
+is(numtest(3, 'dummy1', $href), 2, 'case B7');
+is(numtest(3, 'dummy1', %hash), 5, 'case B8');
+is(numtest(4, 'dummy1', $href), 2, 'case B9');
+is(numtest(4, 'dummy1', %hash), 5, 'case B10');
+is(numtest(5, 'dummy1', $href), 2, 'case B11');
+is(numtest(5, 'dummy1', %hash), 5, 'case B12');
+
+is(numtest(0, 'dummy1', 'dummy2', $href), 3, 'case C1');
+is(numtest(0, 'dummy1', 'dummy2', %hash), 6, 'case C2');
+is(numtest(1, 'dummy1', 'dummy2', $href), 3, 'case C3');
+is(numtest(1, 'dummy1', 'dummy2', %hash), 6, 'case C4');
+is(numtest(2, 'dummy1', 'dummy2', $href), 6, 'case C5');
+is(numtest(2, 'dummy1', 'dummy2', %hash), 6, 'case C6');
+is(numtest(3, 'dummy1', 'dummy2', $href), 3, 'case C7');
+is(numtest(3, 'dummy1', 'dummy2', %hash), 6, 'case C8');
+is(numtest(4, 'dummy1', 'dummy2', $href), 3, 'case C9');
+is(numtest(4, 'dummy1', 'dummy2', %hash), 6, 'case C10');
+is(numtest(5, 'dummy1', 'dummy2', $href), 3, 'case C11');
+is(numtest(5, 'dummy1', 'dummy2', %hash), 6, 'case C12');
+
+is(memtest(42,        0, $href), '54',          'case D1');
+is(memtest(23,        0, %hash), '13',          'case D2');
+is(memtest('unicorn', 0, $href), 'charlie',     'case D3');
+is(memtest('chunky',  0, %hash), 'bacon',       'case D4');
+is(memtest(42,        1, $href), '<empty>',     'case D5');
+is(memtest(23,        1, %hash), '<odd-sized>', 'case D6');
+
+is(memtest(42,        0, 'dummy1', $href), undef,         'case E1');
+is(memtest(23,        0, 'dummy1', %hash), '<odd-sized>', 'case E2');
+is(memtest('unicorn', 0, 'dummy1', $href), undef,         'case E3');
+is(memtest(42,        1, 'dummy1', $href), '54',          'case E4');
+is(memtest(23,        1, 'dummy1', %hash), '13',          'case E5');
+is(memtest('unicorn', 1, 'dymmy1', $href), 'charlie',     'case E6');
+is(memtest('chunky',  1, 'dummy1', %hash), 'bacon',       'case E7');
+is(memtest(42,        2, 'dummy1', $href), '<empty>',     'case E8');
+is(memtest(23,        2, 'dummy1', %hash), '<odd-sized>', 'case E9');
diff --git a/t/helper/snakify.t b/t/helper/snakify.t
new file mode 100644 (file)
index 0000000..2a301bf
--- /dev/null
@@ -0,0 +1,15 @@
+use Test::More tests => 7;
+
+use strict;
+
+use lib 't';
+
+use SL::Util qw(snakify);
+
+is(snakify('Hello'),              'hello',                'Hello');
+is(snakify('HelloWorld'),         'hello_world',          'helloWorld');
+is(snakify('HelloWorld_'),        'hello_world_',         'helloWorld_');
+is(snakify('charlieTheUnicorn'),  'charlie_the_unicorn',  'charlieTheUnicorn');
+is(snakify('_CharlieTheUnicorn'), '_charlie_the_unicorn', '_CharlieTheUnicorn');
+is(snakify('HEllo'),              'h_ello',               'HEllo');
+is(snakify('HELlo'),              'h_e_llo',              'HELlo');
diff --git a/t/prefixed_number.t b/t/prefixed_number.t
new file mode 100644 (file)
index 0000000..1fc762f
--- /dev/null
@@ -0,0 +1,37 @@
+use Test::More tests => 14;
+use Test::Exception;
+
+use strict;
+
+use lib 't';
+use utf8;
+
+use Data::Dumper;
+use Support::TestSetup;
+
+use_ok 'SL::PrefixedNumber';
+
+sub n {
+  return SL::PrefixedNumber->new(number => $_[0]);
+}
+
+is(n('FB4711'     )->get_next, 'FB4712',      'increment FB4711');
+is(n('4711'       )->get_next, '4712',        'increment 4711');
+is(n('FB54UFB4711')->get_next, 'FB54UFB4712', 'increment FB54UFB4711');
+is(n('FB'         )->get_next, 'FB1',         'increment FB');
+is(n(''           )->get_next, '1',           'increment ""');
+is(n('0042-FB'    )->get_next, '0042-FB1',    'increment 0042-FB');
+my $o = n('0042-FB');
+$o->get_next;
+is($o->get_next,               '0042-FB2',    'increment 0042-FB twice');
+
+is(n('FB4711')->set_to(54), 'FB0054', 'set FB4711 to 54');
+$o = n('FB4711');
+$o->set_to(54);
+is($o->get_next,            'FB0055', 'set FB4711 to 54 then increment');
+
+is(n('FB121231')->get_current,                          'FB121231', 'set FB121231 get current');
+is(n('FB121231')->format(42),                           'FB000042', 'set FB121231 format 42');
+is(n('FB123123')->set_to_max('FB0711', 'FB911', 'FB8'), 'FB000911', 'set FB123123 max FB000911');
+
+throws_ok { n()->get_next } qr/no.*number/i, 'get_next without number set';
index 2780fec959847487645baedfb743cab3cf26f425..c8c91f60af6682a6ad97e249d3af7920bf03ba63 100644 (file)
     <tr>
      <th align="right" nowrap>[% 'Last RFQ Number' | $T8 %]</th>
      <td><input name="rfqnumber" size="10" value="[% HTML.escape(defaults_rfqnumber) %]"></td>
+     <th align="right" nowrap>[% 'Last Assembly Number' | $T8 %]</th>
+     <td><input name="assemblynumber" size="10" value="[% HTML.escape(defaults_assemblynumber) %]"></td>
     </tr>
 
     <tr>
      <th align="right" nowrap>[% 'Last Sales Delivery Order Number' | $T8 %]</th>
      <td><input name="sdonumber" size="10" value="[% HTML.escape(defaults_sdonumber) %]"></td>
+    </tr>
+
+    <tr>
      <th align="right" nowrap>[% 'Last Purchase Delivery Order Number' | $T8 %]</th>
      <td><input name="pdonumber" size="10" value="[% HTML.escape(defaults_pdonumber) %]"></td>
     </tr>
index e153d2f839bf85a1d619798681f244878e90e695..f7e7cf9e1d194b5f5388e2e75588968b908238a2 100644 (file)
@@ -1,5 +1,6 @@
 [%- USE T8 %]
 [%- USE HTML %]
+[%- USE L %]
  <form method="post" action="am.pl">
   <input type="hidden" name="id" value="[% HTML.escape(id) %]">
   <input type="hidden" name="type" value="tax">
@@ -9,7 +10,10 @@
   <table width="100%">
    <tr>
     <td>[% 'tax_taxkey' | $T8 %]</td>
-    <td><input name="taxkey" size="2" value="[% HTML.escape(taxkey) %]" [% readonly %]></td>
+    <td>[% IF tax_already_used %]<p>[% HTML.escape(taxkey) %]</p>
+        <input type="hidden" name="taxkey" size="2" value="[% HTML.escape(taxkey) %]">
+        [% ELSE %]<input name="taxkey" size="2" value="[% HTML.escape(taxkey) %]">
+        [% END %]</td>
    </tr>
 
    <tr>
 
    <tr>
     <td>[% 'tax_percent' | $T8 %]</td>
-    <td><input name="rate" size="10" value="[% HTML.escape(rate) %]" [% readonly %]> %</td>
+    <td>[% IF tax_already_used %]<p>[% HTML.escape(rate) %] %</p>
+        <input type="hidden" name="rate" size="10" value="[% HTML.escape(rate) %]">
+        [% ELSE %]<input name="rate" size="10" value="[% HTML.escape(rate) %]"> %
+        [% END %]</td>
    </tr>
 
    <tr>
     <td><select name="chart_id"><option value="0">[% 'None' | $T8 %]</option>[% FOREACH row = ACCOUNTS %]<option value="[% HTML.escape(row.id) %]" [% IF row.selected %]selected[% END %]>[% HTML.escape(row.taxaccount) %]</option>[% END %]</select></td>
    </tr>
 
+    <td>[% 'Account categories' | $T8 %]</td>
+    <td><table>
+          <colgroup>
+            <col width="10">
+            <col width="130">
+            <col width="10">
+            <col width="130">
+            <col width="10">
+            <col width="130">
+            <col width="10">
+            <col width="130">
+            <col width="10">
+            <col width="130">
+            <col width="10">
+            <col width="130">
+          </colgroup>
+          <tr>
+            <td align="right">[% IF asset %]
+                                [% L.checkbox_tag('asset', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('asset', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Asset' | $T8 %] (A)</td>
+            <td align="right">[% IF liability %]
+                                [% L.checkbox_tag('liability', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('liability', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Liability' | $T8 %] (L)</td>
+            <td align="right">[% IF equity %]
+                                [% L.checkbox_tag('equity', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('equity', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Equity' | $T8 %] (Q)</td>
+            <td align="right">[% IF revenue %]
+                                [% L.checkbox_tag('revenue', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('revenue', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Revenue' | $T8 %] (I)</td>
+            <td align="right">[% IF expense %]
+                                [% L.checkbox_tag('expense', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('expense', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Expense' | $T8 %] (E)</td>
+            <td align="right">[% IF costs %]
+                                [% L.checkbox_tag('costs', value => 1, checked => 1, class => 'checkbox') %]
+                              [% ELSE %]
+                                [% L.checkbox_tag('costs', value => 1, checked => 0, class => 'checkbox') %]
+                              [% END %]
+            </td>
+            <td align="left">[% 'Costs' | $T8 %] (C)</td>
+          </tr>
+        </table>
+     </td>
+   </tr>
+
+  </table>
   </table>
 
   [% UNLESS orphaned %]
index 29e0dc43dc15691e7b081acdba64dddbb794b91b..0bd5466cef594452eb2baef67cc3de1e31f8eb25 100644 (file)
   <p>[% saved_message | html %]</p>
 [% END %]
 
+<div class="tabwidget">
+ <ul>
+  <li><a href="#ui-tabs-basic-data">[% 'Basic Data' | $T8 %]</a></li>
+[%- IF id %]
+  <li><a href="controller.pl?action=RecordLinks/ajax_list&object_model=PurchaseInvoice&object_id=[% HTML.url(id) %]">[% 'Linked Records' | $T8 %]</a></li>
+[%- END %]
+ </ul>
+
+<div id="ui-tabs-basic-data">
 <table width="100%">
   <tr valign="top">
     <td>
       </table>
     </td>
   </tr>
-  <tr>
-    <td><hr size="3" noshade></td>
-  </tr>
 </table>
+</div>
+</div>
index af574a598a1a2d80fb51dccf260e914c6edf6d53..8d470faf4df9f40cf15d4407dbb44fc5d7bba3e5 100644 (file)
 [% L.hidden_tag('follow_up_trans_info_1', follow_up_trans_info) %]
 [% L.hidden_tag('follow_up_rowcount', 1) %]
 
+<h1>[% title | html %]</h1>
+
 [%- IF saved_message %]<p>[% saved_message | html  %]</p>[% END %]
 
+<div class="tabwidget">
+ <ul>
+  <li><a href="#ui-tabs-basic-data">[% 'Basic Data' | $T8 %]</a></li>
+[%- IF id %]
+  <li><a href="controller.pl?action=RecordLinks/ajax_list&object_model=Invoice&object_id=[% HTML.url(id) %]">[% 'Linked Records' | $T8 %]</a></li>
+[%- END %]
+ </ul>
+
+<div id="ui-tabs-basic-data">
 <table width=100%>
-  <tr class=listtop>
-    <th class=listtop>[% title | html %]</th>
-  </tr>
-  <tr height="5"></tr>
   <tr valign=top>
     <td>
       <table width=100%>
       </table>
     </td>
   </tr>
-  <tr>
-    <td><hr size=3 noshade></td>
-  </tr>
 </table>
+</div>
+</div>
index ae657475d6d504a01618bf247748c30a0aeddf56..b7e5dd4b66feb0ee7701dcaa7e7716a176aac181 100644 (file)
@@ -1,3 +1,4 @@
+[% PROCESS 'common/flash.html' %]
 <div id='csv_import_report'></div>
 
 <script type='text/javascript'>
diff --git a/templates/webpages/csv_import/_form_contacts.html b/templates/webpages/csv_import/_form_contacts.html
new file mode 100644 (file)
index 0000000..c3325b6
--- /dev/null
@@ -0,0 +1,8 @@
+[%- USE LxERP -%][%- USE L -%]
+<tr>
+ <th align="right">[%- LxERP.t8("Existing contacts (with column 'cp_id')") %]:</th>
+ <td colspan="10">
+  [% opts = [ [ 'update_existing', LxERP.t8('Update properties of existing entries') ], [ 'insert_new', LxERP.t8('Insert with new database ID') ], [ 'skip', LxERP.t8('Skip entry') ] ] %]
+  [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), style = 'width: 300px') %]
+ </td>
+</tr>
index dbb828031f0303d310250b853713c5bb0890d003..960ef5c51b9cb1117da7bbaa145cbf3ef38bb146 100644 (file)
@@ -8,3 +8,11 @@
   [% L.select_tag('settings.table', opts, default = SELF.profile.get('table'), style = 'width: 300px') %]
  </td>
 </tr>
+
+<tr>
+ <th align="right">[%- LxERP.t8('Existing customers/vendors with same customer/vendor number') %]:</th>
+ <td colspan="10">
+  [% opts = [ [ 'update_existing', LxERP.t8('Update properties of existing entries') ], [ 'insert_new', LxERP.t8('Insert with new customer/vendor number') ], [ 'skip', LxERP.t8('Skip entry') ] ] %]
+  [% L.select_tag('settings.update_policy', opts, default = SELF.profile.get('update_policy'), style = 'width: 300px') %]
+ </td>
+</tr>
index 26db240d76bde088524f844980beb6de081354b5..9fd30ac1d953bc09272b571b805ff64c16215239 100644 (file)
@@ -73,6 +73,7 @@
 
 [%- IF SELF.type == 'contacts' %]
    <p>
+    [%- LxERP.t8("You can update existing contacts by providing the 'cp_id' column with their database IDs. Otherwise: ") %]
     [%- LxERP.t8('At least one of the columns #1, customer, customernumber, vendor, vendornumber (depending on the target table) is required for matching the entry to an existing customer or vendor.', 'cp_cv_id') %]
    </p>
 
  [%- INCLUDE 'csv_import/_form_parts.html' %]
 [%- ELSIF SELF.type == 'customers_vendors' %]
  [%- INCLUDE 'csv_import/_form_customers_vendors.html' %]
+[%- ELSIF SELF.type == 'contacts' %]
+ [%- INCLUDE 'csv_import/_form_contacts.html' %]
 [%- END %]
 
    <tr>
index 7155b61d4154974fb8f34cfa3c592eda888f858a..a7d395e96334784e1043c865725b12b09486dc11 100644 (file)
       </td>
      </tr>
 
-     <tr>
-      <td colspan="2"><hr></td>
-     </tr>
-
-     <tr>
-      <th align="left" nowrap>[% 'Bcc' | $T8 %]</th>
-      <td><input name="bcc" size="40" value="[% HTML.escape(bcc) %]"></td>
-     </tr>
      [% IF CUSTOM_VARIABLES.Contacts.size %]
      <tr>
       <td colspan="2"><hr></td>
index e48152327d513697e31adeea30d47910dddea381..825a4b82e614e1fc4d5736da550a62ef394aea9b 100644 (file)
       <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>
       <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><input name="ustid" id="ustid" maxlength="14" size="20" value="[% HTML.escape(ustid) %]"></td>
+      <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><input name="c_vendor_id" size="10" value="[% HTML.escape(c_vendor_id) %]"></td>
+      <td>[% L.input_tag('c_vendor_id', c_vendor_id, size=30) %]</td>
       [%- ELSE %]
       <th align="right">[% 'Customer Number' | $T8 %]</th>
-      <td><input name="v_customer_id" size="10" value="[% HTML.escape(v_customer_id) %]"></td>
+      <td>[% L.input_tag('v_customer_id', v_customer_id, size=30) %]</td>
       [%- END %]
      </tr>
 
      <tr>
       <th align="right">[% 'Account Number' | $T8 %]</th>
-      <td><input name="account_number" size="10" maxlength="100" value="[% HTML.escape(account_number) %]"></td>
+      <td>[% L.input_tag('account_number', account_number, size=30) %]</td>
       <th align="right">[% 'Bank Code Number' | $T8 %]</th>
-      <td><input name="bank_code" size="10" maxlength="100" value="[% HTML.escape(bank_code) %]"></td>
+      <td>[% L.input_tag('bank_code', bank_code, size=30) %]</td>
       <th align="right">[% 'Bank' | $T8 %]</th>
-      <td><input name="bank" size="20" value="[% HTML.escape(bank) %]"></td>
+      <td>[% L.input_tag('bank', bank, size=30) %]</td>
      </tr>
 
      <tr>
       <th align="right">[% 'IBAN' | $T8 %]</th>
-      <td><input name="iban" size="10" maxlength="100" value="[% HTML.escape(iban) %]"></td>
+      <td>[% L.input_tag('iban', iban, maxlength=100, size=30) %]</td>
       <th align="right">[% 'BIC' | $T8 %]</th>
-      <td><input name="bic" size="10" maxlength="100" value="[% HTML.escape(bic) %]"></td>
+      <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, with_empty = 1) %]</td>
index fe26d47dee3f5eaa38a511afaf98992243c1f163..389328abe0d886d8aa6029323b33ca59a7f74021 100644 (file)
         <input name="l.cp_mobile" id="l_cp_mobile" type="checkbox" class="checkbox" value="Y" checked>
         <label for="l_cp_mobile">[% 'Mobile' | $T8 %]</label>
        </td>
-       <td>
-        <input name="l.cp_email" id="l_cp_email" type="checkbox" class="checkbox" value="Y" checked>
-        <label for="l_cp_email">[% 'E-mail' | $T8 %]</label>
-       </td>
+
+       <td>[%- L.checkbox_tag('l.cp_privatphone', value='Y', label=LxERP.t8('Private Phone'), class='checkbox', checked=1) %]</td>
+      </tr>
+
+      <tr>
+       <td>[%- L.checkbox_tag('l.cp_fax',         value='Y', label=LxERP.t8('Fax'),            class='checkbox') %]</td>
+       <td>[%- L.checkbox_tag('l.cp_email',       value='Y', label=LxERP.t8('E-mail'),         class='checkbox', checked=1) %]</td>
+       <td>[%- L.checkbox_tag('l.cp_privatemail', value='Y', label=LxERP.t8('Private E-mail'), class='checkbox') %]</td>
       </tr>
 
       <tr>
-       <td>
-        <input name="l.cp_birthday" id="l_cp_birthday" type="checkbox" class="checkbox" value="Y">
-        <label for="l_cp_birthday">[% 'Birthday' | $T8 %]</label>
-       </td>
        <td>
         <input name="l.cp_abteilung" id="l_cp_abteilung" type="checkbox" class="checkbox" value="Y">
         <label for="l_cp_abteilung">[% 'Department' | $T8 %]</label>
         <input name="l.cp_gender" id="l_cp_gender" type="checkbox" class="checkbox" value="Y">
         <label for="l_cp_gender">[% 'Gender' | $T8 %]</label>
        </td>
+
+       <td>
+        <input name="l.cp_birthday" id="l_cp_birthday" type="checkbox" class="checkbox" value="Y">
+        <label for="l_cp_birthday">[% 'Birthday' | $T8 %]</label>
+       </td>
       </tr>
 
       [% CUSTOM_VARIABLES_INCLUSION_CODE %]
diff --git a/templates/webpages/dbupgrade/erzeugnisnummern.html b/templates/webpages/dbupgrade/erzeugnisnummern.html
new file mode 100644 (file)
index 0000000..e5a7ec6
--- /dev/null
@@ -0,0 +1,41 @@
+[%- USE T8 %]
+[% USE HTML %]<div class="listtop">[% 'Double partnumbers' | $T8 %]</div>
+
+<form name="Form" method="post" action="login.pl">
+<input type="hidden" name="action" value="login">
+<input type="hidden" name="continued" value="1">
+
+<p>[% 'There are double partnumbers in your database.' | $T8 %]</p>
+<p>[% 'From this version on the partnumber of services, articles and assemblies have to be unique.' | $T8 %]</p>
+<p>[% 'So far you could use one partnumber for severel parts, for example a service and an article.' | $T8 %]</p>
+<p>[% 'Because the useability gets worth if one partnumber is used for several parts (for example if you are searching a position for an invoice), partnumbers should be unique.' | $T8 %]</p>
+
+<p>[% 'Please change the partnumber of the following parts and run the update again:' | $T8 %]</p>
+<table>
+  <tr>
+    <th class="listheading">[% 'Partnumber' | $T8 %]</th>
+    <th class="listheading">[% 'Description' | $T8 %]</th>
+    <th class="listheading">[% 'Unit' | $T8 %]</th>
+    <th class="listheading">[% 'Notes' | $T8 %]</th>
+    <th class="listheading">[% 'EAN' | $T8 %]</th>
+    <th class="listheading">[% 'Service, assembly or part' | $T8 %]</th>
+  </tr>
+
+  [% SET row_odd = '1' %][% FOREACH row = PARTS %]
+  <tr class="listrow[% IF row_odd %]1[% SET row_odd = '0' %][% ELSE %]0[% SET row_odd = '1' %][% END %]">
+    <td align="right"><input name='partnumber_[% loop.count %]' value='[% HTML.escape(row.partnumber) %]'></td>
+    <input type="hidden" name='partid_[% loop.count %]' value='[% HTML.escape(row.id) %]'>
+    <td align="left"> [% HTML.escape(row.description) %]</a></td>
+    <td align="right">[% HTML.escape(row.unit) %]</td>
+    <td align="right">[% HTML.escape(row.notes) %]</td>
+    <td align="right">[% HTML.escape(row.ean) %]</td>
+    <td align="right">[% IF row.assembly %] [% 'assembly' | $T8 %] [% ELSE %] [% IF row.inventory_accno_id %] [% 'part' | $T8 %] [% ELSE %] [% 'service' | $T8 %] [% END %] [% END %]</td>
+  </tr>
+  [% SET rowcount = loop.count %]
+  [% END %]
+  <input type="hidden" name="rowcount" value="[% rowcount %]">
+</table>
+
+<input type="submit" value="[% 'Continue' | $T8 %]">
+
+</form>
diff --git a/templates/webpages/dbupgrade/steuerfilterung.html b/templates/webpages/dbupgrade/steuerfilterung.html
new file mode 100644 (file)
index 0000000..da4ebf0
--- /dev/null
@@ -0,0 +1,78 @@
+
+[% USE T8 %]
+[% USE HTML %]
+[% USE L %]
+<div class="listtop">[% 'New filter for tax accounts' | $T8 %]</div>
+
+<form name="Form" method="post" action="login.pl">
+<input type="hidden" name="action" value="login">
+<input type="hidden" name="continued" value="1">
+
+<p>[% 'From this version on a new feature is available.' | $T8 %]</p>
+<p>[% 'You can choose account categories for taxes. Depending on these categories taxes will be displayed for transfers in the general ledger or not.' | $T8 %]</p>
+<p>[% '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.' | $T8 %]</p>
+<p>[% 'This feature especially prevents mistakes by mixing up prior tax and sales tax.' | $T8 %]</p>
+<p>[% 'Please choose for which categories the taxes should be displayed:' | $T8 %]</p>
+<table>
+  <tr>
+    <th class="listheading">[% 'Taxkey' | $T8 %]</th>
+    <th class="listheading">[% 'Description' | $T8 %]</th>
+    <th class="listheading">[% 'Tax rate' | $T8 %]</th>
+    <th class="listheading">[% 'Asset' | $T8 %] (A)</th>
+    <th class="listheading">[% 'Liability' | $T8 %] (L)</th>
+    <th class="listheading">[% 'Equity' | $T8 %] (Q)</th>
+    <th class="listheading">[% 'Costs' | $T8 %] (C)</th>
+    <th class="listheading">[% 'Revenue' | $T8 %] (I)</th>
+    <th class="listheading">[% 'Expense' | $T8 %] (E)</th>
+  </tr>
+
+  [% SET row_odd = '1' %][% FOREACH row = PARTS %]
+  <tr class="listrow[% IF row_odd %]1[% SET row_odd = '0' %][% ELSE %]0[% SET row_odd = '1' %][% END %]">
+    <td align="right">[% HTML.escape(row.taxkey) %]</td>
+    <td align="left"> [% HTML.escape(row.taxdescription) %]</a></td>
+    <td align="right">[% HTML.escape(row.rate) %] %</td>
+    <td align="center">[% IF row.taxkey == 0 or row.taxkey == 1 %]
+                     [% L.checkbox_tag('asset_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('asset_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% END %]</td>
+
+    <td align="center">[% IF row.taxkey == 0 or row.taxkey == 1 %]
+                     [% L.checkbox_tag('liability_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('liability_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% END %]</td>
+
+    <td align="center">[% IF row.taxkey == 0 or row.taxkey == 1 %]
+                     [% L.checkbox_tag('equity_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('equity_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% END %]</td>
+
+    <td align="center">[% IF row.taxkey == 0 or row.taxkey == 1 %]
+                     [% L.checkbox_tag('costs_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('costs_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% END %]</td>
+
+    <td align="center">[% IF row.taxkey == 8 or row.taxkey == 9 or row.taxkey == 18 or row.taxkey == 19%]
+                     [% L.checkbox_tag('revenue_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('revenue_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% END %]</td>
+
+    <td align="center">[% IF row.taxkey == 2 or row.taxkey == 3 or row.taxkey == 10 or row.taxkey == 11 or row.taxkey == 12 or row.taxkey == 13 %]
+                     [% L.checkbox_tag('expense_' _ loop.count, value => 1, checked => 0, class => 'checkbox') %]
+                     [% ELSE %]
+                     [% L.checkbox_tag('expense_' _ loop.count, value => 1, checked => 1, class => 'checkbox') %]
+                     [% END %]</td>
+  </tr>
+  <input type="hidden" name="tax_id_[% loop.count %]" value="[% row.tax_id %]">
+  [% SET rowcount = loop.count %]
+  [% END %]
+  <input type="hidden" name="rowcount" value="[% rowcount %]">
+</table>
+
+<input type="submit" value="[% 'Continue' | $T8 %]">
+
+</form>
index 728c80fc7709ba0aca8cf74f9b7ddac1f5e2fd22..d73380e3d1149f2d724a20129103782edf8b5f43 100644 (file)
@@ -4,22 +4,38 @@
 [%- USE L %]
 <script type="text/javascript">
   <!--
-  function setTaxkey(row) {
-    var accno  = document.getElementById('accno_' + row);
-    var taxkey = accno.options[accno.selectedIndex].value;
-    var reg = /--([0-9]*)/;
-    var found = reg.exec(taxkey);
-    var index = found[1];
-    index = parseInt(index);
-    var tax = 'taxchart_' + row;
-    for (var i = 0; i < document.getElementById(tax).options.length; ++i) {
-      var reg2 = new RegExp("^"+ index, "");
-      if (reg2.exec(document.getElementById(tax).options[i].value)) {
-        document.getElementById(tax).options[i].selected = true;
-        break;
-      }
+function updateTaxes(row)
+{
+  var accno  = document.getElementById('accno_' + row);
+  var taxkey = accno.options[accno.selectedIndex].value;
+  var reg = /--([0-9]*)/;
+  var found = reg.exec(taxkey);
+  var index = found[1];
+  index = parseInt(index);
+  var tax = 'taxchart_' + 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();
+};
 
   function copy_debit_to_credit() {
     var txt = document.getElementsByName('debit_1')[0].value;
index 3f1fd404d03760053ff7b04de5a35393ec491100..e7f1cd1b75d19f3b66448d1a96628c6ab93ef622 100644 (file)
@@ -42,7 +42,7 @@
         </tr>
  <tr>
     <th align=right>[% 'Employee' | $T8 %]</th>
-    <td colspan=3>[% L.select_tag('employee', ALL_EMPLOYEES, title_key = 'safe_name', with_empty = 1) %]</td>
+    <td colspan=3>[% L.select_tag('employee_id', ALL_EMPLOYEES, title_key = 'safe_name', with_empty = 1) %]</td>
   </tr>
   <tr>
     <th align=right>[% 'Filter date by' | $T8 %]</th>
diff --git a/templates/webpages/gl/update_tax_accounts.html b/templates/webpages/gl/update_tax_accounts.html
new file mode 100644 (file)
index 0000000..940d34d
--- /dev/null
@@ -0,0 +1,3 @@
+[% FOR row = TAX_ACCOUNTS %]
+<option value='[% row.id %]--[% row.rate %]' [% IF row.id == select_index %]selected[% END %]>[% row.taxdescription %] %</option>
+[% END %]
index 5a36172835a9edb0fb7e4ba82e0202a0c9277239..74fb123cc43397db80fd2a17e95e7f3946ed9a93 100644 (file)
@@ -25,7 +25,7 @@
      <td valign="top">
       [% L.radio_button_tag("periodicity", value => "m", label => LxERP.t8("monthly"),   checked => periodicity == 'm') %]
       <br>
-      [% L.radio_button_tag("periodicity", value => "q", label => LxERP.t8("quarterly"), checked => periodicity == 'q') %]
+      [% L.radio_button_tag("periodicity", value => "q", label => LxERP.t8("every third month"), checked => periodicity == 'q') %]
       <br>
       [% L.radio_button_tag("periodicity", value => "y", label => LxERP.t8("yearly"),    checked => periodicity == 'y') %]
      </td>
index 82c16f35c33de23f38f0694249bb8c763155e24b..9602388cfb086d71aa33cc6d4f5343ab7122169e 100644 (file)
 
  <script type="text/javascript">
   <!--
-$(function() {
-
-});
-
 function record_links_add() {
-  var url = "controller.pl?action=RecordLinks/ajax_add_filter&object_model=[% JavaScript.escape(object_model) %]&object_id=[% JavaScript.escape(object_id) %]&";
-  var id  = 'record_links_add';
-
-  $('#' + id).remove();
-  var div     = $('<div id="' + id + '" class="jqmWindow record_list_overlay"></div>').hide().appendTo('body');
-  var close   = $('<div class="close"></div>').appendTo(div);
-  var content = $('<div class="overlay_content"></div>').appendTo(div);
-  div.jqm({ modal: true });
-  div.jqmShow();
-  $.ajax({ url: url, success: function(new_html) { $(content).html(new_html); } });
-  $(close).click(function() {
-    div.jqmHide();
-    div.remove();
-  });
+  open_jqm_window({ url:  'controller.pl',
+                    data: { action: 'RecordLinks/ajax_add_filter',
+                            object_model: '[% JavaScript.escape(object_model) %]',
+                            object_id: '[% JavaScript.escape(object_id) %]'
+                          },
+                    id:  'record_links_add' });
+  return true;
 }
 
 function record_links_delete() {
index baa3ac6b01640937191ea392a986ab214974935e..0270d5bbc0bc4eab2f76d3d0e971f7132c9fb727 100644 (file)
@@ -90,7 +90,7 @@
               <tr>
                 <th align=right nowrap>[% 'Difference' | $T8 %]</th>
                 <td width=10%></td>
-                <td align=right><input name=null size=11 value="[% LxERP.format_amount(difference, 2, 0) %]"></td>
+                <td align=right><input name=null size=11 value="[% LxERP.format_amount(difference, 2, 0) %]" readonly></td>
                 <input type=hidden name=difference value="[% LxERP.format_amount(difference, 2, 0) %]">
               </tr>
             </table>
index 1ef213bf8720be9569bd6a672c4d068cd443de38..37d7a83478fff600711cf57b228617c282ca148f 100644 (file)
@@ -46,7 +46,7 @@
   [% L.button_tag('filter_record_links()', LxERP.t8("Search")) %]
   [% L.button_tag('add_selected_record_links()', LxERP.t8("Add links"), id='add_selected_record_links_button', disabled=1) %]
   <a href="#" onclick="record_links_reset_form();">[%- LxERP.t8("Reset") %]</a>
-  <a href="#" onclick="record_links_cancel();">[% LxERP.t8("Cancel") %]</a>
+  <a href="#" onclick="$('#record_links_add').jqmClose();">[% LxERP.t8("Cancel") %]</a>
  </p>
 
  <hr>
@@ -66,11 +66,6 @@ function record_links_reset_form() {
   $('.jqmWindow form select').prop('selectedIndex', 0);
 }
 
-function record_links_cancel() {
-  $('.jqmWindow').jqmHide();
-  $('.jqmWindow').remove();
-}
-
 function filter_record_links() {
   var url="controller.pl?action=RecordLinks/ajax_add_list&" + $(".jqmWindow form").serialize();
   $.ajax({
@@ -88,7 +83,7 @@ function add_selected_record_links() {
     url: url,
     success: function(new_html) {
       $('#record_links_list').replaceWith(new_html);
-      record_links_cancel();
+      $('#record_links_add').jqmClose();
     }
   });
 }
index e6e93240adf0cbfc9348340cb7dea4173961e829..675790db36056aa18e68837f149f11d3f135245e 100644 (file)
@@ -81,7 +81,7 @@
   [% FOREACH row = Q %]
   <tr>
     <td></td>
-    <td>[% row.description %]</td>
+    <td>[% row.accno _ ' - ' IF l_accno and row.accno %][% row.description %]</td>
     <td align="right">[% LxERP.format_amount(row.this, decimalplaces) %]</td>
     [%- IF last_period %]
     <td align="right">[% LxERP.format_amount(row.last, decimalplaces) %]</td>
index 753bc80b55d06adae11d66fd68f8790b4a6e76e8..daeaf46c9cf9c0bd58e36d687fd445babf148494 100644 (file)
   <tr>
     <th align=right>[% 'as at' | $T8 %]</th>
     <td> [% L.date_tag('asofdate', asofdate) %]</td>
+    <td><input name=l_cb class=checkbox type=checkbox value=Y>&nbsp;[% 'CB Transactions' | $T8 %]</td>
+    <td><input name=l_ob class=checkbox type=checkbox value=Y>&nbsp;[% 'only OB Transactions' | $T8 %]</td>
+  </tr>
+  </tr>
     <th align=right nowrap>[% 'Compare to' | $T8 %]</th>
     <td>[% L.date_tag('compareasofdate', compareasofdate) %]</td>
+    <td><input name=l_cb_compared class=checkbox type=checkbox value=Y>&nbsp;[% 'CB Transactions' | $T8 %]</td>
+    <td><input name=l_ob_compared class=checkbox type=checkbox value=Y>&nbsp;[% 'only OB Transactions' | $T8 %]</td>
   </tr>
   <tr>
     <th align=right>[% 'Decimalplaces' | $T8 %]</th>
     <input name=l_subtotal class=checkbox type=checkbox value=Y>&nbsp;[% 'Subtotal' | $T8 %]
     <input name=l_accno class=checkbox type=checkbox value=Y>&nbsp;[% 'Account Number' | $T8 %]</td>
   </tr>
-  <tr>
-    <th></th>
-    <td><input name=l_cb class=checkbox type=checkbox value=Y>&nbsp;[% 'CB Transactions' | $T8 %]
-    <input name=l_ob class=checkbox type=checkbox value=Y>&nbsp;[% 'only OB Transactions' | $T8 %]</td>
-  </tr>
 [%- END %]
 
 [%- IF is_trial_balance %]
index 4877046d7ef42900a7c3a353e3ffb480f99cbcda..e156b7ea111e957bbc996f542c908f1d97cbac57 100644 (file)
       <td align="right">[% LxERP.format_amount(invoice.invoice_amount, -2) %]</td>
       <td align="right">[% LxERP.format_amount(invoice.open_amount, -2) %]</td>
       <td align="right">[% invoice.duedate %]</td>
-      <td><input name="bank_transfers[].reference" value="[% HTML.escape(invoice.reference_prefix _ invoice.invnumber) %]"></td>
+      <td>
+       [%- SET reference = invoice.reference_prefix _ invoice.invnumber %]
+       <input name="bank_transfers[].reference" value="[% HTML.escape(reference.substr(0, 140)) %]" maxlength="140" size="60">
+      </td>
       <td align="right">
-       <input name="bank_transfers[].amount" value="[% LxERP.format_amount(invoice.invoice_amount, -2) %]" style="text-align: right">
+       <input name="bank_transfers[].amount" value="[% LxERP.format_amount(invoice.invoice_amount, -2) %]" style="text-align: right" size="12">
       </td>
      </tr>
     [%- END %]
index c20436fd7fabc6e1d288c67d558d58902a1b7bbd..3100dad434a6a2cb2e2a19908eeccf70cefdeef9 100644 (file)
@@ -56,8 +56,8 @@
       <input type="hidden" name="vc_bank_info[].name" value="[% HTML.escape(vbi.name) %]">
       [% HTML.escape(vbi.name) %]
      </td>
-     <td><input name="vc_bank_info[].iban" size="20" value="[% HTML.escape(vbi.iban) %]"></td>
-     <td><input name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic) %]"></td>
+     <td><input name="vc_bank_info[].iban" size="34" value="[% HTML.escape(vbi.iban.substr(0, 34)) %]" maxlength="34"></td>
+     <td><input name="vc_bank_info[].bic" size="20" value="[% HTML.escape(vbi.bic.substr(0, 20)) %]" maxlength="20"></td>
      <td><input name="vc_bank_info[].bank" size="30" value="[% HTML.escape(vbi.bank) %]"></td>
     </tr>
     [%- END %]
 
       <td align="right">[% LxERP.format_amount(bank_transfer.invoice_amount, -2) %]</td>
       <td align="right">[% LxERP.format_amount(bank_transfer.open_amount, -2) %]</td>
-      <td><input name="bank_transfers[].reference" value="[% HTML.escape(bank_transfer.reference) %]"></td>
-      <td align="right"><input name="bank_transfers[].amount" value="[% LxERP.format_amount(bank_transfer.amount, -2) %]" style="text-align: right"></td>
+      <td>
+       <input name="bank_transfers[].reference" value="[% HTML.escape(bank_transfer.reference.substr(0, 140)) %]" size="60" maxlength="140">
+      </td>
+      <td align="right"><input name="bank_transfers[].amount" value="[% LxERP.format_amount(bank_transfer.amount, -2) %]" style="text-align: right" size="12"></td>
       <td nowrap>
         [% L.date_tag('requested_execution_date_'_ loop.count, bank_transfer.requested_execution_date) %]
       </td>
   <input type="hidden" name="vc" value="[%- HTML.escape(vc) %]">
   <input type="hidden" name="confirmation" value="1">
  </form>
-
index ef9f46dd23969d596206b90d8f49d8ea604ac5e5..280e39055f022822bd88136b23989dd8cecec1a6 100644 (file)
@@ -9,6 +9,60 @@
 
 
   <table>
+
+    <tr>
+      <td align="right">[% 'Main sorting' | $T8 %]</td>
+      <td>
+        <select name="mainsort" id="mainsort">
+          <option value="description">[% 'Part' | $T8 %]</option>
+          <option value="customername">[% 'Customer' | $T8 %]</option>
+          <option value="country">[% 'Country' | $T8 %]</option>
+          <option value="partsgroup">[% 'Group' | $T8 %]</option>
+          <option value="business">[% 'Customer type' | $T8 %]</option>
+          <option value="salesman" selected="selected">[% 'Salesman' | $T8 %]</option>
+          <option value="month">[% 'Month' | $T8 %]</option>
+        </select>
+      </td>
+      <td align=left><input name="l_headers_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Heading' | $T8 %]</td>
+      <td align=left><input name="l_subtotal_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Subtotal' | $T8 %]</td>
+    </tr>
+
+    <tr>
+      <td align="right">[% 'Secondary sorting' | $T8 %]</td>
+      <td>
+        <select name="subsort" id="subsort">
+          <option value="description">[% 'Part' | $T8 %]</option>
+          <option value="customername">[% 'Customer' | $T8 %]</option>
+          <option value="country">[% 'Country' | $T8 %]</option>
+          <option value="partsgroup">[% 'Group' | $T8 %]</option>
+          <option value="business">[% 'Customer type' | $T8 %]</option>
+          <option value="salesman">[% 'Salesman' | $T8 %]</option>
+          <option value="month" selected="selected">[% 'Month' | $T8 %]</option>
+        </select>
+      </td>
+      <td align=left><input name="l_headers_subsort" class=checkbox type=checkbox value=Y checked> [% 'Heading' | $T8 %]</td>
+      <td align=left><input name="l_subtotal_subsort" class=checkbox type=checkbox value=Y checked> [% 'Subtotal' | $T8 %]</td>
+    </tr>
+
+    <tr>
+      <th align="right">[% 'Item mode' | $T8 %]</th>
+      <td colspan="3" align=left><input name="l_parts" class=checkbox type=checkbox value=Y> ([%'Show items from invoices individually' | $T8 %]) </td>
+    </tr>
+
+    <tr>
+      <th align="right">[% 'Total sum' | $T8 %]</th>
+      <td colspan="1" align=left><input name="l_total" class=checkbox type=checkbox value=Y checked></td>
+      <td align="right" nowrap>[% 'Decimalplaces' | $T8 %]: </td>
+      <td colspan="2"><input name="decimalplaces" size="2" value="2"></td>
+    </tr>
+
+    <tr>
+      <td></td>
+      <td colspan="7">
+        <hr size="1" noshade="">
+      </td>
+    <tr>
+
     <tr>
       <th align=right>[% 'Customer' | $T8 %]</th>
       <td>
       </td>
     </tr>
 
-
-
-    <tr>
-      <td></td>
-      <td colspan="7">
-        <hr size="1" noshade="">
-      </td>
-    <tr>
-
-
-
-    <tr>
-      <td align="right">[% 'Main sorting' | $T8 %]</td>
-      <td>
-        <select name="mainsort" id="mainsort">
-          <option value="description">[% 'Part' | $T8 %]</option>
-          <option value="customername">[% 'Customer' | $T8 %]</option>
-          <option value="country">[% 'Country' | $T8 %]</option>
-          <option value="partsgroup">[% 'Group' | $T8 %]</option>
-          <option value="business">[% 'Customer type' | $T8 %]</option>
-          <option value="salesman" selected="selected">[% 'Salesman' | $T8 %]</option>
-          <option value="month">[% 'Month' | $T8 %]</option>
-        </select>
-      </td>
-      <td align=left><input name="l_headers_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Heading' | $T8 %]</td>
-      <td align=left><input name="l_subtotal_mainsort" class=checkbox type=checkbox value=Y checked> [% 'Subtotal' | $T8 %]</td>
-    </tr>
-
-    <tr>
-      <td align="right">[% 'Secondary sorting' | $T8 %]</td>
-      <td>
-        <select name="subsort" id="subsort">
-          <option value="description">[% 'Part' | $T8 %]</option>
-          <option value="customername">[% 'Customer' | $T8 %]</option>
-          <option value="country">[% 'Country' | $T8 %]</option>
-          <option value="partsgroup">[% 'Group' | $T8 %]</option>
-          <option value="business">[% 'Customer type' | $T8 %]</option>
-          <option value="salesman">[% 'Salesman' | $T8 %]</option>
-          <option value="month" selected="selected">[% 'Month' | $T8 %]</option>
-        </select>
-      </td>
-      <td align=left><input name="l_headers_subsort" class=checkbox type=checkbox value=Y checked> [% 'Heading' | $T8 %]</td>
-      <td align=left><input name="l_subtotal_subsort" class=checkbox type=checkbox value=Y checked> [% 'Subtotal' | $T8 %]</td>
-    </tr>
-
-    <tr>
-      <th align="right">[% 'Item mode' | $T8 %]</th>
-      <td colspan="3" align=left><input name="l_parts" class=checkbox type=checkbox value=Y> ([%'Show items from invoices individually' | $T8 %]) </td>
-    </tr>
-
-    <tr>
-      <th align="right">[% 'Total sum' | $T8 %]</th>
-      <td colspan="1" align=left><input name="l_total" class=checkbox type=checkbox value=Y checked></td>
-      <td align="right" nowrap>[% 'Decimalplaces' | $T8 %]: </td>
-      <td colspan="2"><input name="decimalplaces" size="2" value="2"></td>
-    </tr>
   </table>
 
   <hr size="3" noshade="">