From 6086c13180e51b7d2240531b6489ad0061ad702b Mon Sep 17 00:00:00 2001
From: Alexandre Boeglin <alex@nexedi.com>
Date: Fri, 14 Mar 2008 16:20:14 +0000
Subject: [PATCH] * OrderCell.py: isMovement is now a function, depends on
 whether parent   contains lines.   getTotalPrice, getTotalQuantity return 0.0
 if the current object is not   considered a movement.

* DeliveryLine.py (manage_afterAdd, manage_beforeDelete): when adding or
  removing a line from a line, reindex the parent, as its "isMovement" value
  might change.

* OrderLine.py: _getTotalPrice, getTotalQuantity support hierarchical orders.

* testOrder: test_19b_getTotalQuantityAndPrice verifies that isMovement,
  getTotalPrice, getTotalQuantity support hierarchical orders.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@19922 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/DeliveryLine.py |  15 ++
 product/ERP5/Document/OrderCell.py    |  24 +++-
 product/ERP5/Document/OrderLine.py    |  42 ++++++
 product/ERP5/tests/testOrder.py       | 196 +++++++++++++++++++++++++-
 4 files changed, 274 insertions(+), 3 deletions(-)

diff --git a/product/ERP5/Document/DeliveryLine.py b/product/ERP5/Document/DeliveryLine.py
index f3306804d5..e0da6b812c 100644
--- a/product/ERP5/Document/DeliveryLine.py
+++ b/product/ERP5/Document/DeliveryLine.py
@@ -373,3 +373,18 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, Variated,
       parent = self.getParentValue()
       if parent is not None:
         parent.updateSimulationDeliveryProperties(movement_list, self)
+
+    security.declarePrivate('manage_afterAdd')
+    def manage_afterAdd(self, item, container):
+      "if the container is a line too, reindex it"
+      if self.meta_type == container.meta_type:
+        container.reindexObject()
+      return Movement.manage_afterAdd(self, item, container)
+
+    security.declarePrivate('manage_beforeDelete')
+    def manage_beforeDelete(self, item, container):
+      "if the container is a line too, reindex it"
+      if self.meta_type == container.meta_type:
+        container.reindexObject()
+      return Movement.manage_beforeDelete(self, item, container)
+
diff --git a/product/ERP5/Document/OrderCell.py b/product/ERP5/Document/OrderCell.py
index 96844f71ef..b86cdff7d2 100644
--- a/product/ERP5/Document/OrderCell.py
+++ b/product/ERP5/Document/OrderCell.py
@@ -47,7 +47,6 @@ class OrderCell(DeliveryCell):
     meta_type = 'ERP5 Order Cell'
     portal_type = 'Order Cell'
     isCell = 1
-    isMovement = 1
 
     # Declarative security
     security = ClassSecurityInfo()
@@ -74,3 +73,26 @@ class OrderCell(DeliveryCell):
       Reindex children and simulation
       """
       self.recursiveReindexObject(*k,**kw)
+
+    security.declareProtected(Permissions.AccessContentsInformation,
+        'isMovement')
+    def isMovement(self):
+      """
+      should be considered as a movement if the parent does not have sub lines
+      """
+      return not self.getParentValue().hasLineContent()
+
+    security.declareProtected(Permissions.AccessContentsInformation,
+        'getTotalPrice')
+    def getTotalPrice(self, *args, **kw):
+      "only return a value if self is a movement"
+      if not self.isMovement(): return 0.0
+      return DeliveryCell.getTotalPrice(self, *args, **kw)
+
+    security.declareProtected(Permissions.AccessContentsInformation,
+        'getTotalQuantity')
+    def getTotalQuantity(self, *args, **kw):
+      "only return a value if self is a movement"
+      if not self.isMovement(): return 0.0
+      return DeliveryCell.getTotalQuantity(self, *args, **kw)
+
diff --git a/product/ERP5/Document/OrderLine.py b/product/ERP5/Document/OrderLine.py
index 838684743b..e4f2e7636d 100644
--- a/product/ERP5/Document/OrderLine.py
+++ b/product/ERP5/Document/OrderLine.py
@@ -80,6 +80,48 @@ class OrderLine(DeliveryLine):
         transactional_variable[call_method_key] = result
       return result
 
+    def _getTotalPrice(self, context, fast=1):
+      """
+      if hasLineContent: return sum of lines total price
+      if hasCellContent: return sum of cells total price
+      else: return quantity * price
+      """
+      base_id = 'movement'
+      if self.hasLineContent():
+        return sum(l.getTotalPrice() for l in
+            self.contentValues(meta_type=self.meta_type))
+      elif self.hasCellContent(base_id=base_id):
+        if fast : # Use MySQL
+          aggregate = self.DeliveryLine_zGetTotal()[0]
+          return aggregate.total_price or 0.0
+        return sum([ ( (cell.getQuantity() or 0) *
+                       (cell.getPrice(context=context) or 0))
+                        for cell in self.getCellValueList()])
+      else:
+        quantity = self.getQuantity() or 0.0
+        price = self.getPrice(context=context) or 0.0
+        return quantity * price
+
+    security.declareProtected(Permissions.AccessContentsInformation,
+                              'getTotalQuantity')
+    def getTotalQuantity(self, fast=1):
+      """
+      if hasLineContent: return sum of lines total quantity
+      if hasCellContent: return sum of cells total quantity
+      else: return quantity
+      """
+      base_id = 'movement'
+      if self.hasLineContent():
+        return sum(l.getTotalQuantity() for l in
+            self.contentValues(meta_type=self.meta_type))
+      elif self.hasCellContent(base_id=base_id):
+        if fast : # Use MySQL
+          aggregate = self.DeliveryLine_zGetTotal()[0]
+          return aggregate.total_quantity or 0.0
+        return sum([cell.getQuantity() for cell in self.getCellValueList()])
+      else:
+        return self.getQuantity()
+
     def applyToOrderLineRelatedMovement(self, portal_type='Simulation Movement', 
                                         method_id = 'expand'):
       """
diff --git a/product/ERP5/tests/testOrder.py b/product/ERP5/tests/testOrder.py
index 98bd8cc4d0..4f7236d851 100644
--- a/product/ERP5/tests/testOrder.py
+++ b/product/ERP5/tests/testOrder.py
@@ -1850,12 +1850,11 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase):
   def test_19_getMovementList(self, quiet=0, run=run_all_test):
     """
     Check getMovementList.
-    Verify that it manage hierarchical order lines.
+    Verify that it supports hierarchical order lines.
     Check that order cells are returned when defined on a leaf line, and not
     returned when defined on a non leaf line.
     """
     if not run: return
-    sequence_list = SequenceList()
 
     portal = self.getPortal()
     order_module = portal.getDefaultModule(portal_type=self.order_portal_type)
@@ -1933,6 +1932,199 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase):
                                 *cell_key)
     self.assertEquals(2-1+len(cell_key_list), len(order.getMovementList()))
 
+  def test_19b_getTotalQuantityAndPrice(self, quiet=0, run=run_all_test):
+    """
+    Check getTotalQuantity and getTotalPrice.
+    Check isMovement.
+    Verify that it supports hierarchical order lines.
+    Note that this depends on isMovement and Order Line reindexation
+    """
+    if not run: return
+
+    portal = self.getPortal()
+    base_id = 'movement'
+    order_line_vcl=['size/Baby']
+    order_module = portal.getDefaultModule(portal_type=self.order_portal_type)
+    order = order_module.newContent(portal_type=self.order_portal_type)
+
+    # No line, no movement
+    self.assertEquals(order.getTotalQuantity(fast=0), 0)
+    self.assertEquals(order.getTotalQuantity(fast=1), 0)
+    self.assertEquals(order.getTotalPrice(fast=0), 0)
+    self.assertEquals(order.getTotalPrice(fast=1), 0)
+
+    # Create a variated resource
+    resource_module = portal.getDefaultModule(self.resource_portal_type)
+    resource = resource_module.newContent(
+        portal_type=self.resource_portal_type,
+        variation_base_category_list=['size'],
+        size_list=self.size_list)
+
+    # One line is considered as a movement
+    order_line = order.newContent(
+        portal_type=self.order_line_portal_type,
+        resource_value=resource,
+        price=2,
+        quantity=3)
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 3)
+    self.assertEquals(order.getTotalQuantity(fast=1), 3)
+    self.assertEquals(order.getTotalPrice(fast=0), 6)
+    self.assertEquals(order.getTotalPrice(fast=1), 6)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 3)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 3)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 6)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 6)
+
+    # add cell to line, line is not a movement anymore
+    order_line.setVariationCategoryList(order_line_vcl)
+    cell_key = order_line.getCellKeyList(base_id=base_id)[0]
+    cell = order_line.newCell(
+        base_id=base_id,
+        portal_type=self.order_cell_portal_type, 
+        *cell_key)
+    cell.edit(mapped_value_property_list=['price', 'quantity'],
+        price=3, quantity=4,
+        predicate_category_list=cell_key,
+        variation_category_list=cell_key)
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), False)
+    self.assertEquals(cell.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 4)
+    self.assertEquals(order.getTotalQuantity(fast=1), 4)
+    self.assertEquals(order.getTotalPrice(fast=0), 12)
+    self.assertEquals(order.getTotalPrice(fast=1), 12)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 4)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 4)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 12)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 12)
+
+    self.assertEquals(cell.getTotalQuantity(), 4)
+    self.assertEquals(cell.getTotalPrice(fast=0), 12)
+    self.assertEquals(cell.getTotalPrice(fast=1), 12)
+
+    # add sub_line to line, cell and line are not movements
+    sub_order_line = order_line.newContent(
+        portal_type=self.order_line_portal_type,
+        price=4,
+        quantity=5)
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), False)
+    self.assertEquals(cell.isMovement(), False)
+    self.assertEquals(sub_order_line.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 5)
+    self.assertEquals(order.getTotalQuantity(fast=1), 5)
+    self.assertEquals(order.getTotalPrice(fast=0), 20)
+    self.assertEquals(order.getTotalPrice(fast=1), 20)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 5)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 5)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 20)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 20)
+
+    self.assertEquals(cell.getTotalQuantity(), 0)
+    self.assertEquals(cell.getTotalPrice(fast=0), 0)
+    self.assertEquals(cell.getTotalPrice(fast=1), 0)
+
+    self.assertEquals(sub_order_line.getTotalQuantity(fast=0), 5)
+    self.assertEquals(sub_order_line.getTotalQuantity(fast=1), 5)
+    self.assertEquals(sub_order_line.getTotalPrice(fast=0), 20)
+    self.assertEquals(sub_order_line.getTotalPrice(fast=1), 20)
+
+    # add sub_cell to sub_line, only sub_cell is movement
+    sub_order_line.setVariationCategoryList(order_line_vcl)
+    sub_cell_key = sub_order_line.getCellKeyList(base_id=base_id)[0]
+    sub_cell = sub_order_line.newCell(
+        base_id=base_id,
+        portal_type=self.order_cell_portal_type, 
+        *cell_key)
+    sub_cell.edit(mapped_value_property_list=['price', 'quantity'],
+        price=5, quantity=6,
+        predicate_category_list=cell_key,
+        variation_category_list=cell_key)
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), False)
+    self.assertEquals(cell.isMovement(), False)
+    self.assertEquals(sub_order_line.isMovement(), False)
+    self.assertEquals(sub_cell.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 6)
+    self.assertEquals(order.getTotalQuantity(fast=1), 6)
+    self.assertEquals(order.getTotalPrice(fast=0), 30)
+    self.assertEquals(order.getTotalPrice(fast=1), 30)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 6)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 6)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 30)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 30)
+
+    self.assertEquals(cell.getTotalQuantity(), 0)
+    self.assertEquals(cell.getTotalPrice(fast=0), 0)
+    self.assertEquals(cell.getTotalPrice(fast=1), 0)
+
+    self.assertEquals(sub_order_line.getTotalQuantity(fast=0), 6)
+    self.assertEquals(sub_order_line.getTotalQuantity(fast=1), 6)
+    self.assertEquals(sub_order_line.getTotalPrice(fast=0), 30)
+    self.assertEquals(sub_order_line.getTotalPrice(fast=1), 30)
+
+    self.assertEquals(sub_cell.getTotalQuantity(), 6)
+    self.assertEquals(sub_cell.getTotalPrice(fast=0), 30)
+    self.assertEquals(sub_cell.getTotalPrice(fast=1), 30)
+
+    # delete sub_line, cell is movement again
+    order_line.manage_delObjects([sub_order_line.getId()])
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), False)
+    self.assertEquals(cell.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 4)
+    self.assertEquals(order.getTotalQuantity(fast=1), 4)
+    self.assertEquals(order.getTotalPrice(fast=0), 12)
+    self.assertEquals(order.getTotalPrice(fast=1), 12)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 4)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 4)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 12)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 12)
+
+    self.assertEquals(cell.getTotalQuantity(), 4)
+    self.assertEquals(cell.getTotalPrice(fast=0), 12)
+    self.assertEquals(cell.getTotalPrice(fast=1), 12)
+
+    # delete cell, line is movement again
+    order_line.manage_delObjects([cell.getId()])
+    order_line.setVariationCategoryList([])
+    get_transaction().commit()
+    self.tic()
+
+    self.assertEquals(order_line.isMovement(), True)
+
+    self.assertEquals(order.getTotalQuantity(fast=0), 3)
+    self.assertEquals(order.getTotalQuantity(fast=1), 3)
+    self.assertEquals(order.getTotalPrice(fast=0), 6)
+    self.assertEquals(order.getTotalPrice(fast=1), 6)
+
+    self.assertEquals(order_line.getTotalQuantity(fast=0), 3)
+    self.assertEquals(order_line.getTotalQuantity(fast=1), 3)
+    self.assertEquals(order_line.getTotalPrice(fast=0), 6)
+    self.assertEquals(order_line.getTotalPrice(fast=1), 6)
+
   def stepCreateSubOrderLine(self,sequence=None, sequence_list=None, **kw):
     """
       Create a empty order line
-- 
2.30.9