]> wagnertech.de Git - mfinanz.git/commitdiff
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 caae2bb730a0f61797b6c525f3ee5e74c3ec4e23..b1608020b52d35cf343f05280999cc9c1999d229 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 7a7841d3cc39dda37d2e980144639c45e2f2becf..47ab4e7de4f0f45de9fa9e22fa82a8f3fcb9f953 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 c0f6a44111cd62b7971b4d5c5312dedb08c54b9c..e588b3aeb8a32906ba18f1dafedaaf59ad0db9ef 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 49f6f7a638b2a6019f46ed604c87a4e25c6f02d6..a5d20da874c8234022a84bf8035a6fa38c06a5a0 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 507ef71d67edd7977ae7741ffeb63cbcf17969e8..6013a4f0a0cfa19dfd7c28cb7dc9dabb0dfc3dfe 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 470757567e3c22ef8945c8f62b221e68ed7b82e4..66255b544df43fc84a66632ffb98b2da6334cd6e 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 1c0fc77126e031e72d684ccfddb38df8f1b9f5e4..a1d4ee490f7c0d135723fb1e0911de9034968334 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 fd8c91e544898ae26afa905e0e8a01810c829c61..bd52a5189940a8c96f12283e3d22a71aaf3e82bf 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 ee9669c77a5c55d7605b5ef54a80976e26bebb8a..16666782c18e2e14b30160662dac122c4f7b9196 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>