From d87b9db85dcea529257a33bcd137b8c26b684ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com> Date: Tue, 5 Feb 2013 14:47:29 +0100 Subject: [PATCH] Reimplement fast=True for getTotalPrice/Quantity using inventory API --- .../erp5_trade/DeliveryLine_zGetTotal.xml | 77 ------------- .../erp5_trade/Delivery_zGetTotal.xml | 90 ---------------- product/ERP5/Document/Delivery.py | 101 +++++++++--------- product/ERP5/Document/DeliveryLine.py | 34 ++++-- product/ERP5/Document/Order.py | 4 + product/ERP5/tests/testOrder.py | 54 +++++++--- 6 files changed, 121 insertions(+), 239 deletions(-) delete mode 100644 bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/DeliveryLine_zGetTotal.xml delete mode 100644 bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/Delivery_zGetTotal.xml diff --git a/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/DeliveryLine_zGetTotal.xml b/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/DeliveryLine_zGetTotal.xml deleted file mode 100644 index 3ca99d5742..0000000000 --- a/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/DeliveryLine_zGetTotal.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0"?> -<ZopeData> - <record id="1" aka="AAAAAAAAAAE="> - <pickle> - <global name="SQL" module="Products.ZSQLMethods.SQL"/> - </pickle> - <pickle> - <dictionary> - <item> - <key> <string>allow_simple_one_argument_traversal</string> </key> - <value> - <none/> - </value> - </item> - <item> - <key> <string>arguments_src</string> </key> - <value> <string>uid</string> </value> - </item> - <item> - <key> <string>cache_time_</string> </key> - <value> <int>0</int> </value> - </item> - <item> - <key> <string>class_file_</string> </key> - <value> <string></string> </value> - </item> - <item> - <key> <string>class_name_</string> </key> - <value> <string></string> </value> - </item> - <item> - <key> <string>connection_hook</string> </key> - <value> - <none/> - </value> - </item> - <item> - <key> <string>connection_id</string> </key> - <value> <string>erp5_sql_connection</string> </value> - </item> - <item> - <key> <string>id</string> </key> - <value> <string>DeliveryLine_zGetTotal</string> </value> - </item> - <item> - <key> <string>max_cache_</string> </key> - <value> <int>100</int> </value> - </item> - <item> - <key> <string>max_rows_</string> </key> - <value> <int>1000</int> </value> - </item> - <item> - <key> <string>src</string> </key> - <value> <string encoding="cdata"><![CDATA[ - -SELECT \n -\tSUM(quantity) AS total_quantity, \n -\tSUM(quantity * price) AS total_price, \n -\tAVG(price) AS average_price\n -FROM catalog, movement\n -WHERE \n -\tcatalog.parent_uid = <dtml-sqlvar uid type="int">\n -AND\t\n -\tcatalog.uid = movement.uid\n - - -]]></string> </value> - </item> - <item> - <key> <string>title</string> </key> - <value> <string></string> </value> - </item> - </dictionary> - </pickle> - </record> -</ZopeData> diff --git a/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/Delivery_zGetTotal.xml b/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/Delivery_zGetTotal.xml deleted file mode 100644 index cdd74f7e91..0000000000 --- a/bt5/erp5_trade/SkinTemplateItem/portal_skins/erp5_trade/Delivery_zGetTotal.xml +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0"?> -<ZopeData> - <record id="1" aka="AAAAAAAAAAE="> - <pickle> - <global name="SQL" module="Products.ZSQLMethods.SQL"/> - </pickle> - <pickle> - <dictionary> - <item> - <key> <string>allow_simple_one_argument_traversal</string> </key> - <value> - <none/> - </value> - </item> - <item> - <key> <string>arguments_src</string> </key> - <value> <string>from_table_list:list\r\n -where_expression\r\n -order_by_expression</string> </value> - </item> - <item> - <key> <string>cache_time_</string> </key> - <value> <int>0</int> </value> - </item> - <item> - <key> <string>class_file_</string> </key> - <value> <string></string> </value> - </item> - <item> - <key> <string>class_name_</string> </key> - <value> <string></string> </value> - </item> - <item> - <key> <string>connection_hook</string> </key> - <value> - <none/> - </value> - </item> - <item> - <key> <string>connection_id</string> </key> - <value> <string>erp5_sql_connection</string> </value> - </item> - <item> - <key> <string>id</string> </key> - <value> <string>Delivery_zGetTotal</string> </value> - </item> - <item> - <key> <string>max_cache_</string> </key> - <value> <int>100</int> </value> - </item> - <item> - <key> <string>max_rows_</string> </key> - <value> <int>1000</int> </value> - </item> - <item> - <key> <string>src</string> </key> - <value> <string encoding="cdata"><![CDATA[ - -SELECT\n - SUM(movement.quantity) AS inventory,\n - SUM(movement.quantity) AS total_quantity,\n - SUM(movement.price * movement.quantity) AS total_price,\n - AVG(movement.price) AS average_price\n -\n -FROM\n -<dtml-in from_table_list> <dtml-var sequence-item> AS <dtml-var sequence-key><dtml-if sequence-end><dtml-else>,</dtml-if></dtml-in>\n -\n -WHERE\n - 1=1\n - <dtml-if where_expression>\n - AND <dtml-var where_expression> \n - </dtml-if>\n - AND catalog.has_cell_content = 0\n - AND catalog.portal_type NOT IN ("Container", "Container Line", "Container Cell", "Simulation Movement")\n -\n -<dtml-if order_by_expression>\n -ORDER BY <dtml-var order_by_expression>\n -</dtml-if>\n - - -]]></string> </value> - </item> - <item> - <key> <string>title</string> </key> - <value> <string></string> </value> - </item> - </dictionary> - </pickle> - </record> -</ZopeData> diff --git a/product/ERP5/Document/Delivery.py b/product/ERP5/Document/Delivery.py index 7a71308367..a89e7955b4 100644 --- a/product/ERP5/Document/Delivery.py +++ b/product/ERP5/Document/Delivery.py @@ -91,6 +91,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, 'getTotalPrice') def getTotalPrice(self, fast=0, src__=0, base_contribution=None, rounding=False, **kw): """ Returns the total price for this order + if the `fast` argument is set to a true value, then it use SQLCatalog to compute the price, otherwise it sums the total price of objects one by one. @@ -98,49 +99,51 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, So if the order is not in the catalog, getTotalPrice(fast=1) will return 0, this is not a bug. - base_contribution must be a relative url of a category. + base_contribution must be a relative url of a category. If passed, then + fast parameter is ignored. """ result = None - if not fast: - kw.setdefault( 'portal_type', - self.getPortalDeliveryMovementTypeList()) - if base_contribution is None: - result = sum([ line.getTotalPrice(fast=0) for line in - self.objectValues(**kw) ]) + kw.setdefault( 'portal_type', + self.getPortalDeliveryMovementTypeList()) + if base_contribution is None: + if fast: + # XXX fast ignores base_contribution for now, but it should be possible + # to use a related key + kw['section_uid'] = self.getDestinationSectionUid() + kw['stock.explanation_uid'] = self.getUid() + return self.getPortalObject()\ + .portal_simulation.getInventoryAssetPrice(**kw) + + result = sum([ line.getTotalPrice(fast=0) for line in + self.objectValues(**kw) ]) + else: + # Find amounts from movements in the delivery. + if isinstance(base_contribution, (tuple, list)): + base_contribution_list = base_contribution else: - # Find amounts from movements in the delivery. - if isinstance(base_contribution, (tuple, list)): - base_contribution_list = base_contribution - else: - base_contribution_list = (base_contribution,) - base_contribution_value_list = [] - portal_categories = self.portal_categories - for relative_url in base_contribution_list: - base_contribution_value = portal_categories.getCategoryValue(relative_url) - if base_contribution_value is not None: - base_contribution_value_list.append(base_contribution_value) - if not base_contribution_value_list: - # We cannot find any amount so that the result is 0. - result = 0 - else: + base_contribution_list = (base_contribution,) + base_contribution_value_list = [] + portal_categories = self.portal_categories + for relative_url in base_contribution_list: + base_contribution_value = portal_categories.getCategoryValue(relative_url) + if base_contribution_value is not None: + base_contribution_value_list.append(base_contribution_value) + if not base_contribution_value_list: + # We cannot find any amount so that the result is 0. + result = 0 + else: + matched_movement_list = [ + movement + for movement in self.getMovementList() + if set(movement.getBaseContributionValueList()).intersection(base_contribution_value_list)] + if rounding: + portal_roundings = self.portal_roundings matched_movement_list = [ - movement - for movement in self.getMovementList() - if set(movement.getBaseContributionValueList()).intersection(base_contribution_value_list)] - if rounding: - portal_roundings = self.portal_roundings - matched_movement_list = [ - portal_roundings.getRoundingProxy(movement) - for movement in matched_movement_list] - result = sum([movement.getTotalPrice() - for movement in matched_movement_list]) - else: - kw['explanation_uid'] = self.getUid() - kw.update(self.portal_catalog.buildSQLQuery(**kw)) - if src__: - return self.Delivery_zGetTotal(src__=1, **kw) - aggregate = self.Delivery_zGetTotal(**kw)[0] - result = aggregate.total_price or 0 + portal_roundings.getRoundingProxy(movement) + for movement in matched_movement_list] + result = sum([movement.getTotalPrice() + for movement in matched_movement_list]) + method = self._getTypeBasedMethod('convertTotalPrice') if method is not None: return method(result) @@ -164,6 +167,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, 'getTotalQuantity') def getTotalQuantity(self, fast=0, src__=0, **kw): """ Returns the total quantity of this order. + if the `fast` argument is set to a true value, then it use SQLCatalog to compute the quantity, otherwise it sums the total quantity of objects one by one. @@ -171,17 +175,14 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, So if the order is not in the catalog, getTotalQuantity(fast=1) will return 0, this is not a bug. """ - if not fast : - kw.setdefault('portal_type', - self.getPortalDeliveryMovementTypeList()) - return sum([ line.getTotalQuantity(fast=0) for line in - self.objectValues(**kw) ]) - kw['explanation_uid'] = self.getUid() - kw.update(self.portal_catalog.buildSQLQuery(**kw)) - if src__: - return self.Delivery_zGetTotal(src__=1, **kw) - aggregate = self.Delivery_zGetTotal(**kw)[0] - return aggregate.total_quantity or 0 + kw.setdefault('portal_type', + self.getPortalDeliveryMovementTypeList()) + if fast: + kw['section_uid'] = self.getDestinationSectionUid() + kw['stock.explanation_uid'] = self.getUid() + return self.getPortalObject().portal_simulation.getInventory(**kw) + return sum([ line.getTotalQuantity(fast=0) for line in + self.objectValues(**kw) ]) security.declareProtected(Permissions.AccessContentsInformation, 'getDeliveryUid') diff --git a/product/ERP5/Document/DeliveryLine.py b/product/ERP5/Document/DeliveryLine.py index 4f40208c95..8e0981b722 100644 --- a/product/ERP5/Document/DeliveryLine.py +++ b/product/ERP5/Document/DeliveryLine.py @@ -116,16 +116,23 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement): if hasLineContent: return sum of lines total price if hasCellContent: return sum of cells total price else: return quantity * price - if fast is argument true, then a SQL method will be used. - """ + if fast argument is true, inventory API will be used. + """ + if fast: + kw = {} + kw['section_uid'] = self.getDestinationSectionUid() + kw['stock.explanation_uid'] = self.getExplanationUid() + kw['relative_url'] = ( '%s/%%' % ( + self.getRelativeUrl().replace('_', '\\_')), + self.getRelativeUrl() ) + kw['only_accountable'] = False + return self.getPortalObject().portal_simulation.getInventoryAssetPrice(**kw) if self.hasLineContent(): meta_type = self.meta_type return sum(l.getTotalPrice(context=context) for l in self.objectValues() if l.meta_type==meta_type) elif not self.hasCellContent(base_id='movement'): return Movement._getTotalPrice(self, default=default, context=context) - elif fast: # Use MySQL - return self.DeliveryLine_zGetTotal()[0].total_price or 0.0 return sum(cell.getTotalPrice(default=0.0, context=context) for cell in self.getCellValueList()) @@ -138,20 +145,27 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement): if hasLineContent: return sum of lines total quantity if hasCellContent: return sum of cells total quantity else: return quantity - if fast argument is true, then a SQL method will be used. + if fast argument is true, inventory API will be used. """ + + if fast: + kw = {} + kw['section_uid'] = self.getDestinationSectionUid() + kw['stock.explanation_uid'] = self.getExplanationUid() + kw['relative_url'] = ( '%s/%%' % ( + self.getRelativeUrl().replace('_', '\\_')), + self.getRelativeUrl() ) + kw['only_accountable'] = False + return self.getPortalObject().portal_simulation.getInventory(**kw) + base_id = 'movement' if self.hasLineContent(): meta_type = self.meta_type return sum(l.getTotalQuantity() for l in self.objectValues() if l.meta_type==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() + return self.getQuantity() security.declareProtected(Permissions.AccessContentsInformation, 'hasLineContent') diff --git a/product/ERP5/Document/Order.py b/product/ERP5/Document/Order.py index dc34fdc070..1f21e463ec 100644 --- a/product/ERP5/Document/Order.py +++ b/product/ERP5/Document/Order.py @@ -72,6 +72,8 @@ class Order(Delivery): If base_contribution is passed, the trade model lines will be used to include movements that will be generated. """ + if kw.get('fast'): + kw['only_accountable'] = False rounding = kw.get('rounding') if kw.get('base_contribution') is None: kw.setdefault('portal_type', self.getPortalOrderMovementTypeList()) @@ -118,6 +120,8 @@ class Order(Delivery): def getTotalQuantity(self, **kw) : """Returns the total quantity for this Order. """ kw.setdefault('portal_type', self.getPortalOrderMovementTypeList()) + if kw.get('fast'): + kw['only_accountable'] = False return Delivery.getTotalQuantity(self, **kw) @deprecated diff --git a/product/ERP5/tests/testOrder.py b/product/ERP5/tests/testOrder.py index 06ae817b21..6a87001b49 100644 --- a/product/ERP5/tests/testOrder.py +++ b/product/ERP5/tests/testOrder.py @@ -353,19 +353,30 @@ class TestOrderMixin(SubcontentReindexingWrapper): def stepCheckOrder(self, sequence=None, sequence_list=None, **kw): """ - Check if order was well created - """ - organisation = sequence.get('organisation') - project = sequence.get('project') + Check if order was well created, either by stepCreateOrder of + stepSetOrderProfile + """ + source_organisation = sequence.get('organisation1') + if source_organisation is None: + source_organisation = sequence.get('organisation') + destination_organisation = sequence.get('organisation2') + if destination_organisation is None: + destination_organisation = sequence.get('organisation') + source_project = sequence.get('project1') + if source_project is None: + source_project = sequence.get('project') + destination_project = sequence.get('project2') + if destination_project is None: + destination_project = sequence.get('project') order = sequence.get('order') self.assertEquals(self.datetime+10, order.getStartDate()) self.assertEquals(self.datetime+20, order.getStopDate()) - self.assertEquals(organisation, order.getSourceValue()) - self.assertEquals(organisation, order.getDestinationValue()) - self.assertEquals(organisation, order.getSourceSectionValue()) - self.assertEquals(organisation, order.getDestinationSectionValue()) - self.assertEquals(project, order.getSourceProjectValue()) - self.assertEquals(project, order.getDestinationProjectValue()) + self.assertEquals(source_organisation, order.getSourceValue()) + self.assertEquals(destination_organisation, order.getDestinationValue()) + self.assertEquals(source_organisation, order.getSourceSectionValue()) + self.assertEquals(destination_organisation, order.getDestinationSectionValue()) + self.assertEquals(source_project, order.getSourceProjectValue()) + self.assertEquals(destination_project, order.getDestinationProjectValue()) def stepCreateOrderLine(self,sequence=None, sequence_list=None, **kw): @@ -736,7 +747,7 @@ class TestOrderMixin(SubcontentReindexingWrapper): order_line_list = map(lambda x: x.getObject(), order_line_list) total_price = 0 for order_line in order_line_list: - total_price += order_line.getTotalPrice(fast=0) + total_price += order_line.getTotalPrice() self.assertEquals(0, len(portal_catalog(relative_url=order.getRelativeUrl()))) self.assertEquals(total_price, order.getTotalPrice(fast=0)) self.assertNotEquals(total_price, 0) @@ -1148,7 +1159,12 @@ class TestOrderMixin(SubcontentReindexingWrapper): 'adopt_prevision_action') non_variated_order_creation = '\ + stepCreateOrganisation1 \ + stepCreateOrganisation2 \ + stepCreateProject1 \ + stepCreateProject2 \ stepCreateOrder \ + stepSetOrderProfile \ stepCreateNotVariatedResource \ stepCreateOrderLine \ stepCheckOrderLineEmptyMatrix \ @@ -1158,7 +1174,12 @@ class TestOrderMixin(SubcontentReindexingWrapper): ' variated_order_line_creation = '\ + stepCreateOrganisation1 \ + stepCreateOrganisation2 \ + stepCreateProject1 \ + stepCreateProject2 \ stepCreateOrder \ + stepSetOrderProfile \ stepCreateVariatedResource \ stepCreateOrderLine \ ' @@ -1590,7 +1611,12 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase): sequence_list = SequenceList() # Test with positive price order line and negative price order line. sequence_string = '\ + stepCreateOrganisation1 \ + stepCreateOrganisation2 \ + stepCreateProject1 \ + stepCreateProject2 \ stepCreateOrder \ + stepSetOrderProfile \ stepCheckOrderTotalQuantity \ stepCreateNotVariatedResource \ stepCreateOrderLine \ @@ -2250,11 +2276,15 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase): """ if not run: return - portal = self.getPortal() + portal = self.portal base_id = 'movement' order_line_vcl=['size/Baby'] + section = portal.organisation_module.newContent( + portal_type='Organisation') order_module = portal.getDefaultModule(portal_type=self.order_portal_type) order = order_module.newContent(portal_type=self.order_portal_type, + destination_section_value=section, + destination_value=section, specialise=self.business_process) # No line, no movement self.assertEquals(order.getTotalQuantity(fast=0), 0) -- 2.30.9