Commit d87b9db8 authored by Jérome Perrin's avatar Jérome Perrin

Reimplement fast=True for getTotalPrice/Quantity using inventory API

parent 5764dcc8
<?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>
<?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>
...@@ -91,6 +91,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, ...@@ -91,6 +91,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
'getTotalPrice') 'getTotalPrice')
def getTotalPrice(self, fast=0, src__=0, base_contribution=None, rounding=False, **kw): def getTotalPrice(self, fast=0, src__=0, base_contribution=None, rounding=False, **kw):
""" Returns the total price for this order """ Returns the total price for this order
if the `fast` argument is set to a true value, then it use if the `fast` argument is set to a true value, then it use
SQLCatalog to compute the price, otherwise it sums the total SQLCatalog to compute the price, otherwise it sums the total
price of objects one by one. price of objects one by one.
...@@ -98,13 +99,21 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, ...@@ -98,13 +99,21 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
So if the order is not in the catalog, getTotalPrice(fast=1) So if the order is not in the catalog, getTotalPrice(fast=1)
will return 0, this is not a bug. 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 result = None
if not fast:
kw.setdefault( 'portal_type', kw.setdefault( 'portal_type',
self.getPortalDeliveryMovementTypeList()) self.getPortalDeliveryMovementTypeList())
if base_contribution is None: 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 result = sum([ line.getTotalPrice(fast=0) for line in
self.objectValues(**kw) ]) self.objectValues(**kw) ])
else: else:
...@@ -134,13 +143,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, ...@@ -134,13 +143,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
for movement in matched_movement_list] for movement in matched_movement_list]
result = sum([movement.getTotalPrice() result = sum([movement.getTotalPrice()
for movement in matched_movement_list]) 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
method = self._getTypeBasedMethod('convertTotalPrice') method = self._getTypeBasedMethod('convertTotalPrice')
if method is not None: if method is not None:
return method(result) return method(result)
...@@ -164,6 +167,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, ...@@ -164,6 +167,7 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
'getTotalQuantity') 'getTotalQuantity')
def getTotalQuantity(self, fast=0, src__=0, **kw): def getTotalQuantity(self, fast=0, src__=0, **kw):
""" Returns the total quantity of this order. """ Returns the total quantity of this order.
if the `fast` argument is set to a true value, then it use if the `fast` argument is set to a true value, then it use
SQLCatalog to compute the quantity, otherwise it sums the total SQLCatalog to compute the quantity, otherwise it sums the total
quantity of objects one by one. quantity of objects one by one.
...@@ -171,17 +175,14 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin, ...@@ -171,17 +175,14 @@ class Delivery(XMLObject, ImmobilisationDelivery, SimulableMixin,
So if the order is not in the catalog, getTotalQuantity(fast=1) So if the order is not in the catalog, getTotalQuantity(fast=1)
will return 0, this is not a bug. will return 0, this is not a bug.
""" """
if not fast :
kw.setdefault('portal_type', kw.setdefault('portal_type',
self.getPortalDeliveryMovementTypeList()) 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 return sum([ line.getTotalQuantity(fast=0) for line in
self.objectValues(**kw) ]) 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
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getDeliveryUid') 'getDeliveryUid')
......
...@@ -116,16 +116,23 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement): ...@@ -116,16 +116,23 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement):
if hasLineContent: return sum of lines total price if hasLineContent: return sum of lines total price
if hasCellContent: return sum of cells total price if hasCellContent: return sum of cells total price
else: return quantity * 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(): if self.hasLineContent():
meta_type = self.meta_type meta_type = self.meta_type
return sum(l.getTotalPrice(context=context) return sum(l.getTotalPrice(context=context)
for l in self.objectValues() if l.meta_type==meta_type) for l in self.objectValues() if l.meta_type==meta_type)
elif not self.hasCellContent(base_id='movement'): elif not self.hasCellContent(base_id='movement'):
return Movement._getTotalPrice(self, default=default, context=context) 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) return sum(cell.getTotalPrice(default=0.0, context=context)
for cell in self.getCellValueList()) for cell in self.getCellValueList())
...@@ -138,19 +145,26 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement): ...@@ -138,19 +145,26 @@ class DeliveryLine(Movement, XMLObject, XMLMatrix, ImmobilisationMovement):
if hasLineContent: return sum of lines total quantity if hasLineContent: return sum of lines total quantity
if hasCellContent: return sum of cells total quantity if hasCellContent: return sum of cells total quantity
else: return 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' base_id = 'movement'
if self.hasLineContent(): if self.hasLineContent():
meta_type = self.meta_type meta_type = self.meta_type
return sum(l.getTotalQuantity() for l in return sum(l.getTotalQuantity() for l in
self.objectValues() if l.meta_type==meta_type) self.objectValues() if l.meta_type==meta_type)
elif self.hasCellContent(base_id=base_id): 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()]) return sum([cell.getQuantity() for cell in self.getCellValueList()])
else:
return self.getQuantity() return self.getQuantity()
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
......
...@@ -72,6 +72,8 @@ class Order(Delivery): ...@@ -72,6 +72,8 @@ class Order(Delivery):
If base_contribution is passed, the trade model lines will be used to If base_contribution is passed, the trade model lines will be used to
include movements that will be generated. include movements that will be generated.
""" """
if kw.get('fast'):
kw['only_accountable'] = False
rounding = kw.get('rounding') rounding = kw.get('rounding')
if kw.get('base_contribution') is None: if kw.get('base_contribution') is None:
kw.setdefault('portal_type', self.getPortalOrderMovementTypeList()) kw.setdefault('portal_type', self.getPortalOrderMovementTypeList())
...@@ -118,6 +120,8 @@ class Order(Delivery): ...@@ -118,6 +120,8 @@ class Order(Delivery):
def getTotalQuantity(self, **kw) : def getTotalQuantity(self, **kw) :
"""Returns the total quantity for this Order. """ """Returns the total quantity for this Order. """
kw.setdefault('portal_type', self.getPortalOrderMovementTypeList()) kw.setdefault('portal_type', self.getPortalOrderMovementTypeList())
if kw.get('fast'):
kw['only_accountable'] = False
return Delivery.getTotalQuantity(self, **kw) return Delivery.getTotalQuantity(self, **kw)
@deprecated @deprecated
......
...@@ -353,19 +353,30 @@ class TestOrderMixin(SubcontentReindexingWrapper): ...@@ -353,19 +353,30 @@ class TestOrderMixin(SubcontentReindexingWrapper):
def stepCheckOrder(self, sequence=None, sequence_list=None, **kw): def stepCheckOrder(self, sequence=None, sequence_list=None, **kw):
""" """
Check if order was well created Check if order was well created, either by stepCreateOrder of
""" stepSetOrderProfile
organisation = sequence.get('organisation') """
project = sequence.get('project') 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') order = sequence.get('order')
self.assertEquals(self.datetime+10, order.getStartDate()) self.assertEquals(self.datetime+10, order.getStartDate())
self.assertEquals(self.datetime+20, order.getStopDate()) self.assertEquals(self.datetime+20, order.getStopDate())
self.assertEquals(organisation, order.getSourceValue()) self.assertEquals(source_organisation, order.getSourceValue())
self.assertEquals(organisation, order.getDestinationValue()) self.assertEquals(destination_organisation, order.getDestinationValue())
self.assertEquals(organisation, order.getSourceSectionValue()) self.assertEquals(source_organisation, order.getSourceSectionValue())
self.assertEquals(organisation, order.getDestinationSectionValue()) self.assertEquals(destination_organisation, order.getDestinationSectionValue())
self.assertEquals(project, order.getSourceProjectValue()) self.assertEquals(source_project, order.getSourceProjectValue())
self.assertEquals(project, order.getDestinationProjectValue()) self.assertEquals(destination_project, order.getDestinationProjectValue())
def stepCreateOrderLine(self,sequence=None, sequence_list=None, **kw): def stepCreateOrderLine(self,sequence=None, sequence_list=None, **kw):
...@@ -736,7 +747,7 @@ class TestOrderMixin(SubcontentReindexingWrapper): ...@@ -736,7 +747,7 @@ class TestOrderMixin(SubcontentReindexingWrapper):
order_line_list = map(lambda x: x.getObject(), order_line_list) order_line_list = map(lambda x: x.getObject(), order_line_list)
total_price = 0 total_price = 0
for order_line in order_line_list: 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(0, len(portal_catalog(relative_url=order.getRelativeUrl())))
self.assertEquals(total_price, order.getTotalPrice(fast=0)) self.assertEquals(total_price, order.getTotalPrice(fast=0))
self.assertNotEquals(total_price, 0) self.assertNotEquals(total_price, 0)
...@@ -1148,7 +1159,12 @@ class TestOrderMixin(SubcontentReindexingWrapper): ...@@ -1148,7 +1159,12 @@ class TestOrderMixin(SubcontentReindexingWrapper):
'adopt_prevision_action') 'adopt_prevision_action')
non_variated_order_creation = '\ non_variated_order_creation = '\
stepCreateOrganisation1 \
stepCreateOrganisation2 \
stepCreateProject1 \
stepCreateProject2 \
stepCreateOrder \ stepCreateOrder \
stepSetOrderProfile \
stepCreateNotVariatedResource \ stepCreateNotVariatedResource \
stepCreateOrderLine \ stepCreateOrderLine \
stepCheckOrderLineEmptyMatrix \ stepCheckOrderLineEmptyMatrix \
...@@ -1158,7 +1174,12 @@ class TestOrderMixin(SubcontentReindexingWrapper): ...@@ -1158,7 +1174,12 @@ class TestOrderMixin(SubcontentReindexingWrapper):
' '
variated_order_line_creation = '\ variated_order_line_creation = '\
stepCreateOrganisation1 \
stepCreateOrganisation2 \
stepCreateProject1 \
stepCreateProject2 \
stepCreateOrder \ stepCreateOrder \
stepSetOrderProfile \
stepCreateVariatedResource \ stepCreateVariatedResource \
stepCreateOrderLine \ stepCreateOrderLine \
' '
...@@ -1590,7 +1611,12 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase): ...@@ -1590,7 +1611,12 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase):
sequence_list = SequenceList() sequence_list = SequenceList()
# Test with positive price order line and negative price order line. # Test with positive price order line and negative price order line.
sequence_string = '\ sequence_string = '\
stepCreateOrganisation1 \
stepCreateOrganisation2 \
stepCreateProject1 \
stepCreateProject2 \
stepCreateOrder \ stepCreateOrder \
stepSetOrderProfile \
stepCheckOrderTotalQuantity \ stepCheckOrderTotalQuantity \
stepCreateNotVariatedResource \ stepCreateNotVariatedResource \
stepCreateOrderLine \ stepCreateOrderLine \
...@@ -2250,11 +2276,15 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase): ...@@ -2250,11 +2276,15 @@ class TestOrder(TestOrderMixin, ERP5TypeTestCase):
""" """
if not run: return if not run: return
portal = self.getPortal() portal = self.portal
base_id = 'movement' base_id = 'movement'
order_line_vcl=['size/Baby'] order_line_vcl=['size/Baby']
section = portal.organisation_module.newContent(
portal_type='Organisation')
order_module = portal.getDefaultModule(portal_type=self.order_portal_type) order_module = portal.getDefaultModule(portal_type=self.order_portal_type)
order = order_module.newContent(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) specialise=self.business_process)
# No line, no movement # No line, no movement
self.assertEquals(order.getTotalQuantity(fast=0), 0) self.assertEquals(order.getTotalQuantity(fast=0), 0)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment