Neuer Workflow Lieferantenauftrag->Kreditorenbuchung
authorBernd Bleßmann <bernd@kivitendo-premium.de>
Fri, 2 Aug 2019 16:00:21 +0000 (18:00 +0200)
committerBernd Bleßmann <bernd@kivitendo-premium.de>
Thu, 15 Aug 2019 08:23:37 +0000 (10:23 +0200)
Für jedes Aufwandskonto der Positionen im Lieferantenauftrag wird eine
Zeile in der Kreditorenbuchung erstellt. Gebucht wird standardmäßig
auf des entsprechende Aufwandskonto. In der Mandantenkonfiguration
kann unter Standardkonten ein Konto ausgewählt werden, auf das dann
alle Zeilen gebucht werden.
Die Steuern werden übernommen, sofern diese für das ausgewählte
Aufwandskonto gültig sind. Ansonsten wird die Default-Steuer für das
Aufwandskonto gesetzt.
Der Quellauftrag wird geschlossen, wenn der Betrag aller
Kreditorenbuchungen, die aus Workflows aus dem Quellauftrag entstanden
sind, gleich dem Betrag des Quellauftrags ist.

SL/AP.pm
SL/Controller/Order.pm
SL/DB/MetaSetup/Default.pm
bin/mozilla/ap.pl
doc/changelog
locale/de/all
locale/en/all
sql/Pg-upgrade2/defaults_workflow_po_ap_chart_id.sql [new file with mode: 0644]
templates/webpages/ap/form_header.html
templates/webpages/client_config/_default_accounts.html

index caae2bb..b160802 100644 (file)
--- a/SL/AP.pm
+++ b/SL/AP.pm
@@ -41,10 +41,12 @@ use SL::IO;
 use SL::MoreCommon;
 use SL::DB::Default;
 use SL::DB::Draft;
+use SL::DB::Order;
+use SL::DB::PurchaseInvoice;
 use SL::Util qw(trim);
 use SL::DB;
 use Data::Dumper;
-
+use List::Util qw(sum0);
 use strict;
 
 sub post_transaction {
@@ -155,6 +157,31 @@ sub _post_transaction {
 
     $form->new_lastmtime('ap');
 
+    # Link this record to the record it was created from.
+    my $convert_from_oe_id = delete $form->{convert_from_oe_id};
+    if (!$form->{postasnew} && $convert_from_oe_id) {
+      RecordLinks->create_links('dbh'        => $dbh,
+                                'mode'       => 'ids',
+                                'from_table' => 'oe',
+                                'from_ids'   => $convert_from_oe_id,
+                                'to_table'   => 'ap',
+                                'to_id'      => $form->{id},
+      );
+
+      # Close the record it was created from if the amount of
+      # all APs create from this record equals the records amount.
+      my @links = RecordLinks->get_links('dbh'        => $dbh,
+                                         'from_table' => 'oe',
+                                         'from_id'    => $convert_from_oe_id,
+                                         'to_table'   => 'ap',
+      );
+
+      my $amount_sum = sum0 map { SL::DB::PurchaseInvoice->new(id => $_->{to_id})->load->amount } @links;
+      my $order      = SL::DB::Order->new(id => $convert_from_oe_id)->load;
+
+      $order->update_attributes(closed => 1) if ($amount_sum - $order->amount) == 0;
+    }
+
     # add individual transactions
     for my $i (1 .. $form->{rowcount}) {
       if ($form->{"amount_$i"} != 0) {
index 7a7841d..47ab4e7 100644 (file)
@@ -48,10 +48,12 @@ use Rose::Object::MakeMethods::Generic
 __PACKAGE__->run_before('check_auth');
 
 __PACKAGE__->run_before('recalc',
-                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice print send_email) ]);
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_ap_transaction
+                                     print send_email) ]);
 
 __PACKAGE__->run_before('get_unalterable_data',
-                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice print send_email) ]);
+                        only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_ap_transaction
+                                     print send_email) ]);
 
 #
 # actions
@@ -648,6 +650,33 @@ sub action_purchase_order {
   $_[0]->workflow_sales_or_purchase_order();
 }
 
+# workflow from purchase order to ap transaction
+sub action_save_and_ap_transaction {
+  my ($self) = @_;
+
+  my $errors = $self->save();
+
+  if (scalar @{ $errors }) {
+    $self->js->flash('error', $_) foreach @{ $errors };
+    return $self->js->render();
+  }
+
+  my $text = $self->type eq sales_order_type()       ? $::locale->text('The order has been saved')
+           : $self->type eq purchase_order_type()    ? $::locale->text('The order has been saved')
+           : $self->type eq sales_quotation_type()   ? $::locale->text('The quotation has been saved')
+           : $self->type eq request_quotation_type() ? $::locale->text('The rfq has been saved')
+           : '';
+  flash_later('info', $text);
+
+  my @redirect_params = (
+    controller => 'ap.pl',
+    action     => 'add_from_purchase_order',
+    id         => $self->order->id,
+  );
+
+  $self->redirect_to(@redirect_params);
+}
+
 # set form elements in respect to a changed customer or vendor
 #
 # This action is called on an change of the customer/vendor picker.
@@ -1695,6 +1724,12 @@ sub setup_edit_action_bar {
           call      => [ 'kivi.Order.save', 'save_and_invoice', $::instance_conf->get_order_warn_duplicate_parts ],
           checks    => [ 'kivi.Order.check_save_active_periodic_invoices' ],
         ],
+        action => [
+          t8('Save and AP Transaction'),
+          call      => [ 'kivi.Order.save', 'save_and_ap_transaction', $::instance_conf->get_order_warn_duplicate_parts ],
+          only_if   => (any { $self->type eq $_ } (purchase_order_type()))
+        ],
+
       ], # end of combobox "Workflow"
 
       combobox => [
index c0f6a44..e588b3a 100644 (file)
@@ -167,6 +167,7 @@ __PACKAGE__->meta->columns(
   webdav                                    => { type => 'boolean', default => 'false' },
   webdav_documents                          => { type => 'boolean', default => 'false' },
   weightunit                                => { type => 'varchar', length => 5 },
+  workflow_po_ap_chart_id                   => { type => 'integer' },
 );
 
 __PACKAGE__->meta->primary_key_columns([ 'id' ]);
index 49f6f7a..a5d20da 100644 (file)
@@ -47,6 +47,7 @@ use SL::DB::BankTransactionAccTrans;
 use SL::DB::Chart;
 use SL::DB::Currency;
 use SL::DB::Default;
+use SL::DB::Order;
 use SL::DB::PurchaseInvoice;
 use SL::DB::RecordTemplate;
 use SL::DB::Tax;
@@ -649,7 +650,6 @@ sub update {
       # calculate tax exactly the same way as AP in post_transaction via form->calculate_tax
       my $tmpnetamount;
       ($tmpnetamount,$form->{"tax_$i"}) = $form->calculate_tax($form->{"amount_$i"},$rate,$form->{taxincluded},2);
-
       $totaltax += $form->{"tax_$i"};
       map { $a[$j]->{$_} = $form->{"${_}_$i"} } @flds;
       $count++;
@@ -899,7 +899,7 @@ sub use_as_new {
 
   $main::auth->assert('ap_transactions');
 
-  map { delete $form->{$_} } qw(printed emailed queued invnumber deliverydate id datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno);
+  map { delete $form->{$_} } qw(printed emailed queued invnumber deliverydate id datepaid_1 gldate_1 acc_trans_id_1 source_1 memo_1 paid_1 exchangerate_1 AP_paid_1 storno convert_from_oe_id);
   $form->{paidaccounts} = 1;
   $form->{rowcount}--;
 
@@ -1183,6 +1183,74 @@ sub storno {
   $main::lxdebug->leave_sub();
 }
 
+sub add_from_purchase_order {
+  $main::auth->assert('ap_transactions');
+
+  return if !$::form->{id};
+
+  my $order_id = delete $::form->{id};
+  my $order    = SL::DB::Order->new(id => $order_id)->load;
+
+  return if $order->type ne 'purchase_order';
+
+  my $today                     = DateTime->today_local;
+  $::form->{title}              = "Add";
+  $::form->{vc}                 = 'vendor';
+  $::form->{vendor_id}          = $order->customervendor->id;
+  $::form->{vendor}             = $order->vendor->name;
+  $::form->{convert_from_oe_id} = $order->id;
+  $::form->{globalproject_id}   = $order->globalproject_id;
+  $::form->{ordnumber}          = $order->number;
+  $::form->{department_id}      = $order->department_id;
+  $::form->{currency}           = $order->currency->name;
+  $::form->{taxincluded}        = 1; # we use amount below, so tax is included
+  $::form->{transdate}          = $today->to_kivitendo;
+  $::form->{duedate}            = $today->to_kivitendo;
+  $::form->{duedate}            = $order->vendor->payment->calc_date(reference_date => $today)->to_kivitendo if $order->vendor->payment;
+
+  create_links();
+
+  my $config_po_ap_workflow_chart_id = $::instance_conf->get_workflow_po_ap_chart_id;
+
+  my ($first_taxchart, $default_taxchart, $taxchart_to_use);
+  my @taxcharts = ();
+  @taxcharts    = GL->get_active_taxes_for_chart($config_po_ap_workflow_chart_id, $::form->{transdate}) if (defined $config_po_ap_workflow_chart_id);
+  foreach my $item (@taxcharts) {
+    $first_taxchart   //= $item;
+    $default_taxchart   = $item if $item->{is_default};
+  }
+  $taxchart_to_use      = $default_taxchart // $first_taxchart;
+
+  my %pat = $order->calculate_prices_and_taxes;
+  my $row = 1;
+  foreach my $amount_chart (keys %{$pat{amounts}}) {
+    my $tax = SL::DB::Manager::Tax->find_by(id => $pat{amounts}->{$amount_chart}->{tax_id});
+    # If tax chart from order for this amount is active, use it. Use default or first tax chart for selected chart else.
+    if (defined $config_po_ap_workflow_chart_id) {
+      $taxchart_to_use = (first {$_->{id} == $tax->id} @taxcharts) // $taxchart_to_use;
+    } else {
+      $taxchart_to_use = $tax;
+    }
+
+    $::form->{"AP_amount_chart_id_$row"}          = $config_po_ap_workflow_chart_id // $amount_chart;
+    $::form->{"previous_AP_amount_chart_id_$row"} = $::form->{"AP_amount_chart_id_$row"};
+    $::form->{"amount_$row"}                      = $::form->format_amount(\%::myconfig, $pat{amounts}->{$amount_chart}->{amount} * (1 + $tax->rate), 2);
+    $::form->{"taxchart_$row"}                    = $taxchart_to_use->id . '--' . $taxchart_to_use->rate;
+    $::form->{"project_id_$row"}                  = $order->globalproject_id;
+
+    $row++;
+  }
+
+  my $last_used_ap_chart               = SL::DB::Vendor->load_cached($::form->{vendor_id})->last_used_ap_chart;
+  $::form->{"AP_amount_chart_id_$row"} = $last_used_ap_chart->id if $last_used_ap_chart;
+  $::form->{rowcount}                  = $row;
+
+  update(
+    keep_rows_without_amount => 1,
+    dont_add_new_row         => 1,
+  );
+}
+
 sub setup_ap_search_action_bar {
   my %params = @_;
 
index 507ef71..6013a4f 100644 (file)
@@ -14,6 +14,17 @@ Mittelgroße neue Features:
 
 - Part Controller - neuer Tab mit Lagerinformationen - was ist wo gelagert
 
+- Neuer Workflow Lieferantenauftrag->Kreditorenbuchung: Für jedes Aufwandskonto
+  der Positionen im Lieferantenauftrag wird eine Zeile in der Kreditorenbuchung
+  erstellt. Gebucht wird standardmäßig auf des entsprechende Aufwandskonto. In
+  der Mandantenkonfiguration kann unter Standardkonten ein Konto ausgewählt
+  werden, auf das dann alle Zeilen gebucht werden.
+  Die Steuern werden übernommen, sofern diese für das ausgewählte Aufwandskonto
+  gültig sind. Ansonsten wird die Default-Steuer für das Aufwandskonto gesetzt.
+  Der Quellauftrag wird geschlossen, wenn der Betrag aller Kreditorenbuchungen,
+  die aus Workflows aus dem Quellauftrag entstanden sind, gleich dem Betrag
+  des Quellauftrags ist.
+
 Kleinere neue Features und Detailverbesserungen:
 
 - Mahnungen nach Abteilung filtern
index 4707575..66255b5 100755 (executable)
@@ -137,6 +137,7 @@ $self->{texts} = {
   'Account deleted!'            => 'Konto gelöscht!',
   'Account for fees'            => 'Konto für Gebühren',
   'Account for interest'        => 'Konto für Zinsen',
+  'Account for workflow from purchase order to ap transaction' => 'Konto für den Workflow von Lieferantenauftrag nach Kreditorenbuchung',
   'Account number'              => 'Kontonummer',
   'Account number not unique!'  => 'Kontonummer bereits vorhanden!',
   'Account number of the goal/source' => 'Ziel- oder Quellkonto',
index 1c0fc77..a1d4ee4 100644 (file)
@@ -137,6 +137,7 @@ $self->{texts} = {
   'Account deleted!'            => '',
   'Account for fees'            => '',
   'Account for interest'        => '',
+  'Account for workflow from purchase order to ap transaction' => '',
   'Account number'              => '',
   'Account number not unique!'  => '',
   'Account number of the goal/source' => '',
diff --git a/sql/Pg-upgrade2/defaults_workflow_po_ap_chart_id.sql b/sql/Pg-upgrade2/defaults_workflow_po_ap_chart_id.sql
new file mode 100644 (file)
index 0000000..93ae0d0
--- /dev/null
@@ -0,0 +1,5 @@
+-- @tag: defaults_workflow_po_ap_chart_id
+-- @description: Voreingestelltes Konto für Workflow Lieferantenauftrag -> Kreditorenbuchung
+-- @depends: release_3_5_4
+
+ALTER TABLE defaults ADD COLUMN workflow_po_ap_chart_id INTEGER;
index fd8c91e..bd52a51 100644 (file)
@@ -42,6 +42,8 @@
 
 <input type="hidden" name="paidaccounts" value="[% paidaccounts | html %]">
 
+[%- P.hidden_tag('convert_from_oe_id', convert_from_oe_id) -%]
+
 [% FOREACH i IN [1..paidaccounts] %]
   [% temp = "acc_trans_id_"_ i %]
   <input type="hidden" name="[% temp %]" value="[% $temp | html %]">
index ee9669c..1666678 100644 (file)
    <td>[% P.chart.picker('defaults.ar_chart_id', SELF.defaults.ar_chart_id, type='AR', choose=1, style=style) %]<td>
   </tr>
 
- </table>
+  <tr>
+   <td align="right">[% LxERP.t8("Account for workflow from purchase order to ap transaction") %]</td>
+   <td>[% P.chart.picker('defaults.workflow_po_ap_chart_id', SELF.defaults.workflow_po_ap_chart_id, type='AP_amount', choose=1, style=style) %]<td>
+  </tr>
+
+</table>
 </div>