Auftrags-Controller: Positions-Update aus Artikel-Stamm
authorBernd Bleßmann <bernd@kivitendo-premium.de>
Mon, 19 Nov 2018 15:38:27 +0000 (16:38 +0100)
committerBernd Bleßmann <bernd@kivitendo-premium.de>
Tue, 1 Oct 2019 15:21:57 +0000 (17:21 +0200)
SL/Controller/Order.pm
css/kivitendo/main.css
css/lx-office-erp/main.css
image/rotate_cw.svg [new file with mode: 0644]
js/kivi.Order.js
locale/de/all
locale/en/all
templates/webpages/order/tabs/_row.html
templates/webpages/order/tabs/basic_data.html

index 47ab4e7..9b4c1bf 100644 (file)
@@ -1015,6 +1015,47 @@ sub action_load_second_rows {
   $self->js->render();
 }
 
+# update description, notes and sellprice from master data
+sub action_update_row_from_master_data {
+  my ($self) = @_;
+
+  foreach my $item_id (@{ $::form->{item_ids} }) {
+    my $idx  = first_index { $_ eq $item_id } @{ $::form->{orderitem_ids} };
+    my $item = $self->order->items_sorted->[$idx];
+
+    $item->description($item->part->description);
+    $item->longdescription($item->part->notes);
+
+    my $price_source = SL::PriceSource->new(record_item => $item, record => $self->order);
+
+    my $price_src;
+    if ($item->part->is_assortment) {
+    # add assortment items with price 0, as the components carry the price
+      $price_src = $price_source->price_from_source("");
+      $price_src->price(0);
+    } else {
+      $price_src = $price_source->best_price
+                 ? $price_source->best_price
+                 : $price_source->price_from_source("");
+      $price_src->price(0) if !$price_source->best_price;
+    }
+
+    $item->sellprice($price_src->price);
+    $item->active_price_source($price_src);
+
+    $self->js
+      ->run('kivi.Order.update_sellprice', $item_id, $item->sellprice_as_number)
+      ->val('.row_entry:has(#item_' . $item_id . ') [name = "order.orderitems[].description"]', $item->description)
+      ->val('.row_entry:has(#item_' . $item_id . ') [name = "order.orderitems[].longdescription"]', $item->longdescription);
+  }
+
+  $self->recalc();
+  $self->js_redisplay_line_values;
+  $self->js_redisplay_amounts_and_taxes;
+
+  $self->js->render();
+}
+
 sub js_load_second_row {
   my ($self, $item, $item_id, $do_parse) = @_;
 
index 8611462..0c22493 100644 (file)
@@ -531,6 +531,15 @@ span.toggle_selected {
     max-width: 16px;
     max-height: 16px;
 }
+#update_from_master {
+    cursor: pointer;
+    display: block;
+    max-width: 16px;
+    max-height: 16px;
+}
+#update_from_master:hover {
+    background: #ddd;
+}
 
 /* Bank transactions */
 #bank_transactions_proposals .invoice_number_highlight a,
index 8cf9a67..a2a4ad0 100644 (file)
@@ -525,6 +525,15 @@ a.red {
     max-width: 16px;
     max-height: 16px;
 }
+#update_from_master {
+    cursor: pointer;
+    display: block;
+    max-width: 16px;
+    max-height: 16px;
+}
+#update_from_master:hover {
+    background: darkgrey;
+}
 
 /* Bank transactions */
 #bank_transactions_proposals .invoice_number_highlight a,
diff --git a/image/rotate_cw.svg b/image/rotate_cw.svg
new file mode 100644 (file)
index 0000000..fc747b3
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) --><svg height="60.0000000" id="svg1" inkscape:version="0.38.1" sodipodi:docbase="/home/danny/flat/scalable/actions" sodipodi:docname="rotate_cw.svg" sodipodi:version="0.32" version="1.0" width="60.0000000" x="0" xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:xlink="http://www.w3.org/1999/xlink" y="0">
+  <metadata>
+    <rdf:RDF xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+      <cc:Work rdf:about="">
+        <dc:title>Part of the Flat Icon Collection (Wed Aug 25 23:29:46 2004)</dc:title>
+        <dc:description></dc:description>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>hash</rdf:li>
+            <rdf:li></rdf:li>
+            <rdf:li>action</rdf:li>
+            <rdf:li>computer</rdf:li>
+            <rdf:li>icons</rdf:li>
+            <rdf:li>theme</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+        <dc:publisher>
+          <cc:Agent rdf:about="http://www.openclipart.org">
+            <dc:title>Danny Allen</dc:title>
+          </cc:Agent>
+        </dc:publisher>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Danny Allen</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Danny Allen</dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:date></dc:date>
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+        <cc:license rdf:resource="http://web.resource.org/cc/PublicDomain"/>
+        <dc:language>en</dc:language>
+      </cc:Work>
+      <cc:License rdf:about="http://web.resource.org/cc/PublicDomain">
+        <cc:permits rdf:resource="http://web.resource.org/cc/Reproduction"/>
+        <cc:permits rdf:resource="http://web.resource.org/cc/Distribution"/>
+        <cc:permits rdf:resource="http://web.resource.org/cc/DerivativeWorks"/>
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:cx="33.984503" inkscape:cy="18.129014" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="685" inkscape:window-width="1016" inkscape:window-x="0" inkscape:window-y="0" inkscape:zoom="6.9465337" pagecolor="#ffffff" showguides="true" snaptoguides="true"/>
+  <defs id="defs3"/>
+  <path d="M 7.9315240,4.7740988 C 37.959278,18.463222 30.893925,41.646412 30.893925,41.867205 L 20.958272,41.867205 L 36.965712,55.225139 L 50.434041,41.756809 L 40.387993,41.756809 C 40.387993,41.756809 47.894929,9.6315299 7.9315240,4.7740988 z " id="path968" sodipodi:nodetypes="ccccccc" sodipodi:stroke-cmyk="(0 0 0 0.8)" style="font-size:12.000000;fill:#7f7f7f;fill-rule:evenodd;stroke:#333333;stroke-width:3.1249931;stroke-linecap:round;stroke-linejoin:round;" transform="translate(1.079675,0.000000)"/>
+</svg>
index 94de1bc..3421f9f 100644 (file)
@@ -725,6 +725,34 @@ namespace('kivi.Order', function(ns) {
     return true;
   };
 
+  ns.update_row_from_master_data = function(clicked) {
+    var row = $(clicked).parents("tbody").first();
+    var item_id_dom = $(row).find('[name="orderitem_ids[+]"]');
+
+    var data = $('#order_form').serializeArray();
+    data.push({ name: 'action', value: 'Order/update_row_from_master_data' });
+    data.push({ name: 'item_ids[]', value: item_id_dom.val() });
+
+    $.post("controller.pl", data, kivi.eval_json_result);
+  };
+
+  ns.update_all_rows_from_master_data = function() {
+    var item_ids = $.map($('.row_entry'), function(elt) {
+      var item_id = $(elt).find('[name="orderitem_ids[+]"]').val();
+      return { name: 'item_ids[]', value: item_id };
+    });
+
+    if (item_ids.length == 0) {
+      return;
+    }
+
+    var data = $('#order_form').serializeArray();
+    data.push({ name: 'action', value: 'Order/update_row_from_master_data' });
+    data = data.concat(item_ids);
+
+    $.post("controller.pl", data, kivi.eval_json_result);
+  };
+
   ns.show_calculate_qty_dialog = function(clicked) {
     var row        = $(clicked).parents("tbody").first();
     var input_id   = $(row).find('[name="order.orderitems[].qty_as_number"]').attr('id');
index 380aafd..949125b 100755 (executable)
@@ -312,6 +312,8 @@ $self->{texts} = {
   'Apr'                         => 'Apr',
   'April'                       => 'April',
   'Ar aging on %s'              => 'Offene Forderungen zum %s',
+  'Are you sure to update all positions from master data?' => 'Alle Positionen aus den Stammdaten aktualisieren?',
+  'Are you sure to update this position from master data?' => 'Diese Position aus den Stammdaten aktualisieren?',
   'Are you sure you want to delete Invoice Number' => 'Soll die Rechnung mit folgender Nummer wirklich gelöscht werden:',
   'Are you sure you want to delete this letter?' => 'Sind Sie sicher, dass Sie diesen Brief löschen wollen?',
   'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
@@ -3730,6 +3732,7 @@ $self->{texts} = {
   'Update Prices'               => 'Preise aktualisieren',
   'Update SKR04: new tax account 3804 (19%)' => 'Update SKR04: neues Steuerkonto 3804 (19%) für innergemeinschaftlichen Erwerb',
   'Update customer using billing address' => 'Kunde mit Shop-Rechnungsadresse überschreiben',
+  'Update from master data'     => 'Aktualisieren aus Stammdaten',
   'Update prices'               => 'Preise aktualisieren',
   'Update prices of existing entries' => 'Preise von vorhandenen Artikeln aktualisieren',
   'Update prices of existing entries / skip non-existent' => 'Preise von vorhandenen Artikel aktualisieren / Nicht vorhandene überspringen',
index a28c655..bbd4098 100644 (file)
@@ -312,6 +312,8 @@ $self->{texts} = {
   'Apr'                         => '',
   'April'                       => '',
   'Ar aging on %s'              => '',
+  'Are you sure to update all positions from master data?' => '',
+  'Are you sure to update this position from master data?' => '',
   'Are you sure you want to delete Invoice Number' => '',
   'Are you sure you want to delete this letter?' => '',
   'Are you sure you want to remove the marked entries from the queue?' => '',
@@ -3729,6 +3731,7 @@ $self->{texts} = {
   'Update Prices'               => '',
   'Update SKR04: new tax account 3804 (19%)' => '',
   'Update customer using billing address' => '',
+  'Update from master data'     => '',
   'Update prices'               => '',
   'Update prices of existing entries' => '',
   'Update prices of existing entries / skip non-existent' => '',
index 69c8eb5..20755d2 100644 (file)
                        LxERP.t8("X"),
                        confirm=LxERP.t8("Are you sure?")) %]
     </td>
+    <td align="center">
+      [%- L.img_tag(src="image/rotate_cw.svg",
+                    alt=LxERP.t8('Update from master data'),
+                    title= LxERP.t8('Update from master data'),
+                    onclick="if (!confirm('" _ LxERP.t8("Are you sure to update this position from master data?") _ "')) return false; kivi.Order.update_row_from_master_data(this);",
+                    id='update_from_master') %]
+    </td>
     <td>
       <div name="partnumber">[% HTML.escape(ITEM.part.partnumber) %]</div>
     </td>
index 1173580..26aedd5 100644 (file)
                 <th class="listheading" nowrap width="3" >[%- 'position'     | $T8 %] </th>
                 <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/updown.png" alt="[%- LxERP.t8('reorder item') %]"></th>
                 <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/close.png" alt="[%- LxERP.t8('delete item') %]"></th>
+                <th class="listheading" style='text-align:center' nowrap width="1">
+                  [%- L.img_tag(src="image/rotate_cw.svg",
+                                alt=LxERP.t8('Update from master data'),
+                                title= LxERP.t8('Update from master data'),
+                                onclick="if (!confirm('" _ LxERP.t8("Are you sure to update all positions from master data?") _ "')) return false; kivi.Order.update_all_rows_from_master_data();",
+                                id='update_from_master') %]
+                </th>
                 <th id="partnumber_header_id"   class="listheading" nowrap width="15"><a href='#' onClick='javascript:kivi.Order.reorder_items("partnumber")'> [%- 'Partnumber'  | $T8 %]</a></th>
                 [%- IF SELF.search_cvpartnumber -%]
                 <th id="cvpartnumber_header_id" class="listheading" nowrap width="15"><a href='#' onClick='javascript:kivi.Order.reorder_items("cvpartnumber")' > [%- SELF.cv == "customer" ? LxERP.t8('Customer Part Number') : LxERP.t8('Model') %]</a></th>