commit 0743ffa06124546396483188972655dfece2e99b
Author: Tamino Steinert <tamino.steinert@tamino.st>
Date:   Wed Nov 3 09:38:54 2021 +0100

    Workflow: (sales/purchase) invoice → reclamation

diff --git a/SL/Controller/Reclamation.pm b/SL/Controller/Reclamation.pm
index 9e69441a6..1842bb4ec 100644
--- a/SL/Controller/Reclamation.pm
+++ b/SL/Controller/Reclamation.pm
@@ -146,6 +146,53 @@ sub action_add_from_delivery_order {
   );
 }
 
+sub action_add_from_sales_invoice {
+  my ($self) = @_;
+
+  unless ($::form->{from_id}) {
+    $self->js->flash('error', t8("Can't create new reclamation. No 'from_id' was given."));
+    return $self->js->render();
+  }
+
+  require SL::DB::Invoice;
+  my $invoice = SL::DB::Invoice->new(id => $::form->{from_id})->load;
+  my $reclamation = $invoice->convert_to_reclamation();
+
+  $self->reclamation($reclamation);
+
+  $self->reinit_after_new_reclamation();
+
+  $self->render(
+    'reclamation/form',
+    title => $self->get_title_for('add'),
+    %{$self->{template_args}},
+  );
+}
+
+sub action_add_from_purchase_invoice {
+  my ($self) = @_;
+
+  unless ($::form->{from_id}) {
+    $self->js->flash('error', t8("Can't create new reclamation. No 'from_id' was given."));
+    return $self->js->render();
+  }
+
+  require SL::DB::PurchaseInvoice;
+  my $invoice = SL::DB::PurchaseInvoice->new(id => $::form->{from_id})->load;
+  $invoice->{type} = $invoice->invoice_type; #can't add type → invoice_type in SL/DB/PurchaseInvoice
+  my $reclamation = $invoice->convert_to_reclamation();
+
+  $self->reclamation($reclamation);
+
+  $self->reinit_after_new_reclamation();
+
+  $self->render(
+    'reclamation/form',
+    title => $self->get_title_for('add'),
+    %{$self->{template_args}},
+  );
+}
+
 # edit an existing reclamation
 sub action_edit {
   my ($self) = @_;
@@ -1658,11 +1705,14 @@ sub _link_to_records {
     SL::DB::Reclamation
     SL::DB::Order
     SL::DB::DeliveryOrder
+    SL::DB::Invoice
+    SL::DB::PurchaseInvoice
   );
   my %allowed_linked_record_items = map {$_ => 1} qw(
     SL::DB::ReclamationItem
     SL::DB::OrderItem
     SL::DB::DeliveryOrderItem
+    SL::DB::InvoiceItem
   );
 
   my $from_record_id = delete $::form->{converted_from_record_id};
diff --git a/SL/DB/Invoice.pm b/SL/DB/Invoice.pm
index d1ce5be87..7c2677838 100644
--- a/SL/DB/Invoice.pm
+++ b/SL/DB/Invoice.pm
@@ -137,6 +137,17 @@ sub closed {
   return $self->paid >= $self->amount;
 }
 
+sub convert_to_reclamation {
+  my ($self, %params) = @_;
+  $params{destination_type} = $self->is_sales ? 'sales_reclamation'
+                                              : 'purchase_reclamation';
+
+  require SL::DB::Reclamation;
+  my $reclamation = SL::DB::Reclamation->new_from($self, %params);
+
+  return $reclamation;
+}
+
 sub _clone_orderitem_delivery_order_item_cvar {
   my ($cvar) = @_;
 
diff --git a/SL/DB/PurchaseInvoice.pm b/SL/DB/PurchaseInvoice.pm
index 3eb320ee3..3fdbae6ea 100644
--- a/SL/DB/PurchaseInvoice.pm
+++ b/SL/DB/PurchaseInvoice.pm
@@ -124,6 +124,17 @@ sub displayable_name {
   join ' ', grep $_, map $_[0]->$_, qw(displayable_type record_number);
 };
 
+sub convert_to_reclamation {
+  my ($self, %params) = @_;
+  $params{destination_type} = $self->is_sales ? 'sales_reclamation'
+                                              : 'purchase_reclamation';
+
+  require SL::DB::Reclamation;
+  my $reclamation = SL::DB::Reclamation->new_from($self, %params);
+
+  return $reclamation;
+}
+
 sub create_ap_row {
   my ($self, %params) = @_;
   # needs chart as param
diff --git a/SL/DB/Reclamation.pm b/SL/DB/Reclamation.pm
index f72c4e590..412f5a5ad 100644
--- a/SL/DB/Reclamation.pm
+++ b/SL/DB/Reclamation.pm
@@ -258,6 +258,8 @@ sub new_from {
     SL::DB::Reclamation
     SL::DB::Order
     SL::DB::DeliveryOrder
+    SL::DB::Invoice
+    SL::DB::PurchaseInvoice
   );
   unless( $allowed_sources{ref $source} ) {
     croak("Unsupported source object type '" . ref($source) . "'");
@@ -278,6 +280,9 @@ sub new_from {
     #Delivery Order
     { from => 'sales_delivery_order',    to => 'sales_reclamation',    abbr => 'sdsr', },
     { from => 'purchase_delivery_order', to => 'purchase_reclamation', abbr => 'pdpr', },
+    #Invoice
+    { from => 'invoice',                 to => 'sales_reclamation',    abbr => 'sisr', },
+    { from => 'purchase_invoice',        to => 'purchase_reclamation', abbr => 'pipr', },
   );
   my $from_to = (grep { $_->{from} eq $source->type && $_->{to} eq $destination_type} @from_tos)[0];
   if (!$from_to) {
@@ -373,6 +378,53 @@ sub new_from {
     $record_args{contact_id} = $source->cp_id;
     $record_args{cv_record_number} = $source->cusordnumber;
     # }}} for vim folds
+  } elsif ( $is_abbr_any->(qw(sisr)) ) { #Invoice(ar)
+    map { $record_args{$_} = $source->$_ } # {{{ for vim folds
+    qw(
+      amount
+      currency_id
+      customer_id
+      delivery_term_id
+      department_id
+      globalproject_id
+      intnotes
+      language_id
+      netamount
+      notes
+      payment_id
+      salesman_id
+      shippingpoint
+      shipvia
+      tax_point
+      taxincluded
+      taxzone_id
+      transaction_description
+    );
+    $record_args{contact_id} = $source->cp_id;
+    $record_args{cv_record_number} = $source->cusordnumber;
+    # }}} for vim folds
+  } elsif ( $is_abbr_any->(qw(pipr)) ) { #Invoice(ap)
+    map { $record_args{$_} = $source->$_ } # {{{ for vim folds
+    qw(
+      amount
+      currency_id
+      delivery_term_id
+      department_id
+      globalproject_id
+      intnotes
+      language_id
+      netamount
+      notes
+      payment_id
+      shipvia
+      tax_point
+      taxincluded
+      taxzone_id
+      transaction_description
+      vendor_id
+    );
+    $record_args{contact_id} = $source->cp_id;
+    # }}} for vim folds
   }
 
   if ( ($from_to->{from} =~ m{sales}) && ($from_to->{to} =~ m{purchase}) ) {
diff --git a/SL/DB/ReclamationItem.pm b/SL/DB/ReclamationItem.pm
index 7b8d83093..01f7e6d79 100644
--- a/SL/DB/ReclamationItem.pm
+++ b/SL/DB/ReclamationItem.pm
@@ -55,6 +55,7 @@ sub new_from {
       SL::DB::ReclamationItem
       SL::DB::OrderItem
       SL::DB::DeliveryOrderItem
+      SL::DB::InvoiceItem
     )
   ) {
     croak("Unsupported source object type '" . ref($source) . "'");
@@ -86,6 +87,13 @@ sub new_from {
       pricegroup_id project_id qty reqdate sellprice serialnumber unit
     );
     $item_args{custom_variables} = \@custom_variables;
+  } elsif (ref($source) eq 'SL::DB::InvoiceItem') {
+    map { $item_args{$_} = $source->$_ } qw(
+      active_discount_source active_price_source base_qty description discount
+      lastcost longdescription parts_id position price_factor price_factor_id
+      pricegroup_id project_id qty sellprice serialnumber unit
+    );
+    $item_args{custom_variables} = \@custom_variables;
   }
 
   my $item = $class->new(%item_args);
diff --git a/bin/mozilla/io.pl b/bin/mozilla/io.pl
index 793099ff0..738c5c142 100644
--- a/bin/mozilla/io.pl
+++ b/bin/mozilla/io.pl
@@ -1109,6 +1109,32 @@ sub request_for_quotation {
   quotation();
 }
 
+sub sales_reclamation {
+  my $id = $::form->{id};
+
+  require SL::Controller::Reclamation;
+  my $c = SL::Controller::Reclamation->new();
+  $c->redirect_to(
+    controller => 'Reclamation',
+    action => 'add_from_sales_invoice',
+    from_id => $id,
+    type => 'sales_reclamation',
+  );
+}
+
+sub purchase_reclamation {
+  my $id = $::form->{id};
+
+  require SL::Controller::Reclamation;
+  my $c = SL::Controller::Reclamation->new();
+  $c->redirect_to(
+    controller => 'Reclamation',
+    action => 'add_from_purchase_invoice',
+    from_id => $id,
+    type => 'purchase_reclamation',
+  );
+}
+
 sub print_options {
   $::lxdebug->enter_sub();
 
diff --git a/bin/mozilla/ir.pl b/bin/mozilla/ir.pl
index b0df30c44..80497e7ff 100644
--- a/bin/mozilla/ir.pl
+++ b/bin/mozilla/ir.pl
@@ -362,6 +362,12 @@ sub setup_ir_action_bar {
                     : !$form->{id}      ? t8('This invoice has not been posted yet.')
                     :                     undef,
         ],
+        action => [
+          t8('Reclamation'),
+          submit   => ['#form', { action => "purchase_reclamation" }], # can't call Reclamation directly
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+          only_if   => ($::form->{type} eq 'purchase_invoice')
+        ],
        ], # end of combobox "Workflow"
 
       combobox => [
diff --git a/bin/mozilla/is.pl b/bin/mozilla/is.pl
index d0e25062a..398ed4f87 100644
--- a/bin/mozilla/is.pl
+++ b/bin/mozilla/is.pl
@@ -469,6 +469,12 @@ sub setup_is_action_bar {
           checks   => [ 'kivi.validate_form' ],
           disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
         ],
+        action => [
+          t8('Reclamation'),
+          submit   => ['#form', { action => "sales_reclamation" }], # can't call Reclamation directly
+          disabled => !$form->{id} ? t8('This invoice has not been posted yet.') : undef,
+          only_if   => ($::form->{type} eq 'invoice')
+        ],
       ], # end of combobox "Workflow"
 
       combobox => [
