From: Bernd Bleßmann Date: Wed, 2 Dec 2015 10:28:36 +0000 (+0100) Subject: Auftrags-Controller: vorhandene orderitems vor Neu-Schreiben nicht löschen … X-Git-Tag: release-3.4.1~337 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=5dd5e97bc9fe0aee0be2621fe11d48c86be40a5d;p=kivitendo-erp.git Auftrags-Controller: vorhandene orderitems vor Neu-Schreiben nicht löschen … Hintergrund: Wird einem Rose-Object eine Relationship als Array übergeben (z.B. $order->orderitems(@items), so löscht Rose dei DB-Einträge und schreibt sie neu. In dem Fall werden allerdings auch DB-Trigger ausgelöst, was hier zum unerwünschten Löschen der record_links auf item-Ebene führte. Hier wurden die items via assign_attributes als ganzes dem Order-Objekt hinzugefügt. Jetzt werden die items einzeln hinzugefügt, wenn sie neu sind und die vorhandenen, die aus der Makse entfernt wurde, werden extra gelöscht. --- diff --git a/SL/Controller/Order.pm b/SL/Controller/Order.pm index 4a06c6ba2..8bebe381d 100644 --- a/SL/Controller/Order.pm +++ b/SL/Controller/Order.pm @@ -34,6 +34,7 @@ use File::Spec; use Rose::Object::MakeMethods::Generic ( + scalar => [ qw(item_ids_to_delete) ], 'scalar --get_set_init' => [ qw(order valid_types type cv p multi_items_models) ], ); @@ -42,7 +43,7 @@ use Rose::Object::MakeMethods::Generic __PACKAGE__->run_before('_check_auth'); __PACKAGE__->run_before('_recalc', - only => [ qw(edit update save save_and_delivery_order create_pdf send_email) ]); + only => [ qw(update save save_and_delivery_order create_pdf send_email) ]); __PACKAGE__->run_before('_get_unalterable_data', only => [ qw(save save_and_delivery_order create_pdf send_email) ]); @@ -70,6 +71,8 @@ sub action_add { sub action_edit { my ($self) = @_; + $self->_load_order; + $self->_recalc(); $self->_pre_render(); $self->render( 'order/form', @@ -333,7 +336,7 @@ sub action_unit_changed { my ($self) = @_; my $idx = first_index { $_ eq $::form->{item_id} } @{ $::form->{orderitem_ids} }; - my $item = $self->order->items->[$idx]; + my $item = $self->order->items_sorted->[$idx]; my $old_unit_obj = SL::DB::Unit->new(name => $::form->{old_unit})->load; $item->sellprice($item->unit_obj->convert_to($item->sellprice, $old_unit_obj)); @@ -354,7 +357,7 @@ sub action_add_item { return unless $form_attr->{parts_id}; - my $item = _make_item($self->order, $form_attr); + my $item = _new_item($self->order, $form_attr); $self->order->add_items($item); $self->_recalc(); @@ -407,7 +410,7 @@ sub action_add_multi_items { my @items; foreach my $attr (@form_attr) { - push @items, _make_item($self->order, $attr); + push @items, _new_item($self->order, $attr); } $self->order->add_items(@items); @@ -448,7 +451,7 @@ sub action_price_popup { my ($self) = @_; my $idx = first_index { $_ eq $::form->{item_id} } @{ $::form->{orderitem_ids} }; - my $item = $self->order->items->[$idx]; + my $item = $self->order->items_sorted->[$idx]; $self->render_price_dialog($item); } @@ -456,7 +459,7 @@ sub action_price_popup { sub _js_redisplay_linetotals { my ($self) = @_; - my @data = map {$::form->format_amount(\%::myconfig, $_->{linetotal}, 2, 0)} @{ $self->order->items }; + my @data = map {$::form->format_amount(\%::myconfig, $_->{linetotal}, 2, 0)} @{ $self->order->items_sorted }; $self->js ->run('redisplay_linetotals', \@data); } @@ -516,7 +519,7 @@ sub init_p { } sub init_order { - _make_order(); + $_[0]->_make_order; } sub init_multi_items_models { @@ -603,6 +606,14 @@ sub render_price_dialog { $self->js->render; } +sub _load_order { + my ($self) = @_; + + return if !$::form->{id}; + + $self->order(SL::DB::Manager::Order->find_by(id => $::form->{id})); +} + sub _make_order { my ($self) = @_; @@ -613,14 +624,53 @@ sub _make_order { $order = SL::DB::Manager::Order->find_by(id => $::form->{id}) if $::form->{id}; $order ||= SL::DB::Order->new(orderitems => []); + my $form_orderitems = delete $::form->{order}->{orderitems}; $order->assign_attributes(%{$::form->{order}}); + # remove deleted items + $self->item_ids_to_delete([]); + foreach my $idx (reverse 0..$#{$order->orderitems}) { + my $item = $order->orderitems->[$idx]; + if (none { $item->id == $_->{id} } @{$form_orderitems}) { + splice @{$order->orderitems}, $idx, 1; + push @{$self->item_ids_to_delete}, $item->id; + } + } + + my @items; + my $pos = 1; + foreach my $form_attr (@{$form_orderitems}) { + my $item = _make_item($order, $form_attr); + $item->position($pos); + push @items, $item; + $pos++; + } + $order->add_items(grep {!$_->id} @items); + return $order; } + +# Make item objects from form values. For items already existing read from db. +# Create a new item else. And assign attributes. sub _make_item { my ($record, $attr) = @_; + my $item; + $item = first { $_->id == $attr->{id} } @{$record->items} if $attr->{id}; + + # add_custom_variables adds cvars to an orderitem with no cvars for saving, but + # they cannot be retrieved via custom_variables until the order/orderitem is + # saved. Adding empty custom_variables to new orderitem here solves this problem. + $item ||= SL::DB::OrderItem->new(custom_variables => []); + $item->assign_attributes(%$attr); + + return $item; +} + +sub _new_item { + my ($record, $attr) = @_; + my $item = SL::DB::OrderItem->new; $item->assign_attributes(%$attr); @@ -742,7 +792,8 @@ sub _save { $db->do_transaction( sub { - $self->order->save(); + SL::DB::OrderItem->new(id => $_)->delete for @{$self->item_ids_to_delete}; + $self->order->save(cascade => 1); }) || push(@{$errors}, $db->error); return $errors; @@ -768,11 +819,10 @@ sub _pre_render { $self->{current_employee_id} = SL::DB::Manager::Employee->current->id; - foreach my $item (@{$self->order->items}) { + foreach my $item (@{$self->order->orderitems}) { my $price_source = SL::PriceSource->new(record_item => $item, record => $self->order); $item->active_price_source( $price_source->price_from_source( $item->active_price_source )); $item->active_discount_source($price_source->discount_from_source($item->active_discount_source)); - } if ($self->order->ordnumber && $::instance_conf->get_webdav) {