Merge branch 'master' of github.com:kivitendo/kivitendo-erp
authorSven Schöling <s.schoeling@linet-services.de>
Fri, 12 Jul 2013 13:14:47 +0000 (15:14 +0200)
committerSven Schöling <s.schoeling@linet-services.de>
Fri, 12 Jul 2013 13:14:47 +0000 (15:14 +0200)
16 files changed:
SL/Controller/Helper/Filtered.pm
SL/Controller/Helper/Paginated.pm
SL/Controller/Helper/ParseFilter.pm
SL/Controller/Part.pm
SL/DB/Helper/PriceTaxCalculator.pm
SL/DB/Manager/Part.pm
SL/DB/Manager/Unit.pm
SL/DB/PaymentTerm.pm
SL/DBUpgrade2/Base.pm
SL/DO.pm
SL/Form.pm
SL/OE.pm
SL/Presenter/Part.pm
js/autocomplete_part.js
js/common.js
sql/Pg-upgrade2/oe_do_delete_via_trigger.pl [new file with mode: 0644]

index d248bf6..c5b876f 100644 (file)
@@ -12,19 +12,19 @@ use constant PRIV => '__filteredhelper_priv';
 my %controller_filter_spec;
 
 sub make_filtered {
-  my ($class, %specs)       = @_;
+  my ($class, %specs)             = @_;
 
-  $specs{MODEL}           //=  $class->controller_name;
-  $specs{MODEL}             =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
-  $specs{FORM_PARAMS}     //= 'filter';
-  $specs{LAUNDER_TO}        = '__INPLACE__' unless exists $specs{LAUNDER_TO};
-  $specs{ONLY}            //= [];
-  $specs{ONLY}              = [ $specs{ONLY} ] if !ref $specs{ONLY};
-  $specs{ONLY_MAP}          = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
+  $specs{MODEL}                 //=  $class->controller_name;
+  $specs{MODEL}                   =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
+  $specs{FORM_PARAMS}           //= 'filter';
+  $specs{LAUNDER_TO}              = '__INPLACE__' unless exists $specs{LAUNDER_TO};
+  $specs{ONLY}                  //= [];
+  $specs{ONLY}                    = [ $specs{ONLY} ] if !ref $specs{ONLY};
+  $specs{ONLY_MAP}                = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
 
   $controller_filter_spec{$class} = \%specs;
 
-  my %hook_params           = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
+  my %hook_params                 = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
   $class->run_before('_save_current_filter_params', %hook_params);
 
   SL::Controller::Helper::GetModels::register_get_models_handlers(
@@ -76,15 +76,15 @@ sub _make_current_filter_params {
 
   $calculated_params{query} = [
     @{ $calculated_params{query} || [] },
-    @{ $filter_args{query} || [] },
-    @{ $params{query} || [] },
+    @{ $filter_args{      query} || [] },
+    @{ $params{           query} || [] },
   ];
 
   $calculated_params{with_objects} = [
     uniq
     @{ $calculated_params{with_objects} || [] },
-    @{ $filter_args{with_objects} || [] },
-    @{ $params{with_objects} || [] },
+    @{ $filter_args{      with_objects} || [] },
+    @{ $params{           with_objects} || [] },
   ];
 
   if ($laundered) {
@@ -114,11 +114,11 @@ sub disable_filtering {
 sub _get_filter_args {
   my ($self, $spec) = @_;
 
-  $spec ||= $self->get_filter_spec;
+  $spec           ||= $self->get_filter_spec;
 
-  my %filter_args       = ref($spec->{FILTER_ARGS}) eq 'CODE' ? %{ $spec->{FILTER_ARGS}->($self) }
-                        :     $spec->{FILTER_ARGS}            ? do { my $sub = $spec->{FILTER_ARGS}; %{ $self->$sub() } }
-                        :                                       ();
+  my %filter_args   = ref($spec->{FILTER_ARGS}) eq 'CODE' ? %{ $spec->{FILTER_ARGS}->($self) }
+                    :     $spec->{FILTER_ARGS}            ? do { my $sub = $spec->{FILTER_ARGS}; %{ $self->$sub() } }
+                    :                                       ();
 }
 
 sub _save_current_filter_params {
@@ -137,9 +137,9 @@ sub _callback_handler_for_filtered {
   my $priv            = _priv($self);
 
   if (_is_enabled($self) && $priv->{filter}) {
-    my $filter_spec                             = $self->get_filter_spec;
+    my $filter_spec = $self->get_filter_spec;
     my ($flattened) = SL::Controller::Helper::ParseFilter::flatten($priv->{filter}, undef, $filter_spec->{FORM_PARAMS});
-    %params = (%params, @$flattened);
+    %params         = (%params, @$flattened);
   }
 
   # $::lxdebug->dump(0, "CB handler for filtered; params after flatten:", \%params);
@@ -172,7 +172,6 @@ sub _is_enabled {
   return !_priv($self)->{disabled} && ($self->get_filter_spec->{ONLY_MAP}->{$self->action_name} || $self->get_filter_spec->{ONLY_MAP}->{'__ALL__'});
 }
 
-
 1;
 
 __END__
index 25c4efa..0db285f 100644 (file)
@@ -12,20 +12,20 @@ use List::Util qw(min);
 my %controller_paginate_spec;
 
 sub make_paginated {
-  my ($class, %specs)       = @_;
+  my ($class, %specs)               = @_;
 
-  $specs{MODEL}           ||=  $class->controller_name;
-  $specs{MODEL}             =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
-  $specs{PER_PAGE}        ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
-  $specs{FORM_PARAMS}     ||= [ qw(page per_page) ];
-  $specs{PAGINATE_ARGS}   ||= '__FILTER__';
-  $specs{ONLY}            ||= [];
-  $specs{ONLY}              = [ $specs{ONLY} ] if !ref $specs{ONLY};
-  $specs{ONLY_MAP}          = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
+  $specs{MODEL}                   ||=  $class->controller_name;
+  $specs{MODEL}                     =~ s{ ^ SL::DB:: (?: .* :: )? }{}x;
+  $specs{PER_PAGE}                ||= "SL::DB::Manager::$specs{MODEL}"->default_objects_per_page;
+  $specs{FORM_PARAMS}             ||= [ qw(page per_page) ];
+  $specs{PAGINATE_ARGS}           ||= '__FILTER__';
+  $specs{ONLY}                    ||= [];
+  $specs{ONLY}                      = [ $specs{ONLY} ] if !ref $specs{ONLY};
+  $specs{ONLY_MAP}                  = @{ $specs{ONLY} } ? { map { ($_ => 1) } @{ $specs{ONLY} } } : { '__ALL__' => 1 };
 
   $controller_paginate_spec{$class} = \%specs;
 
-  my %hook_params           = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
+  my %hook_params                   = @{ $specs{ONLY} } ? ( only => $specs{ONLY} ) : ();
   $class->run_before('_save_current_paginate_params', %hook_params);
 
   SL::Controller::Helper::GetModels::register_get_models_handlers(
index 8b26e88..1d0515c 100644 (file)
@@ -80,7 +80,7 @@ sub flatten {
     next if !defined $value || $value eq ''; # 0 is fine
     if ('HASH' eq ref $value) {
       my ($query, $more_objects) = flatten($value, _prefix($prefix, $key));
-      push @result,        @$query if $query;
+      push @result, @$query        if  $query;
       _add_uniq($with_objects, $_) for _prefix($prefix, $key), @$more_objects;
     } else {
       push @result, _prefix($prefix, $key) => $value;
index 9b4744e..38b3b55 100644 (file)
@@ -21,20 +21,20 @@ use Rose::Object::MakeMethods::Generic (
 __PACKAGE__->run_before(sub { $::auth->assert('part_service_assembly_edit') });
 
 __PACKAGE__->make_filtered(
-  ONLY          => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
-  LAUNDER_TO    => 'filter',
+  ONLY        => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
+  LAUNDER_TO  => 'filter',
 );
 __PACKAGE__->make_paginated(
-  ONLY          => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
+  ONLY        => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
 );
 
 __PACKAGE__->make_sorted(
-  ONLY              => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
+  ONLY        => [ qw(part_picker_search part_picker_result ajax_autocomplete) ],
 
-  DEFAULT_BY        => 'partnumber',
-  DEFAULT_DIR       => 1,
+  DEFAULT_BY  => 'partnumber',
+  DEFAULT_DIR => 1,
 
-  partnumber        => t8('Partnumber'),
+  partnumber  => t8('Partnumber'),
 );
 
 sub action_ajax_autocomplete {
@@ -92,7 +92,7 @@ sub action_part_picker_result {
 }
 
 sub init_parts {
-  $_[0]->get_models;
+  $_[0]->get_models(with_objects => [ qw(unit_obj) ]);
 }
 
 1;
index e4a9180..af56646 100644 (file)
@@ -90,13 +90,14 @@ sub _calculate_item {
     $item->marge_percent(0);
 
   } else {
-    my $lastcost = ! ($item->lastcost * 1) ? ($item->part->lastcost || 0) : $item->lastcost;
+    my $lastcost       = ! ($item->lastcost * 1) ? ($item->part->lastcost || 0) : $item->lastcost;
+    my $linetotal_cost = _round($lastcost * $item->qty / $item->marge_price_factor, 2);
 
-    $item->marge_total(  $linetotal - $lastcost / $item->marge_price_factor);
+    $item->marge_total(  $linetotal - $linetotal_cost);
     $item->marge_percent($item->marge_total * 100 / $linetotal);
 
     $self->marge_total(  $self->marge_total + $item->marge_total);
-    $data->{lastcost_total} += $lastcost;
+    $data->{lastcost_total} += $linetotal_cost;
   }
 
   my $taxkey     = $item->part->get_taxkey(date => $self->transdate, is_sales => $data->{is_sales}, taxzone => $self->taxzone_id);
index 159f599..0b751bd 100644 (file)
@@ -35,29 +35,28 @@ sub type_filter {
 
   # this is to make selection like type => { part => 1, service => 1 } work
   if ('HASH' eq ref $type) {
-    $type = grep { $type->{$_} } keys %$type;
+    $type = [ grep { $type->{$_} } keys %$type ];
   }
 
-  my @types = listify($type);
+  my @types = grep { $_ } listify($type);
   my @filter;
 
   for my $type (@types) {
     if ($type =~ m/^part/) {
-      push @filter, (and => [ or                    => [ $prefix . assembly => 0, $prefix . assembly => undef ],
-                       "!${prefix}inventory_accno_id" => 0,
-                       "!${prefix}inventory_accno_id" => undef,
+      push @filter, (and => [ or                             => [ $prefix . assembly => 0, $prefix . assembly => undef ],
+                              "!${prefix}inventory_accno_id" => 0,
+                              "!${prefix}inventory_accno_id" => undef,
                      ]);
     } elsif ($type =~ m/^service/) {
       push @filter, (and => [ or => [ $prefix . assembly           => 0, $prefix . assembly           => undef ],
-                       or => [ $prefix . inventory_accno_id => 0, $prefix . inventory_accno_id => undef ],
+                              or => [ $prefix . inventory_accno_id => 0, $prefix . inventory_accno_id => undef ],
                      ]);
     } elsif ($type =~ m/^assembl/) {
       push @filter, ($prefix . assembly => 1);
     }
   }
 
-  return @filter > 2 ? (or => \@filter) :
-         @filter     ? @filter          : ();
+  return @filter > 2 ? (or => \@filter) : @filter;
 }
 
 sub get_ordered_qty {
index 8b3c138..f081af4 100644 (file)
@@ -6,10 +6,17 @@ use SL::DB::Helper::Manager;
 use base qw(SL::DB::Helper::Manager);
 
 use SL::DB::Helper::Sorted;
+use SL::DB::Helper::Filtered;
 
 sub object_class { 'SL::DB::Unit' }
 
 __PACKAGE__->make_manager_methods;
+__PACKAGE__->add_filter_specs(
+  convertible_to => sub {
+    my ($key, $value, $prefix) = @_;
+    return __PACKAGE__->convertible_to_filter($key, $value, $prefix);
+  },
+);
 
 sub _sort_spec {
   return ( default => [ 'sortkey', 1 ],
@@ -18,4 +25,20 @@ sub _sort_spec {
                       });
 }
 
+sub convertible_to_filter {
+  my ($class, $key, $unit_name, $prefix) = @_;
+
+  return () unless $unit_name;
+
+  $prefix //= '';
+
+  my $unit = $class->find_by(name => $unit_name);
+  if (!$unit) {
+    $::lxdebug->warn("Unit manager: No unit with name $unit_name");
+    return ();
+  }
+
+  return ("${prefix}name" => [ map { $_->name } @{ $unit->convertible_units } ]);
+}
+
 1;
index 6a40a40..c087b7f 100644 (file)
@@ -19,4 +19,83 @@ sub validate {
   return @errors;
 }
 
+sub calc_date {
+  my ($self, %params) = @_;
+
+  my $reference_date  = $params{reference_date} || DateTime->today_local;
+  $reference_date     = DateTime->from_kivitendo($reference_date) unless ref($reference_date) eq 'DateTime';
+
+  my $terms           = ($params{terms} // 'net') eq 'discount' ? 'terms_skonto' : 'terms_netto';
+  my $date            = $reference_date->add(days => $self->$terms);
+
+  my $dow             = $date->day_of_week;
+  $date               = $date->add(days => 8 - $dow) if $dow > 5;
+
+  return $date;
+}
+
 1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::DB::PaymentTerm - Rose model for the payment_terms table
+
+=head1 SYNOPSIS
+
+  my $terms             = SL::DB::PaymentTerm->new(id => $::form->{payment_id})->load;
+  my $due_date_net      = $erms->calc_date(terms => 'net');      # uses terms_netto
+  my $due_date_discount = $erms->calc_date(terms => 'discount'); # uses terms_skonto
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<calc_date [%params]>
+
+Calculates and returns a due date as an instance of L<DateTime> by
+adding one of C<$self>'s terms fields. Note that the resulting date
+will be the following Monday if the result falls on a weekend.
+
+C<%params> can contain the following parameters:
+
+=over 4
+
+=item C<reference_date>
+
+The reference date from which the due date will be calculated. Can be
+either an instance of L<DateTime> or a scalar in which case the scalar
+is parsed via L<DateTime/from_kivitendo>.
+
+Defaults to the current date if unset.
+
+=item C<terms>
+
+Can be either C<net> or C<discount>. For C<net> the number of days to
+add to the reference date are C<$self-E<gt>terms_netto>. For
+C<discount> C<$self-E<gt>terms_skonto> is used.
+
+Defaults to C<net> if unset.
+
+=back
+
+=item C<validate>
+
+Validates before saving and returns an array of human-readable error
+messages in case of an error.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index 6367911..100652e 100644 (file)
@@ -132,7 +132,7 @@ sub drop_constraints {
       AND (table_name      = ?)
 SQL
 
-  $self->db_query(qq|ALTER TABLE auth."$params{table}" DROP CONSTRAINT "${_}"|) for map { $_->[0] } @{ $constraints };
+  $self->db_query(qq|ALTER TABLE $params{schema}."$params{table}" DROP CONSTRAINT "${_}"|) for map { $_->[0] } @{ $constraints };
 }
 
 1;
index fb8672c..15f1c8d 100644 (file)
--- a/SL/DO.pm
+++ b/SL/DO.pm
@@ -39,6 +39,8 @@ use YAML;
 use SL::AM;
 use SL::Common;
 use SL::CVar;
+use SL::DB::DeliveryOrder;
+use SL::DB::Status;
 use SL::DBUtils;
 use SL::RecordLinks;
 use SL::IC;
@@ -507,59 +509,16 @@ sub delete {
   my $form     = $main::form;
   my $spool    = $::lx_office_conf{paths}->{spool};
 
-  # connect to database
-  my $dbh = $form->get_standard_dbh($myconfig);
-
-  # delete spool files
-  my $query = qq|SELECT s.spoolfile FROM status s WHERE s.trans_id = ?|;
-  my $sth   = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
-
-  my $spoolfile;
-  my @spoolfiles = ();
-  my @values;
-
-  while (($spoolfile) = $sth->fetchrow_array) {
-    push @spoolfiles, $spoolfile;
-  }
-  $sth->finish();
-
-  # delete-values
-  @values = (conv_i($form->{id}));
-
-  # delete status entries
-  $query = qq|DELETE FROM status
-              WHERE trans_id = ?|;
-  do_query($form, $dbh, $query, @values);
+  my $rc = SL::DB::Order->new->db->with_transaction(sub {
+    my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
 
-  # delete individual entries
-  $query = qq|DELETE FROM delivery_order_items_stock
-              WHERE delivery_order_item_id IN (
-                SELECT id FROM delivery_order_items
-                WHERE delivery_order_id = ?
-              )|;
-  do_query($form, $dbh, $query, @values);
-
-  # delete individual entries
-  $query = qq|DELETE FROM delivery_order_items
-              WHERE delivery_order_id = ?|;
-  do_query($form, $dbh, $query, @values);
-
-  # delete DO record
-  $query = qq|DELETE FROM delivery_orders
-              WHERE id = ?|;
-  do_query($form, $dbh, $query, @values);
+    SL::DB::DeliveryOrder->new(id => $form->{id})->delete;
 
-  $query = qq|DELETE FROM shipto
-              WHERE trans_id = ? AND module = 'DO'|;
-  do_query($form, $dbh, $query, @values);
-
-  my $rc = $dbh->commit();
+    my $spool = $::lx_office_conf{paths}->{spool};
+    unlink map { "$spool/$_" } @spoolfiles if $spool;
 
-  if ($rc) {
-    foreach $spoolfile (@spoolfiles) {
-      unlink "$spool/$spoolfile" if $spoolfile;
-    }
-  }
+    1;
+  });
 
   $main::lxdebug->leave_sub();
 
index 874aace..79ff703 100644 (file)
@@ -37,6 +37,7 @@
 
 package Form;
 
+use Carp;
 use Data::Dumper;
 
 use CGI;
@@ -53,7 +54,10 @@ use SL::CVar;
 use SL::DB;
 use SL::DBConnect;
 use SL::DBUtils;
+use SL::DB::Customer;
 use SL::DB::Default;
+use SL::DB::PaymentTerm;
+use SL::DB::Vendor;
 use SL::DO;
 use SL::IC;
 use SL::IS;
@@ -1920,22 +1924,12 @@ sub get_duedate {
 
   my ($self, $myconfig, $reference_date) = @_;
 
-  $reference_date = $reference_date ? conv_dateq($reference_date) . '::DATE' : 'current_date';
+  my $terms   = $self->{payment_id}  ? SL::DB::PaymentTerm->new(id => $self->{payment_id}) ->load
+              : $self->{customer_id} ? SL::DB::Customer   ->new(id => $self->{customer_id})->load->payment
+              : $self->{vendor_id}   ? SL::DB::Vendor     ->new(id => $self->{vendor_id})  ->load->payment
+              :                        croak("Missing field in \$::form: payment_id, customer_id or vendor_id");
 
-  my $dbh         = $self->get_standard_dbh($myconfig);
-  my ($payment_id, $duedate);
-
-  if($self->{payment_id}) {
-    $payment_id = $self->{payment_id};
-  } elsif($self->{vendor_id}) {
-    my $query = 'SELECT payment_id FROM vendor WHERE id = ?';
-    ($payment_id) = selectrow_query($self, $dbh, $query, $self->{vendor_id});
-  }
-
-  if ($payment_id) {
-    my $query  = qq|SELECT ${reference_date} + terms_netto FROM payment_terms WHERE id = ?|;
-    ($duedate) = selectrow_query($self, $dbh, $query, $payment_id);
-  }
+  my $duedate = $terms->calc_date(reference_date => $reference_date)->to_kivitendo;
 
   $main::lxdebug->leave_sub();
 
index fec7507..694732e 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -40,7 +40,9 @@ use YAML;
 use SL::AM;
 use SL::Common;
 use SL::CVar;
+use SL::DB::Order;
 use SL::DB::PeriodicInvoicesConfig;
+use SL::DB::Status;
 use SL::DBUtils;
 use SL::IC;
 
@@ -660,59 +662,16 @@ sub delete {
 
   my ($self, $myconfig, $form) = @_;
 
-  # connect to database
-  my $dbh = $form->get_standard_dbh;
-  $dbh->begin_work;
-
-  # delete spool files
-  my $query = qq|SELECT s.spoolfile FROM status s | .
-              qq|WHERE s.trans_id = ?|;
-  my @values = (conv_i($form->{id}));
-  my $sth = $dbh->prepare($query);
-  $sth->execute(@values) || $self->dberror($query);
-
-  my $spoolfile;
-  my @spoolfiles = ();
-
-  while (($spoolfile) = $sth->fetchrow_array) {
-    push @spoolfiles, $spoolfile;
-  }
-  $sth->finish;
-
-  # delete-values
-  @values = (conv_i($form->{id}));
-
-  # periodic invoices and their configuration
-  do_query($form, $dbh, qq|DELETE FROM periodic_invoices         WHERE config_id IN (SELECT id FROM periodic_invoices_configs WHERE oe_id = ?)|, @values);
-  do_query($form, $dbh, qq|DELETE FROM periodic_invoices_configs WHERE oe_id = ?|, @values);
-
-  # delete status entries
-  $query = qq|DELETE FROM status | .
-           qq|WHERE trans_id = ?|;
-  do_query($form, $dbh, $query, @values);
-
-  # delete individual entries
-  $query = qq|DELETE FROM orderitems | .
-           qq|WHERE trans_id = ?|;
-  do_query($form, $dbh, $query, @values);
+  my $rc = SL::DB::Order->new->db->with_transaction(sub {
+    my @spoolfiles = grep { $_ } map { $_->spoolfile } @{ SL::DB::Manager::Status->get_all(where => [ trans_id => $form->{id} ]) };
 
-  $query = qq|DELETE FROM shipto | .
-           qq|WHERE trans_id = ? AND module = 'OE'|;
-  do_query($form, $dbh, $query, @values);
-
-  # delete OE record
-  $query = qq|DELETE FROM oe | .
-           qq|WHERE id = ?|;
-  do_query($form, $dbh, $query, @values);
+    SL::DB::Order->new(id => $form->{id})->delete;
 
-  my $rc = $dbh->commit;
-
-  if ($rc) {
     my $spool = $::lx_office_conf{paths}->{spool};
-    foreach $spoolfile (@spoolfiles) {
-      unlink "$spool/$spoolfile" if $spoolfile;
-    }
-  }
+    unlink map { "$spool/$_" } @spoolfiles if $spool;
+
+    1;
+  });
 
   $main::lxdebug->leave_sub();
 
index f32149d..599d093 100644 (file)
@@ -2,18 +2,21 @@ package SL::Presenter::Part;
 
 use strict;
 
+use SL::DB::Part;
+
 use Exporter qw(import);
 our @EXPORT = qw(part_picker);
 
 sub part_picker {
   my ($self, $name, $value, %params) = @_;
-  my $name_e    = $self->escape($name);
+
+  $value = SL::DB::Manager::Part->find_by(id => $value) if !ref $value;
+  my $id = delete($params{id}) || $self->name_to_id($name);
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'part_autocomplete', type => 'hidden') .
-    $self->input_tag("", delete $params{type}, id => $self->name_to_id("$name_e\_type"), type => 'hidden') .
-    $self->input_tag("", (ref $value && $value->can('description')) ? $value->description : '', id => $self->name_to_id("$name_e\_name"), %params) .
-    $self->input_tag("", delete $params{column}, id => $self->name_to_id("$name_e\_column"), type => 'hidden');
+    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => 'part_autocomplete', type => 'hidden', id => $id) .
+    join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(column type unit convertible_unit)) .
+    $self->input_tag("", (ref $value && $value->can('description')) ? $value->description : '', id => "${id}_name", %params);
 
   $self->html_tag('span', $ret, class => 'part_picker');
 }
@@ -40,17 +43,33 @@ see L<SL::Presenter>
 
 =over 4
 
-=item C<part_picker NAME, VALUE, PARAMS>
+=item C<part_picker $name, $value, %params>
+
+All-in-one picker widget for parts. The name will be both id and name
+of the resulting hidden C<id> input field (but the ID can be
+overwritten with C<$params{id}>).
+
+An additional dummy input will be generated which is used to find
+parts. For a detailed description of it's behaviour, see section
+C<PART PICKER SPECIFICATION>.
+
+C<$value> can be a parts id or a C<Rose::DB:Object> instance.
 
 All-in-one picker widget for parts. The name will be both id and name of the
 resulting hidden C<id> input field. An additional dummy input will be generated
 which is used to find parts. For a detailed description of it's behaviour, see
 section L</PART PICKER SPECIFICATION>.
 
-C<VALUE> can be an id or C<Rose::DB:Object> instance.
+If C<%params> contains C<type> only parts of this type will be used
+for autocompletion. You may comma separate multiple types as in
+C<part,assembly>.
+
+If C<%params> contains C<unit> only parts with this unit will be used
+for autocompletion. You may comma separate multiple units as in
+C<h,min>.
 
-If C<PARAMS> contains C<type> only parts of this type will be used for
-autocompletion. You may comma separate multiple types as in C<part,assembly>.
+If C<%params> contains C<convertible_unit> only parts with a unit
+that's convertible to unit will be used for autocompletion.
 
 Obsolete parts will by default not displayed for selection. However they are
 accepted as default values and can persist during updates. As with other
index 901da5e..b9ef3e9 100644 (file)
@@ -12,6 +12,8 @@ namespace('kivi', function(k){
     var real_id = $real.attr('id');
     var $dummy  = $('#' + real_id + '_name');
     var $type   = $('#' + real_id + '_type');
+    var $unit   = $('#' + real_id + '_unit');
+    var $convertible_unit = $('#' + real_id + '_convertible_unit');
     var $column = $('#' + real_id + '_column');
     var state   = STATES.PICKED;
     var last_real = $real.val();
@@ -28,13 +30,21 @@ namespace('kivi', function(k){
     };
 
     function ajax_data(term) {
-      return {
+      var data = {
         'filter.all:substr::ilike': term,
-        'filter.type':  $type.val().split(','),
         'filter.obsolete': 0,
-        column:   $column.val()===undefined ? '' : $column.val(),
+        'filter.unit_obj.convertible_to': $convertible_unit && $convertible_unit.val() ? $convertible_unit.val() : '',
+        column:   $column && $column.val() ? $column.val() : '',
         current:  $real.val(),
-      }
+      };
+
+      if ($type && $type.val())
+        data['filter.type'] = $type.val().split(',');
+
+      if ($unit && $unit.val())
+        data['filter.unit'] = $unit.val().split(',');
+
+      return data;
     }
 
     function set_item (item) {
@@ -145,6 +155,8 @@ namespace('kivi', function(k){
       real:           function() { return $real },
       dummy:          function() { return $dummy },
       type:           function() { return $type },
+      unit:           function() { return $unit },
+      convertible_unit: function() { return $convertible_unit },
       column:         function() { return $column },
       update_results: update_results,
       set_item:       set_item,
index b89a5b4..71f1d31 100644 (file)
@@ -185,14 +185,6 @@ function open_jqm_window(params) {
   return true;
 }
 
-function close_jqm_window(params) {
-  params = params || { };
-  var url = params.url;
-  var id  = params.id ? params.id : 'jqm_popup_dialog';
-
-  $('#' + id).jqmClose()();
-}
-
 $(document).ready(function () {
   // initialize all jQuery UI tab elements:
   $(".tabwidget").each(function(idx, element) { $(element).tabs(); });
diff --git a/sql/Pg-upgrade2/oe_do_delete_via_trigger.pl b/sql/Pg-upgrade2/oe_do_delete_via_trigger.pl
new file mode 100644 (file)
index 0000000..34f4a2a
--- /dev/null
@@ -0,0 +1,77 @@
+# @tag: oe_do_delete_via_trigger
+# @description: Aus oe/delivery_orders via Trigger löschen können
+# @depends: release_3_0_0
+
+package SL::DBUpgrade2::oe_do_delete_via_trigger;
+
+use utf8;
+use strict;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub run {
+  my ($self) = @_;
+
+  $self->drop_constraints(table => $_) for qw(periodic_invoices periodic_invoices_configs orderitems delivery_order_items delivery_order_items_stock);
+
+  my @queries = (
+    q|ALTER TABLE periodic_invoices          ADD CONSTRAINT periodic_invoices_ar_id_fkey                           FOREIGN KEY (ar_id)                  REFERENCES ar                        (id) ON DELETE CASCADE|,
+    q|ALTER TABLE periodic_invoices          ADD CONSTRAINT periodic_invoices_config_id_fkey                       FOREIGN KEY (config_id)              REFERENCES periodic_invoices_configs (id) ON DELETE CASCADE|,
+
+    q|ALTER TABLE periodic_invoices_configs  ADD CONSTRAINT periodic_invoices_configs_ar_chart_id_fkey             FOREIGN KEY (ar_chart_id)            REFERENCES chart                     (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE periodic_invoices_configs  ADD CONSTRAINT periodic_invoices_configs_oe_id_fkey                   FOREIGN KEY (oe_id)                  REFERENCES oe                        (id) ON DELETE CASCADE|,
+    q|ALTER TABLE periodic_invoices_configs  ADD CONSTRAINT periodic_invoices_configs_printer_id_fkey              FOREIGN KEY (printer_id)             REFERENCES printers                  (id) ON DELETE SET NULL|,
+
+    q|ALTER TABLE orderitems                 ADD CONSTRAINT orderitems_parts_id_fkey                               FOREIGN KEY (parts_id)               REFERENCES parts                     (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE orderitems                 ADD CONSTRAINT orderitems_price_factor_id_fkey                        FOREIGN KEY (price_factor_id)        REFERENCES price_factors             (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE orderitems                 ADD CONSTRAINT orderitems_pricegroup_id_fkey                          FOREIGN KEY (pricegroup_id)          REFERENCES pricegroup                (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE orderitems                 ADD CONSTRAINT orderitems_project_id_fkey                             FOREIGN KEY (project_id)             REFERENCES project                   (id) ON DELETE SET NULL|,
+    q|ALTER TABLE orderitems                 ADD CONSTRAINT orderitems_trans_id_fkey                               FOREIGN KEY (trans_id)               REFERENCES oe                        (id) ON DELETE CASCADE|,
+
+    q|ALTER TABLE delivery_order_items       ADD CONSTRAINT delivery_order_items_delivery_order_id_fkey            FOREIGN KEY (delivery_order_id)      REFERENCES delivery_orders           (id) ON DELETE CASCADE|,
+    q|ALTER TABLE delivery_order_items       ADD CONSTRAINT delivery_order_items_parts_id_fkey                     FOREIGN KEY (parts_id)               REFERENCES parts                     (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE delivery_order_items       ADD CONSTRAINT delivery_order_items_price_factor_id_fkey              FOREIGN KEY (price_factor_id)        REFERENCES price_factors             (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE delivery_order_items       ADD CONSTRAINT delivery_order_items_pricegroup_id_fkey                FOREIGN KEY (pricegroup_id)          REFERENCES pricegroup                (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE delivery_order_items       ADD CONSTRAINT delivery_order_items_project_id_fkey                   FOREIGN KEY (project_id)             REFERENCES project                   (id) ON DELETE SET NULL|,
+
+    q|ALTER TABLE delivery_order_items_stock ADD CONSTRAINT delivery_order_items_stock_bin_id_fkey                 FOREIGN KEY (bin_id)                 REFERENCES bin                       (id) ON DELETE RESTRICT|,
+    q|ALTER TABLE delivery_order_items_stock ADD CONSTRAINT delivery_order_items_stock_delivery_order_item_id_fkey FOREIGN KEY (delivery_order_item_id) REFERENCES delivery_order_items      (id) ON DELETE CASCADE|,
+    q|ALTER TABLE delivery_order_items_stock ADD CONSTRAINT delivery_order_items_stock_warehouse_id_fkey           FOREIGN KEY (warehouse_id)           REFERENCES warehouse                 (id) ON DELETE RESTRICT|,
+
+    q|CREATE OR REPLACE FUNCTION oe_before_delete_trigger() RETURNS trigger AS $$
+        BEGIN
+          DELETE FROM status WHERE trans_id = OLD.id;
+          DELETE FROM shipto WHERE (trans_id = OLD.id) AND (module = 'OE');
+
+          RETURN OLD;
+        END;
+      $$ LANGUAGE plpgsql|,
+
+    q|DROP TRIGGER IF EXISTS delete_oe_dependencies ON oe|,
+
+    q|CREATE TRIGGER delete_oe_dependencies
+      BEFORE DELETE ON oe
+      FOR EACH ROW EXECUTE PROCEDURE oe_before_delete_trigger()|,
+
+    q|CREATE OR REPLACE FUNCTION delivery_orders_before_delete_trigger() RETURNS trigger AS $$
+        BEGIN
+          DELETE FROM status                     WHERE trans_id = OLD.id;
+          DELETE FROM delivery_order_items_stock WHERE delivery_order_item_id IN (SELECT id FROM delivery_order_items WHERE delivery_order_id = OLD.id);
+          DELETE FROM shipto                     WHERE (trans_id = OLD.id) AND (module = 'OE');
+
+          RETURN OLD;
+        END;
+      $$ LANGUAGE plpgsql|,
+
+    q|DROP TRIGGER IF EXISTS delete_delivery_orders_dependencies ON delivery_orders|,
+
+    q|CREATE TRIGGER delete_delivery_orders_dependencies
+      BEFORE DELETE ON delivery_orders
+      FOR EACH ROW EXECUTE PROCEDURE delivery_orders_before_delete_trigger()|);
+
+  $self->db_query($_) for @queries;
+
+  return 1;
+}
+
+1;