From a72154b9861824f66e79c7ee8c15aa3bd41c8b23 Mon Sep 17 00:00:00 2001
From: Pascal Neub <p.neub@adito.de>
Date: Fri, 5 Nov 2021 14:41:29 +0100
Subject: [PATCH] [Projekt: xRM-Sales][TicketNr.: 2001370][Angebot/Beleg Summen
 berechnen Aktion]

---
 entity/Offeritem_entity/Offeritem_entity.aod  |  5 ++
 .../calculateprices/onActionProcess.js        |  8 +++
 .../recordcontainers/db/onDBInsert.js         | 33 ++--------
 entity/Order_entity/Order_entity.aod          |  3 +
 entity/Orderitem_entity/Orderitem_entity.aod  |  5 ++
 .../calculateprices/onActionProcess.js        |  8 +++
 .../recordcontainers/db/onDBInsert.js         | 29 ++-------
 .../_____LANGUAGE_de/_____LANGUAGE_de.aod     |  4 ++
 .../OrderitemFilter_view.aod                  |  1 +
 process/OfferOrder_lib/process.js             | 61 ++++++++++++++++++-
 process/Offer_lib/process.js                  | 31 +++++++++-
 process/Order_lib/process.js                  | 30 +++++++++
 12 files changed, 163 insertions(+), 55 deletions(-)
 create mode 100644 entity/Offeritem_entity/entityfields/group/children/calculateprices/onActionProcess.js
 create mode 100644 entity/Orderitem_entity/entityfields/group/children/calculateprices/onActionProcess.js

diff --git a/entity/Offeritem_entity/Offeritem_entity.aod b/entity/Offeritem_entity/Offeritem_entity.aod
index 87a0d3f6cae..f62154dc493 100644
--- a/entity/Offeritem_entity/Offeritem_entity.aod
+++ b/entity/Offeritem_entity/Offeritem_entity.aod
@@ -284,6 +284,11 @@
           <state>DISABLED</state>
           <stateProcess>%aditoprj%/entity/Offeritem_entity/entityfields/group/children/movedown/stateProcess.js</stateProcess>
         </entityActionField>
+        <entityActionField>
+          <name>calculatePrices</name>
+          <title>Calculate sum</title>
+          <onActionProcess>%aditoprj%/entity/Offeritem_entity/entityfields/group/children/calculateprices/onActionProcess.js</onActionProcess>
+        </entityActionField>
       </children>
     </entityActionGroup>
     <entityField>
diff --git a/entity/Offeritem_entity/entityfields/group/children/calculateprices/onActionProcess.js b/entity/Offeritem_entity/entityfields/group/children/calculateprices/onActionProcess.js
new file mode 100644
index 00000000000..84e65c7263c
--- /dev/null
+++ b/entity/Offeritem_entity/entityfields/group/children/calculateprices/onActionProcess.js
@@ -0,0 +1,8 @@
+import("system.neon");
+import("system.vars");
+import("Offer_lib");
+
+var oiUtils = new OfferItemUtils(vars.get("$field.OFFER_ID"));
+oiUtils.recalculatePrices(vars.get("$sys.selection"));
+OfferItemUtils.updateOfferNet(vars.get("$field.OFFER_ID"));
+neon.refreshAll();
diff --git a/entity/Offeritem_entity/recordcontainers/db/onDBInsert.js b/entity/Offeritem_entity/recordcontainers/db/onDBInsert.js
index f9d76609118..1c643431550 100644
--- a/entity/Offeritem_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/Offeritem_entity/recordcontainers/db/onDBInsert.js
@@ -1,40 +1,17 @@
 import("system.db");
-import("system.entities");
-import("system.neon");
 import("system.vars");
 import("Offer_lib");
-import("Product_lib");
-import("Sql_lib");
 
 var rowdata = vars.get("$local.rowdata");
 var oid = rowdata["OFFERITEM.OFFER_ID"];
 if(oid != "")
 {
-    var discount = vars.exists("$param.Discount_param") ? vars.get("$param.Discount_param"): "";
-    var oiUtils = new OfferItemUtils(rowdata["OFFERITEM.OFFER_ID"]);    
+    OfferItemUtils.updateOfferNet(oid, vars.get("$param.Discount_param"));
     
-    var insertStatements = JSON.parse(vars.get("$field.itemInsertStatements"));//insert statements get set by insertPartsList in the onvalueChanges of product_id and quantity
-    var statements = [];
-    
-    //update offer price
-    var vals = oiUtils.getNetAndVat();
-    var discountedVals = OfferItemUtils.getDiscountedNet(null, oid,  discount);
-    if(discountedVals)
-    {
-        statements.push(
-            newWhere("OFFER.OFFERID", rowdata["OFFERITEM.OFFER_ID"]).buildUpdateStatement({
-                    "NET": vals[0],
-                    "VAT": vals[1],
-                    "DISCOUNTED_NET": discountedVals[0],
-                    "DISCOUNTED_VAT": discountedVals[1]
-                })
-            )
-    }
-
+    //insert statements get set by insertPartsList in the onvalueChanges of product_id and quantity
+    var insertStatements = JSON.parse(vars.get("$field.itemInsertStatements"));
     if(insertStatements)
     {
-        statements = statements.concat(insertStatements);
+        db.execute(insertStatements, "Data_alias", 10000000);
     }
-
-    db.execute(statements, "Data_alias", 10000000);
-}
\ No newline at end of file
+}
diff --git a/entity/Order_entity/Order_entity.aod b/entity/Order_entity/Order_entity.aod
index 399419f16b3..009030c5970 100644
--- a/entity/Order_entity/Order_entity.aod
+++ b/entity/Order_entity/Order_entity.aod
@@ -4,6 +4,9 @@
   <title>Receipt</title>
   <majorModelMode>DISTRIBUTED</majorModelMode>
   <documentation>%aditoprj%/entity/Order_entity/documentation.adoc</documentation>
+  <siblings>
+    <element>Orderitem_entity</element>
+  </siblings>
   <grantUpdateProcess>%aditoprj%/entity/Order_entity/grantUpdateProcess.js</grantUpdateProcess>
   <grantDeleteProcess>%aditoprj%/entity/Order_entity/grantDeleteProcess.js</grantDeleteProcess>
   <contentTitleProcess>%aditoprj%/entity/Order_entity/contentTitleProcess.js</contentTitleProcess>
diff --git a/entity/Orderitem_entity/Orderitem_entity.aod b/entity/Orderitem_entity/Orderitem_entity.aod
index b6f5ec9513c..5fc63065077 100644
--- a/entity/Orderitem_entity/Orderitem_entity.aod
+++ b/entity/Orderitem_entity/Orderitem_entity.aod
@@ -261,6 +261,11 @@
           <state>DISABLED</state>
           <stateProcess>%aditoprj%/entity/Orderitem_entity/entityfields/group/children/movedown/stateProcess.js</stateProcess>
         </entityActionField>
+        <entityActionField>
+          <name>calculatePrices</name>
+          <title>Calculate sum</title>
+          <onActionProcess>%aditoprj%/entity/Orderitem_entity/entityfields/group/children/calculateprices/onActionProcess.js</onActionProcess>
+        </entityActionField>
       </children>
     </entityActionGroup>
     <entityParameter>
diff --git a/entity/Orderitem_entity/entityfields/group/children/calculateprices/onActionProcess.js b/entity/Orderitem_entity/entityfields/group/children/calculateprices/onActionProcess.js
new file mode 100644
index 00000000000..83922ee37c9
--- /dev/null
+++ b/entity/Orderitem_entity/entityfields/group/children/calculateprices/onActionProcess.js
@@ -0,0 +1,8 @@
+import("system.neon");
+import("system.vars");
+import("Order_lib");
+
+var oiUtils = new OrderItemUtils(vars.get("$field.SALESORDER_ID"));
+oiUtils.recalculatePrices(vars.get("$sys.selection"));
+OrderItemUtils.updateOrderNet(vars.get("$field.SALESORDER_ID"));
+neon.refreshAll();
diff --git a/entity/Orderitem_entity/recordcontainers/db/onDBInsert.js b/entity/Orderitem_entity/recordcontainers/db/onDBInsert.js
index 1e9a6cdb99b..b5ccd5df970 100644
--- a/entity/Orderitem_entity/recordcontainers/db/onDBInsert.js
+++ b/entity/Orderitem_entity/recordcontainers/db/onDBInsert.js
@@ -10,31 +10,12 @@ var rowdata = vars.get("$local.rowdata");
 var oid = rowdata["SALESORDERITEM.SALESORDER_ID"];
 if(oid != "")
 {
-    var discount = vars.exists("$param.Discount_param") ? vars.get("$param.Discount_param"): "";
-    var oiUtils = new OrderItemUtils(oid);    
-    
-    var insertStatements = JSON.parse(vars.get("$field.itemInsertStatements"));//insert statements get set by insertPartsList in the onvalueChanges of product_id and quantity
-    var statements = [];
-    
-    //update offer price
-    var vals = oiUtils.getNetAndVat();
-    var discountedVals = OrderItemUtils.getDiscountedNet(null, oid,  discount);
-    if(discountedVals)
-    {
-        statements.push(
-            newWhere("SALESORDER.SALESORDERID", oid).buildUpdateStatement({
-                    "NET": vals[0],
-                    "VAT": vals[1],
-                    "DISCOUNTED_NET": discountedVals[0],
-                    "DISCOUNTED_VAT": discountedVals[1]
-                })
-            )
-    }
+    OrderItemUtils.updateOrderNet(oid, vars.get("$param.Discount_param"));
 
+    // insert statements get set by insertPartsList in the onvalueChanges of product_id and quantity
+    var insertStatements = JSON.parse(vars.get("$field.itemInsertStatements"));
     if(insertStatements)
     {
-        statements = statements.concat(insertStatements);
+        db.execute(insertStatements, "Data_alias", 10000000);
     }
-
-    db.execute(statements, "Data_alias", 10000000);
-}
\ No newline at end of file
+}
diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index 2faf905b45a..7ae71eff92c 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -14624,6 +14624,10 @@ Bitte Datumseingabe prüfen</value>
       <key>Manual resynchronization</key>
       <value>Manuelle Neusynchronisation</value>
     </entry>
+    <entry>
+      <key>Calculate sum</key>
+      <value>Summe berechnen</value>
+    </entry>
   </keyValueMap>
   <font name="Dialog" style="0" size="11" />
 </language>
diff --git a/neonView/OrderitemFilter_view/OrderitemFilter_view.aod b/neonView/OrderitemFilter_view/OrderitemFilter_view.aod
index 289133336b7..8d4a407dd44 100644
--- a/neonView/OrderitemFilter_view/OrderitemFilter_view.aod
+++ b/neonView/OrderitemFilter_view/OrderitemFilter_view.aod
@@ -114,6 +114,7 @@
         <element>DISCOUNT</element>
         <element>INFO</element>
       </editableColumns>
+      <favoriteActionGroup1>group</favoriteActionGroup1>
       <columns>
         <neonTableColumn>
           <name>85d30b0c-1e03-4b46-a4d1-caa3e2e7e589</name>
diff --git a/process/OfferOrder_lib/process.js b/process/OfferOrder_lib/process.js
index 7200a549076..b65c2293ba6 100644
--- a/process/OfferOrder_lib/process.js
+++ b/process/OfferOrder_lib/process.js
@@ -25,8 +25,6 @@ import("Sql_lib");
 function ItemUtils(pOfferOrderId, pTableName) {
     if (this.constructor === ItemUtils)
         throw new Error("Can't instantiate abstract class ItemUtils!");
-    
-    
     this.offerOrderId = pOfferOrderId;
     this.ItemTree = undefined; // object for dealing with hierarchical structure of Items, item position and item order
     this.ItemIds = undefined; // contains all ItemIDs to save the order
@@ -98,6 +96,65 @@ function ItemUtils(pOfferOrderId, pTableName) {
     }
 }
 
+/**
+ * Recalculates the prices of the given items
+ * If no items were passed, all elements of the offer/order will be recalculated
+ * The items are updated directly in the database
+ * 
+ * @params {String[]} pItemIds the item ids
+ */
+ItemUtils.prototype.recalculatePrices = function(pItemIds)
+{
+    if(!pItemIds || !pItemIds.length)
+    {
+        pItemIds = this.ItemIds;
+    }
+    
+    var idsSet = new Set(pItemIds);
+    var data = new Map();
+    newSelect([this.tableName + "ITEMID", "QUANTITY", "PRICE", "DISCOUNT"]).from(this.tableName + "ITEM")
+        .where(this.tableName + "ITEM." + this.tableName + "_ID", this.offerOrderId)
+        .table().forEach(function(row)
+    {
+        data.set(row[0], {quantity: row[1], price: row[2], discount: row[3]});
+    });
+    var prices = new Map();
+    
+    // TODO: replace with a lambda function as soon as they are supported
+    var that = this;
+    _traverse(pItemIds);
+    function _traverse(pIds)
+    {
+        pIds.forEach(function(id)
+        {
+            var childIds = that.ItemTree[id].ids;
+            if(!childIds.length)
+            {
+                return;
+            }
+            _traverse(childIds.filter(function(childId){return idsSet.has(childId)}));
+            var price = childIds.reduce(function(curr, childId)
+            {
+                var entry = data.get(childId);
+                var price = prices.get(childId) || entry.price;
+                return curr + price * entry.quantity * (1 - entry.discount / 100);
+            }, 0);
+            if(data.get(id).price != price)
+            {
+                prices.set(id, price);
+            }
+        });
+    }
+    
+    // TODO: use a "for of" loop to iterate through the enties iterator
+    var stmts = Array.from(prices.entries()).map(function([rowId, rowPrice])
+    {
+        var cond = newWhere(that.tableName + "ITEM." + that.tableName + "ITEMID", rowId).toString()
+        return [that.tableName + "ITEM", ["PRICE"], null, [rowPrice], cond];
+    });
+    db.updates(stmts);
+}
+
 /**
  * get netto and vat for all items excluding the provided ones.
  * 
diff --git a/process/Offer_lib/process.js b/process/Offer_lib/process.js
index 03b4b6e521c..2e3e4109529 100644
--- a/process/Offer_lib/process.js
+++ b/process/Offer_lib/process.js
@@ -535,6 +535,15 @@ function OfferItemUtils(pOfferId) {
     OfferItemUtils.prototype.constructor = OfferItemUtils;
 }
 
+/**
+ * For documentation, see class ItemUtils.
+ */
+OfferItemUtils.prototype.recalculatePrices = function(pItemIds)
+{
+    this.initItemTree();
+    ItemUtils.prototype.recalculatePrices.apply(this, Array.from(arguments));
+}
+
 /**
  * For documentation, see class ItemUtils.
  */
@@ -636,7 +645,27 @@ OfferItemUtils.prototype.reOrgItems = function() {
     ItemUtils.prototype.reOrgItems.apply(this);
 }
 
-
+/*
+ * Recalculates the offer net and vat based on the offer items
+ * and writes the result into the database
+ * 
+ * @params {String} pOfferId the offer id
+ * @params {Number} pDiscount overrides the discount (if set)
+ */
+OfferItemUtils.updateOfferNet = function(pOfferId, pDiscount)
+{
+    var oiUtils = new OfferItemUtils(pOfferId)
+    var vals = oiUtils.getNetAndVat();
+    var discountedVals = OfferItemUtils.getDiscountedNet(null, pOfferId, pDiscount);
+    if(discountedVals)
+    {
+        newWhere("OFFER.OFFERID", pOfferId).updateFields({
+            NET: vals[0], VAT: vals[1],
+            DISCOUNTED_NET: discountedVals[0],
+            DISCOUNTED_VAT: discountedVals[1]
+        });
+    }
+}
 
 OfferItemUtils.getDiscountedNet = function(pExcludedIs, pOfferId, pDiscount, pExcludedProductgroups){
     pDiscount = pDiscount ? pDiscount : 0;
diff --git a/process/Order_lib/process.js b/process/Order_lib/process.js
index 09648dec1ca..db4ee6805e7 100644
--- a/process/Order_lib/process.js
+++ b/process/Order_lib/process.js
@@ -683,6 +683,15 @@ function OrderItemUtils(pSourceOfferId) {
     OrderItemUtils.prototype.constructor = OrderItemUtils;
 }
 
+/**
+ * For documentation, see class ItemUtils.
+ */
+OrderItemUtils.prototype.recalculatePrices = function(pItemIds)
+{
+    this.initItemTree();
+    ItemUtils.prototype.recalculatePrices.apply(this, Array.from(arguments));
+}
+
 /**
  * For documentation, see class ItemUtils.
  */
@@ -776,6 +785,27 @@ OrderItemUtils.prototype.reOrgItems = function() {
     ItemUtils.prototype.reOrgItems.apply(this);
 }
 
+/*
+ * Recalculates the order net and vat based on the order items
+ * and writes the result into the database
+ * 
+ * @params {String} pOrderId the order id
+ * @params {Number} pDiscount overrides the discount (if set)
+ */
+OrderItemUtils.updateOrderNet = function(pOrderId, pDiscount)
+{
+    var oiUtils = new OrderItemUtils(pOrderId);
+    var vals = oiUtils.getNetAndVat();
+    var discountedVals = OrderItemUtils.getDiscountedNet(null, pOrderId, pDiscount);
+    if(discountedVals)
+    {
+        newWhere("SALESORDER.SALESORDERID", pOrderId).updateFields({
+            NET: vals[0], VAT: vals[1],
+            DISCOUNTED_NET: discountedVals[0],
+            DISCOUNTED_VAT: discountedVals[1]
+        });
+    }
+}
 
 OrderItemUtils.getDiscountedNet = function(pExcludedIs, pOrderId, pDiscount, pExcludedProductgroups){
     pDiscount = pDiscount || 0;
-- 
GitLab