Merge branch 'master' of github.com:kivitendo/kivitendo-erp
authorJan Büren <jan@kivitendo-premium.de>
Thu, 16 Jul 2015 11:13:01 +0000 (13:13 +0200)
committerJan Büren <jan@kivitendo-premium.de>
Thu, 16 Jul 2015 11:13:01 +0000 (13:13 +0200)
24 files changed:
SL/Controller/Chart.pm
SL/Controller/Helper/GetModels.pm
SL/Controller/Helper/GetModels/Filtered.pm
SL/Controller/Helper/ParseFilter.pm
SL/DB/Helper/AttrDuration.pm
SL/DBConnect/Cache.pm
bin/mozilla/is.pl
bin/mozilla/oe.pl
js/common.js
t/controllers/helpers/parse_filter.t
t/db_helper/attr_duration.t
templates/print/RB/deutsch.tex
templates/print/RB/english.tex
templates/print/RB/letter.tex
templates/webpages/background_job_history/list.html
templates/webpages/bank_transactions/report_top.html
templates/webpages/delivery_plan/report_top.html
templates/webpages/do/search.html
templates/webpages/financial_controlling_report/report_top.html
templates/webpages/part/part_picker_search.html
templates/webpages/price_rule/report_top.html
templates/webpages/project/report_top.html
templates/webpages/reconciliation/tabs/overview.html
templates/webpages/requirement_spec/report_top.html

index 274f2ea..624a65b 100644 (file)
@@ -10,7 +10,7 @@ use SL::Locale::String qw(t8);
 use SL::JSON;
 
 use Rose::Object::MakeMethods::Generic (
-  'scalar --get_set_init' => [ qw(charts models chart) ],
+  'scalar --get_set_init' => [ qw(charts models chart filter) ],
 );
 
 sub action_ajax_autocomplete {
@@ -111,4 +111,6 @@ sub init_models {
   );
 }
 
+sub init_filter { $_[0]->models->filtered->laundered }
+
 1;
index 861834b..0e7f5bb 100644 (file)
@@ -11,7 +11,7 @@ use Scalar::Util qw(weaken);
 
 use Rose::Object::MakeMethods::Generic (
   scalar => [ qw(controller model query with_objects filtered sorted paginated finalized final_params) ],
-  'scalar --get_set_init' => [ qw(handlers source additional_url_params) ],
+  'scalar --get_set_init' => [ qw(handlers source list_action additional_url_params) ],
   array => [ qw(plugins) ],
 );
 
@@ -95,7 +95,9 @@ sub init {
 
   my @plugins;
   for my $plugin (qw(filtered sorted paginated)) {
-    next unless my $spec = delete $params{$plugin} // {};
+    next if exists($params{$plugin}) && !$params{$plugin};
+
+    my $spec         = delete $params{$plugin} // {};
     my $plugin_class = "SL::Controller::Helper::GetModels::" . ucfirst $plugin;
     push @plugins, $self->$plugin($plugin_class->new(%$spec, get_models => $self));
   }
@@ -160,7 +162,7 @@ sub get_models_url_params {
 sub get_callback_params {
   my ($self, %override_params) = @_;
 
-  my %default_params = $self->_run_handlers('callback', action => $self->controller->action_name);
+  my %default_params = $self->_run_handlers('callback', action => $self->list_action);
 }
 
 sub get_callback {
@@ -206,6 +208,10 @@ sub init_source {
   $::form
 }
 
+sub init_list_action {
+  $_[0]->controller->action_name
+}
+
 sub init_additional_url_params { +{} }
 
 1;
@@ -400,6 +406,13 @@ The creating controller. Currently this is mandatory.
 The name of the model for this GetModels instance. If none is given, the model
 is inferred from the name of the controller class.
 
+=item list_action ACTION
+
+If callbacks are generated, use this action instead of the current action.
+Usually you can omit this. In case the reporting is done without redirecting
+from a mutating action, this is necessary to have callbacks for paginating and
+sorting point to the correct action.
+
 =item sorted PARAMS
 
 =item paginated PARAMS
index 642d542..6325815 100644 (file)
@@ -8,8 +8,8 @@ use SL::Controller::Helper::ParseFilter ();
 use List::MoreUtils qw(uniq);
 
 use Rose::Object::MakeMethods::Generic (
-  scalar => [ qw(filter_args filter_params orig_filter filter) ],
-  'scalar --get_set_init' => [ qw(form_params launder_to) ],
+  scalar => [ qw(filter_args filter_params orig_filter filter no_launder) ],
+  'scalar --get_set_init' => [ qw(form_params laundered) ],
 );
 
 sub init {
@@ -39,27 +39,20 @@ sub read_params {
     class        => $self->get_models->manager,
     with_objects => $params{with_objects},
   );
-  my $laundered;
-  if ($self->launder_to eq '__INPLACE__') {
-    # nothing to do
-  } elsif ($self->launder_to) {
-    $laundered = {};
-    $parse_filter_args{launder_to} = $laundered;
+
+  # Store laundered result in $self->laundered.
+
+  if (!$self->no_launder) {
+    $self->laundered({});
+    $parse_filter_args{launder_to} = $self->laundered;
   } else {
+    $self->laundered(undef);
     $parse_filter_args{no_launder} = 1;
   }
 
   my %calculated_params = SL::Controller::Helper::ParseFilter::parse_filter($filter, %parse_filter_args);
   %calculated_params = $self->merge_args(\%calculated_params, \%filter_args, \%params);
 
-  if ($laundered) {
-    if ($self->get_models->controller->can($self->launder_to)) {
-      $self->get_models->controller->${\ $self->launder_to }($laundered);
-    } else {
-      $self->get_models->controller->{$self->launder_to} = $laundered;
-    }
-  }
-
   # $::lxdebug->dump(0, "get_current_filter_params: ", \%calculated_params);
 
   $self->filter_params(\%calculated_params);
@@ -107,10 +100,12 @@ sub init_form_params {
   'filter'
 }
 
-sub init_launder_to {
-  'filter'
-}
+sub init_laundered {
+  my ($self) = @_;
 
+  $self->get_models->finalize;
+  return $self->{laundered};
+}
 
 1;
 
@@ -132,7 +127,7 @@ In a controller:
     ...
     filtered => {
       filter      => HASHREF,
-      launder_to  => HASHREF | SUBNAME | '__INPLACE__',
+      no_launder  => 0 | 1,
     }
 
     OR
@@ -162,17 +157,9 @@ Optional. Indicates a key in C<source> to be used as filter.
 
 Defaults to the value C<filter> if missing.
 
-=item * C<launder_to>
-
-Optional. Indicates a target for laundered filter arguments in the controller.
-Can be set to C<undef> to disable laundering, and can be set to method named or
-hash keys of the controller. In the latter case the laundered structure will be
-put there.
+=item * C<no_launder>
 
-Defaults to the controller. Laundered values will end up in C<SELF.filter> for
-template purposes.
-
-Setting this to the special value C<__INPLACE__> will cause inplace laundering.
+Optional. If given and trueish then laundering is disabled.
 
 =back
 
@@ -185,6 +172,18 @@ See L<SL::Controller::Helper::ParseFilter> for a description of the filter forma
 C<Filtered> will honor custom filters defined in RDBO managers. See
 L<SL::DB::Helper::Filtered> for an explanation fo those.
 
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<laundered>
+
+Finalizes the object (which causes laundering of the filter structure)
+and returns a hashref of the laundered filter. If the plugin is
+configured not to launder then C<undef> will be returned.
+
+=back
+
 =head1 BUGS
 
 =over 4
index cdd3eb7..2ff9798 100644 (file)
@@ -12,6 +12,14 @@ use SL::MoreCommon qw(listify);
 use Data::Dumper;
 use Text::ParseWords;
 
+sub _lazy_bool_eq {
+  my ($key, $value) = @_;
+
+  return ()                                   if ($value // '') eq '';
+  return (or => [ $key => undef, $key => 0 ]) if !$value;
+  return ($key => 1);
+}
+
 my %filters = (
   date    => sub { DateTime->from_lxoffice($_[0]) },
   number  => sub { $::form->parse_amount(\%::myconfig, $_[0]) },
@@ -32,6 +40,10 @@ my %methods = (
   } qw(similar match imatch regex regexp like ilike rlike is is_not ne eq lt gt le ge),
 );
 
+my %complex_methods = (
+  lazy_bool_eq => \&_lazy_bool_eq,
+);
+
 sub parse_filter {
   my ($filter, %params) = @_;
 
@@ -99,31 +111,40 @@ sub _parse_filter {
 
   $flattened = _collapse_indirect_filters($flattened);
 
+  my $all_filters = { %filters,         %{ $params{filters}         || {} } };
+  my $all_methods = { %methods,         %{ $params{methods}         || {} } };
+  my $all_complex = { %complex_methods, %{ $params{complex_methods} || {} } };
+
   my @result;
   for (my $i = 0; $i < scalar @$flattened; $i += 2) {
+    my (@args, @filters, $method);
+
     my ($key, $value) = ($flattened->[$i], $flattened->[$i+1]);
     my ($type, $op)   = $key =~ m{:(.+)::(.+)};
 
-    if ($key =~ s/:multi//) {
-      my @multi;
-      my $orig_key = $key;
-      for my $value (parse_line('\s+', 0, $value)) {
-        ($key, $value) = _apply_all($key, $value, qr/\b:(\w+)/,  { %filters, %{ $params{filters} || {} } });
-        ($key, $value) = _apply_all($key, $value, qr/\b::(\w+)/, { %methods, %{ $params{methods} || {} } });
-        ($key, $value) = _dispatch_custom_filters($params{class}, $with_objects, $key, $value) if $params{class};
-        ($key, $value) = _apply_value_filters($key, $value, $type, $op);
-        push @multi, $key, $value;
-        $key = $orig_key;
-      }
-      ($key, $value) = (and => \@multi);
-    } else {
-      ($key, $value) = _apply_all($key, $value, qr/\b:(\w+)/,  { %filters, %{ $params{filters} || {} } });
-      ($key, $value) = _apply_all($key, $value, qr/\b::(\w+)/, { %methods, %{ $params{methods} || {} } });
-      ($key, $value) = _dispatch_custom_filters($params{class}, $with_objects, $key, $value) if $params{class};
-      ($key, $value) = _apply_value_filters($key, $value, $type, $op);
+    my $is_multi      = $key =~ s/:multi//;
+    my @value_tokens  = $is_multi ? parse_line('\s+', 0, $value) : ($value);
+
+    ($key, $method)   = split m{::}, $key, 2;
+    ($key, @filters)  = split m{:},  $key;
+
+    my $orig_key      = $key;
+
+    for my $value_token (@value_tokens) {
+      $key                 = $orig_key;
+
+      $value_token         = _apply($value_token, $_, $all_filters) for @filters;
+      $value_token         = _apply($value_token, $method, $all_methods)                                 if $method && exists $all_methods->{$method};
+      ($key, $value_token) = _apply_complex($key, $value_token, $method, $all_complex)                   if $method && exists $all_complex->{$method};
+      ($key, $value_token) = _dispatch_custom_filters($params{class}, $with_objects, $key, $value_token) if $params{class};
+      ($key, $value_token) = _apply_value_filters($key, $value_token, $type, $op);
+
+      push @args, $key, $value_token;
     }
 
-    push @result, $key, $value if defined $key;
+    next unless defined $key;
+
+    push @result, $is_multi ? (and => [ @args ]) : @args;
   }
   return \@result;
 }
@@ -239,14 +260,10 @@ sub _apply {
   return $filters->{$name}->($value);
 }
 
-sub _apply_all {
-  my ($key, $value, $re, $subs) = @_;
-
-  while ($key =~ s/$re//) {
-    $value = _apply($value, $1, $subs);
-  }
-
-  return $key, $value;
+sub _apply_complex {
+  my ($key, $value, $name, $filters) = @_;
+  return $key, $value unless $name && $filters->{$name};
+  return $filters->{$name}->($key, $value);
 }
 
 1;
@@ -448,6 +465,13 @@ standard SQL C<=> operator.
 
 All these are recognized like the L<Rose::DB::Object> methods.
 
+=item lazu_bool_eq
+
+If the value is undefined or an empty string then this parameter will
+be completely removed from the query. Otherwise a falsish filter value
+will match for C<NULL> and C<FALSE>; trueish values will only match
+C<TRUE>.
+
 =back
 
 =head1 BUGS AND CAVEATS
index 704c340..5bdd4bf 100644 (file)
@@ -3,7 +3,7 @@ package SL::DB::Helper::AttrDuration;
 use strict;
 
 use parent qw(Exporter);
-our @EXPORT = qw(attr_duration);
+our @EXPORT = qw(attr_duration attr_duration_minutes);
 
 use Carp;
 
@@ -13,6 +13,12 @@ sub attr_duration {
   _make($package, $_) for @attributes;
 }
 
+sub attr_duration_minutes {
+  my ($package, @attributes) = @_;
+
+  _make_minutes($package, $_) for @attributes;
+}
+
 sub _make {
   my ($package, $attribute) = @_;
 
@@ -75,6 +81,43 @@ sub _make {
   };
 }
 
+sub _make_minutes {
+  my ($package, $attribute) = @_;
+
+  no strict 'refs';
+
+  *{ $package . '::' . $attribute . '_as_hours' } = sub {
+    my ($self, $value) = @_;
+
+    $self->$attribute($value * 60 + ($self->$attribute % 60)) if @_ > 1;
+    return int(($self->$attribute // 0) / 60);
+  };
+
+  *{ $package . '::' . $attribute . '_as_minutes' } = sub {
+    my ($self, $value) = @_;
+
+    $self->$attribute(int($self->$attribute) - (int($self->$attribute) % 60) + ($value // 0)) if @_ > 1;
+    return ($self->$attribute // 0) % 60;
+  };
+
+  *{ $package . '::' . $attribute . '_as_duration_string' } = sub {
+    my ($self, $value) = @_;
+
+    if (@_ > 1) {
+      if (!defined($value) || ($value eq '')) {
+        $self->$attribute(undef);
+      } else {
+        croak $::locale->text("Invalid duration format") if $value !~ m{^(?:(\d*):)?(\d+)$};
+        $self->$attribute(($1 // 0) * 60 + ($2 // 0));
+      }
+    }
+
+    my $as_hours   = "${attribute}_as_hours";
+    my $as_minutes = "${attribute}_as_minutes";
+    return defined($self->$attribute) ? sprintf('%d:%02d', $self->$as_hours, $self->$as_minutes) : undef;
+  };
+}
+
 1;
 __END__
 
@@ -92,6 +135,7 @@ numeric columns
   # In a Rose model:
   use SL::DB::Helper::AttrDuration;
   __PACKAGE__->attr_duration('time_estimation');
+  __PACKAGE__->attr_duration_minutes('hours');
 
   # Read access:
   print "Minutes: " . $obj->time_estimation_as_minutes . " hours: " . $obj->time_estimation_as_hours . "\n";
@@ -104,11 +148,23 @@ numeric columns
 
 =head1 OVERVIEW
 
-This is a helper for columns that store a duration as a numeric or
-floating point number representing a number of hours. So the value
-1.75 would stand for "1 hour, 45 minutes".
+This is a helper for columns that store a duration in one of two formats:
 
-The helper methods created are:
+=over 2
+
+=item 1. as a numeric or floating point number representing a number
+of hours
+
+=item 2. as an integer presenting a number of minutes
+
+=back
+
+In the first case the value 1.75 would stand for "1 hour, 45
+minutes". In the second case the value 105 represents the same
+duration.
+
+The helper methods created depend on the mode. Calling
+C<attr_duration> makes the following methods available:
 
 =over 4
 
@@ -160,6 +216,26 @@ handles this case correctly.
 
 =back
 
+With C<attr_duration_minutes> the following methods are available:
+
+=over 4
+
+=item C<attribute_as_minutes [$new_value]>
+
+Access only the minutes. Return values are in the range [0 - 59].
+
+=item C<attribute_as_hours [$new_value]>
+
+Access only the hours. Returns an integer value.
+
+=item C<attribute_as_duration_string [$new_value]>
+
+Access the full value as a formatted string in the form C<h:mm>,
+e.g. C<1:30> for the value 90 minutes. Parsing such a string is
+supported, too.
+
+=back
+
 =head1 FUNCTIONS
 
 =over 4
@@ -169,6 +245,11 @@ handles this case correctly.
 Package method. Call with the names of attributes for which the helper
 methods should be created.
 
+=item C<attr_duration_minutes @attributes>
+
+Package method. Call with the names of attributes for which the helper
+methods should be created.
+
 =back
 
 =head1 BUGS
index 778785c..262c113 100644 (file)
@@ -52,7 +52,7 @@ sub _args2str {
     map { $_ => $options->{$_} }
     sort keys %$options;                  # deterministic order
 
-  join ';', apply { s/([;\\])/\\$1/g } $dbconnect, $dbuser, $dbpasswd, $options_str, $initial_sql;
+  join ';', apply { $_ //= ''; s/([;\\])/\\$1/g } $dbconnect, $dbuser, $dbpasswd, $options_str, $initial_sql;
 }
 
 1;
index e184cd8..dec398d 100644 (file)
@@ -320,6 +320,7 @@ sub form_header {
     ]);
 
   $TMPL_VAR{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
+  $form->{ALL_PROJECTS}            = $TMPL_VAR{ALL_PROJECTS}; # make projects available for second row drop-down in io.pl
   $TMPL_VAR{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
   $TMPL_VAR{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
   $TMPL_VAR{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
index c3242ec..1d3fe7f 100644 (file)
@@ -376,6 +376,7 @@ sub form_header {
     ]);
 
   $TMPL_VAR{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
+  $form->{ALL_PROJECTS}            = $TMPL_VAR{ALL_PROJECTS}; # make projects available for second row drop-down in io.pl
 
   # label subs
   my $employee_list_query_gen      = sub { $::form->{$_[0]} ? [ or => [ id => $::form->{$_[0]}, deleted => 0 ] ] : [ deleted => 0 ] };
index 3a85529..8ab2a5b 100644 (file)
@@ -194,6 +194,10 @@ $(document).ready(function () {
     if (focussable(this)) window.focused_element = this;
   });
 
+  // Lowest priority: first focussable element in form.
+  set_cursor_to_first_element();
+
+  // Medium priority: class set in template
   var initial_focus = $(".initial_focus").filter(':visible')[0];
   if (initial_focus)
     $(initial_focus).focus();
@@ -201,7 +205,6 @@ $(document).ready(function () {
   // legacy. sone forms install these
   if (typeof fokus == 'function') { fokus(); return; }
   if (focus_by_name('cursor_fokus')) return;
-  set_cursor_to_first_element();
 });
 
 $('form').submit(function(){
index aa8856a..2c10d03 100644 (file)
@@ -1,6 +1,6 @@
 use lib 't';
 
-use Test::More tests => 37;
+use Test::More tests => 38;
 use Test::Deep;
 use Data::Dumper;
 
@@ -405,3 +405,18 @@ test {
     'orderitems.part.test' => { 'what', { ilike => '%2%' } },
   ]
 }, 'relationship + additional tokens + filters + methods', class => 'SL::DB::Manager::Order';
+
+test {
+  part => {
+    'obsolete::lazy_bool_eq' => '0',
+  },
+}, {
+  query => [
+      or => [
+        'part.obsolete' => undef,
+        'part.obsolete' => 0
+      ],
+  ],
+  with_objects => [ 'part' ],
+}, 'complex methods modifying the key';
+
index da05a32..c2f2940 100644 (file)
@@ -4,16 +4,20 @@ use base qw(SL::DB::Object);
 
 __PACKAGE__->meta->setup(
   table   => 'dummy',
-  columns => [ dummy => { type => 'numeric', precision => 2, scale => 12 }, ]
+  columns => [
+    dummy => { type => 'numeric', precision => 2, scale => 12 },
+    inty  => { type => 'integer' },
+  ]
 );
 
 use SL::DB::Helper::AttrDuration;
 
 __PACKAGE__->attr_duration('dummy');
+__PACKAGE__->attr_duration_minutes('inty');
 
 package main;
 
-use Test::More tests => 91;
+use Test::More tests => 120;
 use Test::Exception;
 
 use strict;
@@ -31,6 +35,8 @@ sub new_item {
 Support::TestSetup::login();
 my $item;
 
+### attr_duration
+
 # Wenn das Attribut undef ist:
 is(new_item->dummy,                    undef,  'uninitialized: raw');
 is(new_item->dummy_as_hours,           0,      'uninitialized: as_hours');
@@ -165,4 +171,51 @@ lives_ok  { new_item()->dummy_as_man_days_unit('h')       } 'known unit h';
 lives_ok  { new_item()->dummy_as_man_days_unit('hour')    } 'known unit hour';
 lives_ok  { new_item()->dummy_as_man_days_unit('man_day') } 'known unit man_day';
 
+### attr_duration_minutes
+
+# Wenn das Attribut undef ist:
+is(new_item->inty,                    undef,  'uninitialized: raw');
+is(new_item->inty_as_hours,           0,      'uninitialized: as_hours');
+is(new_item->inty_as_minutes,         0,      'uninitialized: as_minutes');
+is(new_item->inty_as_duration_string, undef,  'uninitialized: as_duration_string');
+
+# Auslesen kleiner 60 Minuten:
+is(new_item(inty => 37)->inty,                    37,     'initialized < 60: raw');
+is(new_item(inty => 37)->inty_as_hours,           0,      'initialized < 60: as_hours');
+is(new_item(inty => 37)->inty_as_minutes,         37,     'initialized < 60: as_minutes');
+is(new_item(inty => 37)->inty_as_duration_string, '0:37', 'initialized < 60: as_duration_string');
+
+# Auslesen größer 60 Minuten:
+is(new_item(inty => 145)->inty,                    145,    'initialized > 60: raw');
+is(new_item(inty => 145)->inty_as_hours,           2,      'initialized > 60: as_hours');
+is(new_item(inty => 145)->inty_as_minutes,         25,     'initialized > 60: as_minutes');
+is(new_item(inty => 145)->inty_as_duration_string, '2:25', 'initialized > 60: as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string(undef);
+is($item->inty,                    undef, 'write as_duration_string undef read raw');
+is($item->inty_as_minutes,         0,     'write as_duration_string undef read as_minutes');
+is($item->inty_as_hours,           0,     'write as_duration_string undef read as_hours');
+is($item->inty_as_duration_string, undef, 'write as_duration_string undef read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string('');
+is($item->inty,                    undef, 'write as_duration_string "" read raw');
+is($item->inty_as_minutes,         0,     'write as_duration_string "" read as_minutes');
+is($item->inty_as_hours,           0,     'write as_duration_string "" read as_hours');
+is($item->inty_as_duration_string, undef, 'write as_duration_string "" read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string("3:21");
+is($item->inty,                    201,    'write as_duration_string 3:21 read raw');
+is($item->inty_as_minutes,         21,     'write as_duration_string 3:21 read as_minutes');
+is($item->inty_as_hours,           3,      'write as_duration_string 3:21 read as_hours');
+is($item->inty_as_duration_string, "3:21", 'write as_duration_string 3:21 read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string("03:1");
+is($item->inty,                    181,    'write as_duration_string 03:1 read raw');
+is($item->inty_as_minutes,         1,      'write as_duration_string 03:1 read as_minutes');
+is($item->inty_as_hours,           3,      'write as_duration_string 03:1 read as_hours');
+is($item->inty_as_duration_string, "3:01", 'write as_duration_string 03:1 read as_duration_string');
+
+# Parametervalidierung
+throws_ok { new_item()->inty_as_duration_string('invalid') } qr/invalid.*format/i, 'invalid duration format';
+
 done_testing();
index 7c85065..e53db19 100644 (file)
 
 % einkaufslieferschein (purchase_delivery_order)
 \newcommand{\einkaufslieferschein} {Eingangslieferschein}
+
+% Brief/letter
+\newcommand{\ihrzeichen}{Ihr Zeichen}
+\newcommand{\betreff}{Betreff}
index 2812389..8d393d5 100644 (file)
 
 % einkaufslieferschein (purchase_delivery_order)
 \newcommand{\einkaufslieferschein} {Purchase delivery order}
+
+% Brief/letter
+\newcommand{\ihrzeichen}{Your reference}
+\newcommand{\betreff}{Subject}
index b924152..f783eca 100644 (file)
@@ -26,7 +26,9 @@
 
   <%contact_formal%>
 
-  <%countrycode%> <%zipcode%> <%city%>
+  <%street%>
+
+  <%zipcode%> <%city%>
 
   <%country%>
 
 
 \vspace{2.5cm}
 \hfill<%date%>
-\textbf{<%reference%>}
 
-\vspace{1cm}
+<%if reference%>
+\textbf{\ihrzeichen : <%reference%>}
+<%end if%>
 
+\vspace{1cm}
 
-\textbf{<%subject%>}
+<%if subject%>
+\textbf{\betreff : <%subject%>}
+<%end if%>
 
 \vspace{1cm}
 
index 1a2b948..2f251e2 100644 (file)
@@ -4,7 +4,7 @@
 
 [%- INCLUDE 'common/flash.html' %]
 
-[%- PROCESS 'background_job_history/_filter.html' filter=SELF.filter %]
+[%- PROCESS 'background_job_history/_filter.html' filter=SELF.models.filtered.laundered %]
 
 [% IF !ENTRIES.size %]
  <p>
index f6fbce1..8b98dcb 100644 (file)
@@ -1,3 +1,3 @@
 [%- USE L %]
-[%- PROCESS 'bank_transactions/_filter.html' filter=SELF.filter %]
+[%- PROCESS 'bank_transactions/_filter.html' filter=SELF.models.filtered.laundered %]
  <hr>
index cc35146..01d0ff9 100644 (file)
@@ -1,3 +1,3 @@
 [%- USE L %]
-[%- PROCESS 'delivery_plan/_filter.html' filter=SELF.filter %]
+[%- PROCESS 'delivery_plan/_filter.html' filter=SELF.models.filtered.laundered %]
  <hr>
index baa00bb..a5ecf75 100644 (file)
 
  [%- SET vctypelabel = vc == 'customer' ? LxERP.t8('Customer type') : LxERP.t8('Vendor type') %]
 
- <script type="text/javascript">
-   $(function(){ document.Form.donumber.focus(); });
- </script>
-
  <style type="text/css">
   .fixed_width {
     width: 250px;
@@ -29,9 +25,9 @@
      <th align="right">[% IF is_customer %][% 'Customer' | $T8 %][% ELSE %][% 'Vendor' | $T8 %][% END %]</th>
      <td colspan="3">
       [%- UNLESS SHOW_VC_DROP_DOWN %]
-      <input type="text" name="[% HTML.escape(vc) %]" class="fixed_width">
+      <input type="text" name="[% HTML.escape(vc) %]" class="fixed_width initial_focus">
       [%- ELSE %]
-      <select name="[% vc %]" class="fixed_width">
+      <select name="[% vc %]" class="fixed_width initial_focus">
        <option></option>
        [%- FOREACH row = ALL_VC %]
        <option>[% HTML.escape(row.name) %]--[% HTML.escape(row.id) %]</option>
index 5fb399c..1312d75 100644 (file)
@@ -1,3 +1,3 @@
 [%- USE L %]
-[%- PROCESS 'financial_controlling_report/_filter.html' filter=SELF.filter %]
+[%- PROCESS 'financial_controlling_report/_filter.html' filter=SELF.models.filtered.laundered %]
  <hr>
index 4d625c7..5669d8e 100644 (file)
@@ -5,7 +5,7 @@
 
 <div style='overflow:hidden'>
 
-[% LxERP.t8("Filter") %]: [% L.input_tag('part_picker_filter', SELF.filter.all_substr_multi__ilike, class='part_picker_filter') %]
+[% LxERP.t8("Filter") %]: [% L.input_tag('part_picker_filter', SELF.models.filtered.laundered.all_substr_multi__ilike, class='part_picker_filter') %]
 [% L.hidden_tag('part_picker_real_id', FORM.real_id) %]
 
 <div class='float-right'>
index 0af23b4..a534a87 100644 (file)
@@ -1,4 +1,4 @@
 [%- USE L %]
 [%- PROCESS 'common/flash.html' %]
-[%- PROCESS 'price_rule/_filter.html' filter=SELF.filter UNLESS FORM.inline %]
+[%- PROCESS 'price_rule/_filter.html' filter=SELF.models.filtered.laundered UNLESS FORM.inline %]
  <hr>
index df03f9e..87c7efe 100644 (file)
@@ -9,7 +9,7 @@
 </div>
 <div class='filter_toggle' style='display:none'>
 <a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a>
-[%- PROCESS 'project/_filter.html' filter=SELF.filter %]
+[%- PROCESS 'project/_filter.html' filter=SELF.models.filtered.laundered %]
 
 [% L.hidden_tag('action', 'Project/dispatch') %]
 [% L.hidden_tag('sort_by', FORM.sort_by) %]
index 013ba54..18842b2 100644 (file)
@@ -33,7 +33,6 @@
           <td class="top_border"></td>
           <td class="top_border"></td>
           <td class="top_border"></td>
-          <td class="top_border"></td>
           <td class="bt_balance top_border" align="right">[% LxERP.format_amount(SELF.bt_balance, 2) %]</td>
           <td class="bb_balance top_border" align="right">[% LxERP.format_amount(-1 * SELF.bb_balance, 2) %]</td>
           <td class="top_border"></td>
@@ -41,6 +40,7 @@
           <td class="top_border"></td>
           <td class="top_border"></td>
           <td class="top_border"></td>
+          <td class="top_border"></td>
         </tr>
       </tfoot>
     </table>
index 507bfae..457e05e 100644 (file)
@@ -1,3 +1,3 @@
 [%- USE L %]
-[%- PROCESS "requirement_spec/_filter.html" filter=SELF.filter %]
+[%- PROCESS "requirement_spec/_filter.html" filter=SELF.models.filtered.laundered %]
  <hr>