Revert "retrieve_items in IR.pm bzgl. notes überarbeitet"
[kivitendo-erp.git] / SL / IR.pm
index ee676cb..55987af 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -102,9 +102,12 @@ sub post_invoice {
   my %price_factors = map { $_->{id} => $_->{factor} } @{ $form->{ALL_PRICE_FACTORS} };
   my $price_factor;
 
+  my @processed_invoice_ids;
   for my $i (1 .. $form->{rowcount}) {
     next unless $form->{"id_$i"};
 
+    my $position = $i;
+
     $form->{"qty_$i"}  = $form->parse_amount($myconfig, $form->{"qty_$i"});
     $form->{"qty_$i"} *= -1 if $form->{storno};
 
@@ -144,17 +147,8 @@ sub post_invoice {
     map { $taxrate += $form->{"${_}_rate"} } @taxaccounts;
 
     $price_factor = $price_factors{ $form->{"price_factor_id_$i"} } || 1;
-    #####################################################################
-    # das ist aus IS.pm kopiert. schlimm. jb 7.10.2009
-    # ich würde mir wünschen, dass diese vier stellen zusammengefasst werden
-    # ... vier stellen = (einkauf + verkauf) * (maske + backend)
-    # ansonsten stolpert man immer wieder viermal statt einmal heftig
-    # und auch das undo discount formatting ist nicht besonders wartungsfreundlich
-
-    # keine ahnung wofür das in IS.pm gemacht wird:
-    #      my ($dec) = ($fxsellprice =~ /\.(\d+)/);
-    #  $dec = length $dec;
-    #  my $decimalplaces = ($dec > 2) ? $dec : 2;
+    # copied from IS.pm, with some changes (no decimalplaces corrections here etc)
+    # TODO maybe use PriceTaxCalculation or something like this for backends (IR.pm / IS.pm)
 
     # undo discount formatting
     $form->{"discount_$i"} = $form->parse_amount($myconfig, $form->{"discount_$i"}) / 100;
@@ -234,17 +228,17 @@ sub post_invoice {
            ORDER BY transdate|;
            # ORDER BY transdate guarantees FIFO
 
-# sold two items without having bought them yet, example result of query:
-# id | qty | allocated | trans_id | inventory_accno_id | expense_accno_id | transdate
-# ---+-----+-----------+----------+--------------------+------------------+------------
-#  9 |   2 |         0 |        9 |                 15 |              151 | 2011-01-05
+      # sold two items without having bought them yet, example result of query:
+      # id | qty | allocated | trans_id | inventory_accno_id | expense_accno_id | transdate
+      # ---+-----+-----------+----------+--------------------+------------------+------------
+      #  9 |   2 |         0 |        9 |                 15 |              151 | 2011-01-05
 
-# base_qty + allocated > 0 if article has already been sold but not bought yet
+      # base_qty + allocated > 0 if article has already been sold but not bought yet
 
-# select qty,allocated,base_qty,sellprice from invoice where trans_id = 9;
-#  qty | allocated | base_qty | sellprice
-# -----+-----------+----------+------------
-#    2 |         0 |        2 | 1000.00000
+      # select qty,allocated,base_qty,sellprice from invoice where trans_id = 9;
+      #  qty | allocated | base_qty | sellprice
+      # -----+-----------+----------+------------
+      #    2 |         0 |        2 | 1000.00000
 
       $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{"id_$i"}));
 
@@ -259,7 +253,7 @@ sub post_invoice {
 
           if ($ref->{allocated} < 0) {
 
-# we have an entry for it already, adjust amount
+            # we have an entry for it already, adjust amount
             $form->update_balance($dbh, "acc_trans", "amount",
                 qq|    (trans_id = $ref->{trans_id})
                 AND (chart_id = $ref->{inventory_accno_id})
@@ -292,7 +286,7 @@ sub post_invoice {
                        $ref->{inventory_accno_id});
             do_query($form, $dbh, $query, @values);
 
-# add expense
+            # add expense
             $query = qq|INSERT INTO acc_trans (trans_id, chart_id, amount, transdate, taxkey, tax_id, chart_link) VALUES (?, ?, ?, ?,
                                 (SELECT taxkey_id
                                  FROM taxkeys
@@ -375,33 +369,67 @@ sub post_invoice {
 
     next if $payments_only;
 
-    # save detail record in invoice table
-    my ($invoice_id) = selectfirst_array_query($form, $dbh, qq|SELECT nextval('invoiceid')|);
+    CVar->get_non_editable_ic_cvars(form               => $form,
+                                    dbh                => $dbh,
+                                    row                => $i,
+                                    sub_module         => 'invoice',
+                                    may_converted_from => ['delivery_order_items', 'orderitems', 'invoice']);
+
+    if (!$form->{"invoice_id_$i"}) {
+      # there is no persistent id, therefore create one with all necessary constraints
+      my $q_invoice_id = qq|SELECT nextval('invoiceid')|;
+      my $h_invoice_id = prepare_query($form, $dbh, $q_invoice_id);
+      do_statement($form, $h_invoice_id, $q_invoice_id);
+      $form->{"invoice_id_$i"}  = $h_invoice_id->fetchrow_array();
+      my $q_create_invoice_id = qq|INSERT INTO invoice (id, trans_id, position, parts_id) values (?, ?, ?, ?)|;
+      do_query($form, $dbh, $q_create_invoice_id, conv_i($form->{"invoice_id_$i"}),
+               conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}));
+      $h_invoice_id->finish();
+    }
 
-    $query =
-      qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty, base_qty,
-                              sellprice, fxsellprice, discount, allocated, unit, deliverydate,
-                              project_id, serialnumber, price_factor_id, price_factor, marge_price_factor,
-                              active_price_source, active_discount_source)
-         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|;
-    @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}),
+      # save detail record in invoice table
+      $query = <<SQL;
+        UPDATE invoice SET trans_id = ?, position = ?, parts_id = ?, description = ?, longdescription = ?, qty = ?, base_qty = ?,
+                           sellprice = ?, fxsellprice = ?, discount = ?, allocated = ?, unit = ?, deliverydate = ?,
+                           project_id = ?, serialnumber = ?, price_factor_id = ?,
+                           price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?,
+                           active_price_source = ?, active_discount_source = ?
+        WHERE id = ?
+SQL
+
+    @values = (conv_i($form->{id}), conv_i($position), conv_i($form->{"id_$i"}),
                $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"} * -1,
                $baseqty * -1, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated,
                $form->{"unit_$i"}, conv_date($form->{deliverydate}),
                conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"},
                conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
-               conv_i($form->{"active_price_source_$i"}), conv_i($form->{"active_discount_source_$i"}),
-               );
+               $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
+               conv_i($form->{"invoice_id_$i"}));
     do_query($form, $dbh, $query, @values);
+    push @processed_invoice_ids, $form->{"invoice_id_$i"};
 
     CVar->save_custom_variables(module       => 'IC',
                                 sub_module   => 'invoice',
-                                trans_id     => $invoice_id,
+                                trans_id     => $form->{"invoice_id_$i"},
                                 configs      => $ic_cvar_configs,
                                 variables    => $form,
                                 name_prefix  => 'ic_',
                                 name_postfix => "_$i",
                                 dbh          => $dbh);
+
+    # link previous items with invoice items See IS.pm (no credit note -> no invoice item)
+    foreach (qw(delivery_order_items orderitems)) {
+      if (!$form->{useasnew} && $form->{"converted_from_${_}_id_$i"}) {
+        RecordLinks->create_links('dbh'        => $dbh,
+                                  'mode'       => 'ids',
+                                  'from_table' => $_,
+                                  'from_ids'   => $form->{"converted_from_${_}_id_$i"},
+                                  'to_table'   => 'invoice',
+                                  'to_id'      => $form->{"invoice_id_$i"},
+        );
+      }
+      delete $form->{"converted_from_${_}_id_$i"};
+    }
   }
 
   $h_item_unit->finish();
@@ -678,13 +706,6 @@ sub post_invoice {
   my $taxzone_id         = $form->{taxzone_id} * 1;
   $taxzone_id = SL::DB::Manager::TaxZone->get_default->id unless SL::DB::Manager::TaxZone->find_by(id => $taxzone_id);
 
-  # Seit neuestem wird die department_id schon übergeben UND $form->department nicht mehr
-  # korrekt zusammengebaut. Sehr wahrscheinlich beim Umstieg auf T8 kaputt gegangen
-  # Ich lass den Code von 2005 erstmal noch stehen ;-) jb 03-2011
-  # copy & paste von IS.pm
-  if (!$form->{department_id}){
-    $form->{department_id} = (split /--/, $form->{department})[1];
-  }
   $form->{invnumber}     = $form->{id} unless $form->{invnumber};
 
   # save AP record
@@ -701,7 +722,7 @@ sub post_invoice {
                 $form->{invnumber},          $form->{ordnumber},           $form->{quonumber},      conv_date($form->{invdate}),
       conv_date($form->{orddate}), conv_date($form->{quodate}),     conv_i($form->{vendor_id}),               $amount,
                 $netamount,                  $form->{paid},      conv_date($form->{duedate}),
-            '1',                             $taxzone_id,                  $form->{notes},          $form->{taxincluded} ? 't' : 'f',
+            '1',                             $taxzone_id, $restricter->process($form->{notes}),               $form->{taxincluded} ? 't' : 'f',
                 $form->{intnotes},           conv_i($form->{storno_id}),     $form->{storno}      ? 't' : 'f',
          conv_i($form->{cp_id}),      conv_i($form->{employee_id}), conv_i($form->{department_id}), conv_i($form->{delivery_term_id}),
          conv_i($form->{globalproject_id}),
@@ -737,14 +758,16 @@ sub post_invoice {
   Common::webdav_folder($form);
 
   # Link this record to the records it was created from.
-  RecordLinks->create_links('dbh'        => $dbh,
-                            'mode'       => 'ids',
-                            'from_table' => 'oe',
-                            'from_ids'   => $form->{convert_from_oe_ids},
-                            'to_table'   => 'ap',
-                            'to_id'      => $form->{id},
-    );
-  delete $form->{convert_from_oe_ids};
+  if ($form->{convert_from_oe_ids}) {
+    RecordLinks->create_links('dbh'        => $dbh,
+                              'mode'       => 'ids',
+                              'from_table' => 'oe',
+                              'from_ids'   => $form->{convert_from_oe_ids},
+                              'to_table'   => 'ap',
+                              'to_id'      => $form->{id},
+      );
+    delete $form->{convert_from_oe_ids};
+  }
 
   my @convert_from_do_ids = map { $_ * 1 } grep { $_ } split m/\s+/, $form->{convert_from_do_ids};
   if (scalar @convert_from_do_ids) {
@@ -765,8 +788,20 @@ sub post_invoice {
                                'arap_id' => $form->{id},
                                'table'   => 'ap',);
 
+  # search for orphaned invoice items
+  $query  = sprintf 'SELECT id FROM invoice WHERE trans_id = ? AND NOT id IN (%s)', join ', ', ("?") x scalar @processed_invoice_ids;
+  @values = (conv_i($form->{id}), map { conv_i($_) } @processed_invoice_ids);
+  my @orphaned_ids = map { $_->{id} } selectall_hashref_query($form, $dbh, $query, @values);
+  if (scalar @orphaned_ids) {
+    # clean up invoice items
+    $query  = sprintf 'DELETE FROM invoice WHERE id IN (%s)', join ', ', ("?") x scalar @orphaned_ids;
+    do_query($form, $dbh, $query, @orphaned_ids);
+  }
+
   # safety check datev export
   if ($::instance_conf->get_datev_check_on_purchase_invoice) {
+    # if we need department for kostenstelle in DATEV check
+    $form->{department} = SL::DB::Manager::Department->find_by(id => $form->{department_id})->description if $form->{department_id};
     my $transdate = $::form->{invdate} ? DateTime->from_lxoffice($::form->{invdate}) : undef;
     $transdate  ||= DateTime->today;
 
@@ -774,8 +809,6 @@ sub post_invoice {
       exporttype => DATEV_ET_BUCHUNGEN,
       format     => DATEV_FORMAT_KNE,
       dbh        => $dbh,
-      from       => $transdate,
-      to         => $transdate,
       trans_id   => $form->{id},
     );
 
@@ -842,17 +875,20 @@ sub reverse_invoice {
         #adjust allocated
         $form->update_balance($dbh, "invoice", "allocated", qq|id = $pthref->{id}|, $qty);
 
-        $form->update_balance($dbh, "acc_trans", "amount",
-                              qq|    (trans_id = $pthref->{trans_id})
-                                 AND (chart_id = $ref->{expense_accno_id})
-                                 AND (transdate = '$pthref->{transdate}')|,
-                              $amount);
+        if  ( $::instance_conf->get_inventory_system eq 'perpetual' ) {
 
-        $form->update_balance($dbh, "acc_trans", "amount",
-                              qq|    (trans_id = $pthref->{trans_id})
-                                 AND (chart_id = $ref->{inventory_accno_id})
-                                 AND (transdate = '$pthref->{transdate}')|,
-                              $amount * -1);
+          $form->update_balance($dbh, "acc_trans", "amount",
+                                qq|    (trans_id = $pthref->{trans_id})
+                                   AND (chart_id = $ref->{expense_accno_id})
+                                   AND (transdate = '$pthref->{transdate}')|,
+                                $amount);
+
+          $form->update_balance($dbh, "acc_trans", "amount",
+                                qq|    (trans_id = $pthref->{trans_id})
+                                   AND (chart_id = $ref->{inventory_accno_id})
+                                   AND (transdate = '$pthref->{transdate}')|,
+                                $amount * -1);
+        }
 
         last if (($ref->{allocated} -= $qty) <= 0);
       }
@@ -866,10 +902,6 @@ sub reverse_invoice {
   $query = qq|DELETE FROM acc_trans WHERE trans_id = ?|;
   do_query($form, $dbh, $query, $id);
 
-  # delete invoice entries
-  $query = qq|DELETE FROM invoice WHERE trans_id = ?|;
-  do_query($form, $dbh, $query, $id);
-
   $query = qq|DELETE FROM shipto WHERE (trans_id = ?) AND (module = 'AP')|;
   do_query($form, $dbh, $query, $id);
 
@@ -886,13 +918,20 @@ sub delete_invoice {
 
   &reverse_invoice($dbh, $form);
 
+  my @values = (conv_i($form->{id}));
+
   # delete zero entries
+  # wtf? use case for this?
   $query = qq|DELETE FROM acc_trans WHERE amount = 0|;
   do_query($form, $dbh, $query);
 
-  # delete AP record
-  $query = qq|DELETE FROM ap WHERE id = ?|;
-  do_query($form, $dbh, $query, conv_i($form->{id}));
+
+  my @queries = (
+    qq|DELETE FROM invoice WHERE trans_id = ?|,
+    qq|DELETE FROM ap WHERE id = ?|,
+  );
+
+  map { do_query($form, $dbh, $_, @values) } @queries;
 
   my $rc = $dbh->commit;
   $dbh->disconnect;
@@ -950,12 +989,15 @@ sub retrieve_invoice {
   $query = qq|SELECT cp_id, invnumber, transdate AS invdate, duedate,
                 orddate, quodate, globalproject_id,
                 ordnumber, quonumber, paid, taxincluded, notes, taxzone_id, storno, gldate,
+                mtime, itime,
                 intnotes, (SELECT cu.name FROM currencies cu WHERE cu.id=ap.currency_id) AS currency, direct_debit,
                 delivery_term_id
               FROM ap
               WHERE id = ?|;
   $ref = selectfirst_hashref_query($form, $dbh, $query, conv_i($form->{id}));
   map { $form->{$_} = $ref->{$_} } keys %$ref;
+  $form->{mtime} = $form->{itime} if !$form->{mtime};
+  $form->{lastmtime} = $form->{mtime};
 
   $form->{exchangerate}  = $form->get_exchangerate($dbh, $form->{currency}, $form->{invdate}, "sell");
 
@@ -992,7 +1034,7 @@ sub retrieve_invoice {
 
         WHERE i.trans_id = ?
 
-        ORDER BY i.id|;
+        ORDER BY i.position|;
   $sth = prepare_execute_query($form, $dbh, $query, conv_i($form->{id}));
 
   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
@@ -1003,7 +1045,6 @@ sub retrieve_invoice {
                                            trans_id   => $ref->{invoice_id},
                                           );
     map { $ref->{"ic_cvar_$_->{name}"} = $_->{value} } @{ $cvars };
-    delete $ref->{invoice_id};
 
     map({ delete($ref->{$_}); } qw(inventory_accno inventory_new_chart inventory_valid)) if !$ref->{"part_inventory_accno_id"};
     delete($ref->{"part_inventory_accno_id"});
@@ -1096,11 +1137,11 @@ sub get_vendor {
   my $query =
     qq|SELECT
          v.id AS vendor_id, v.name AS vendor, v.discount as vendor_discount,
-         v.creditlimit, v.terms, v.notes AS intnotes,
+         v.creditlimit, v.notes AS intnotes,
          v.email, v.cc, v.bcc, v.language_id, v.payment_id, v.delivery_term_id,
          v.street, v.zipcode, v.city, v.country, v.taxzone_id, cu.name AS curr, v.direct_debit,
          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
-         b.description AS business
+         b.discount AS tradediscount, b.description AS business
        FROM vendor v
        LEFT JOIN business b       ON (b.id = v.business_id)
        LEFT JOIN payment_terms pt ON (v.payment_id = pt.id)
@@ -1612,5 +1653,4 @@ sub get_duedate {
   return $duedate;
 }
 
-
 1;