diff --git a/product/ERP5/Extensions/InventoryBrain.py b/product/ERP5/Extensions/InventoryBrain.py index b74b6aa84571146037f8d4c8a3b069adb6bad76c..78991ba6de762e231b7fb39bc601d5f48184b58c 100644 --- a/product/ERP5/Extensions/InventoryBrain.py +++ b/product/ERP5/Extensions/InventoryBrain.py @@ -12,13 +12,41 @@ # ############################################################################## from Products.ZSQLCatalog.zsqlbrain import ZSQLBrain -from DateTime import DateTime +from Products.ERP5Type.TransactionalVariable import getTransactionalVariable from ZTUtils import make_query from Products.CMFCore.utils import getToolByName from zLOG import LOG, PROBLEM from Products.ERP5Type.Message import translateString from ComputedAttribute import ComputedAttribute +class ComputedAttributeGetItemCompatibleMixin(ZSQLBrain): + """A brain that supports accessing computed attributes using __getitem__ + protocol. + """ + def __init__(self): + # __getitem__ returns the computed attribute directly, but if we access + # brain['node_title'] we expect to have the attribute after computation, + # not the ComputedAttribute attribue instance. Defining a __getitem__ + # method on that class is not enough, because the brain class is not + # directly this class but a class created on the fly that also inherits + # from Record which already defines a __getitem__ method. + # We cannot patch the instance, because Record does not allow this kind of + # mutation, but as the class is created on the fly, for each query, it's + # safe to patch the class. See Shared/DC/ZRDB/Results.py for more detail. + # A Records holds a list of r instances, only the first __init__ needs to + # do this patching. + if not hasattr(self.__class__, '__super__getitem__'): + self.__class__.__super__getitem__ = self.__class__.__getitem__ + self.__class__.__getitem__ =\ + ComputedAttributeGetItemCompatibleMixin.__getitem__ + + # ComputedAttribute compatibility for __getitem__ + def __getitem__(self, name): + item = self.__super__getitem__(name) + if isinstance(item, ComputedAttribute): + return item.__of__(self) + return item + class InventoryBrain(ZSQLBrain): """ Global analysis (all variations and categories) @@ -55,7 +83,6 @@ class InventoryBrain(ZSQLBrain): list(self.getPortalCurrentInventoryStateList())) def getAvailableInventory(self): - at_date=DateTime() current = self.getCurrentInventory() result = self.Resource_zGetInventory( resource_uid=[self.resource_uid], ignore_variation=1, @@ -75,11 +102,11 @@ class InventoryBrain(ZSQLBrain): if resource is not None: return resource.getQuantityUnit() -class InventoryListBrain(ZSQLBrain): + +class InventoryListBrain(ComputedAttributeGetItemCompatibleMixin): """ Lists each variation """ - # Stock management def getInventory(self, **kw): simulation_tool = getToolByName(self, 'portal_simulation') @@ -110,13 +137,73 @@ class InventoryListBrain(ZSQLBrain): resource_uid=self.resource_uid, **kw) def getQuantityUnit(self, **kw): - resource = self.portal_catalog.getObject(self.resource_uid) + resource = self.getResourceValue() if resource is not None: return resource.getQuantityUnit() + def _getObjectByUid(self, uid): + uid_cache = getTransactionalVariable().setdefault( + 'InventoryBrain.uid_cache', {None: None}) + try: + return uid_cache[uid] + except KeyError: + result_list = self.portal_catalog(uid=uid, limit=1, + select_dict=dict(title=None, relative_url=None)) + result = None + if result_list: + result = result_list[0] + uid_cache[uid] = result + return result + + def getSectionValue(self): + return self._getObjectByUid(self.section_uid) + + def getSectionTitle(self): + section = self.getSectionValue() + if section is not None: + return section.title + section_title = ComputedAttribute(getSectionTitle, 1) + + def getSectionRelativeUrl(self): + section = self.getSectionValue() + if section is not None: + return section.relative_url + section_relative_url = ComputedAttribute(getSectionRelativeUrl, 1) + + def getNodeValue(self): + return self._getObjectByUid(self.node_uid) + + def getNodeTitle(self): + node = self.getNodeValue() + if node is not None: + return node.title + node_title = ComputedAttribute(getNodeTitle, 1) + + def getNodeRelativeUrl(self): + node = self.getNodeValue() + if node is not None: + return node.relative_url + node_relative_url = ComputedAttribute(getNodeRelativeUrl, 1) + + def getResourceValue(self): + return self._getObjectByUid(self.resource_uid) + + def getResourceTitle(self): + resource = self.getResourceValue() + if resource is not None: + return resource.title + resource_title = ComputedAttribute(getResourceTitle, 1) + + def getResourceRelativeUrl(self): + resource = self.getResourceValue() + if resource is not None: + return resource.relative_url + resource_relative_url = ComputedAttribute(getResourceRelativeUrl, 1) + def getListItemUrl(self, cname_id, selection_index, selection_name): """Returns the URL for column `cname_id`. Used by ListBox """ + resource = self.getResourceValue() if cname_id in ('getExplanationText', 'getExplanation', ): o = self.getObject() if o is not None: @@ -132,9 +219,8 @@ class InventoryListBrain(ZSQLBrain): explanation.getRelativeUrl()) else: return '' - elif getattr(self, 'resource_uid', None) is not None: + elif resource is not None: # A resource is defined, so try to display the movement list - resource = self.portal_catalog.getObject(self.resource_uid) form_name = 'Resource_viewMovementHistory' query_kw = { 'variation_text': self.variation_text, @@ -291,7 +377,6 @@ class DeliveryListBrain(InventoryListBrain): """ Returns current inventory at current date """ - at_date = DateTime() current = self.getCurrentInventory() result = self.Resource_zGetInventory( resource_uid = [self.resource_uid], @@ -323,6 +408,7 @@ class MovementHistoryListBrain(InventoryListBrain): """Brain for getMovementHistoryList """ def __init__(self): + InventoryListBrain.__init__(self) if not self.date: return # convert the date in the movement's original timezone. @@ -331,7 +417,7 @@ class MovementHistoryListBrain(InventoryListBrain): obj = self.getObject() if obj is not None: timezone = None - if self.node_relative_url == obj.getSource(): + if self.node_uid == obj.getSourceUid(): start_date = obj.getStartDate() if start_date is not None: timezone = start_date.timezone() diff --git a/product/ERP5/Tool/SimulationTool.py b/product/ERP5/Tool/SimulationTool.py index e26fb6691cb5127b3e7d9443713a2d73b1e06577..571b437e0181bf9453d80a1ce9a417605e999ada 100644 --- a/product/ERP5/Tool/SimulationTool.py +++ b/product/ERP5/Tool/SimulationTool.py @@ -715,8 +715,7 @@ class SimulationTool(BaseTool): column_value_dict.set('funding_uid', funding_uid) column_value_dict.set('payment_request_uid', payment_request_uid) column_value_dict.set('function_uid', function_uid) - if column_value_dict.set('section_uid', section_uid): - sql_kw['section_filtered'] = 1 + column_value_dict.set('section_uid', section_uid) column_value_dict.set('node_uid', node_uid) column_value_dict.set('mirror_node_uid', mirror_node_uid) column_value_dict.set('mirror_section_uid', mirror_section_uid) @@ -731,8 +730,7 @@ class SimulationTool(BaseTool): sql_kw['transformed_uid'] = self._generatePropertyUidList(transformed_resource) - if column_value_dict.setUIDList('section_uid', section): - sql_kw['section_filtered'] = 1 + column_value_dict.setUIDList('section_uid', section) column_value_dict.setUIDList('mirror_section_uid', mirror_section) column_value_dict.setUIDList('variation_text', variation_text, as_text=1) @@ -749,9 +747,7 @@ class SimulationTool(BaseTool): related_key_dict.setUIDList('payment_request_category_uid', payment_request_category) related_key_dict.setUIDList('function_category_uid', function_category) related_key_dict.setUIDList('payment_category_uid', payment_category) - if related_key_dict.setUIDList('section_category_uid', - section_category): - sql_kw['section_filtered'] = 1 + related_key_dict.setUIDList('section_category_uid', section_category) related_key_dict.setUIDList('mirror_section_category_uid', mirror_section_category) # category strict membership @@ -769,9 +765,8 @@ class SimulationTool(BaseTool): function_category_strict_membership) related_key_dict.setUIDList('payment_category_strict_membership_uid', payment_category_strict_membership) - if related_key_dict.setUIDList('section_category_strict_membership_uid', - section_category_strict_membership): - sql_kw['section_filtered'] = 1 + related_key_dict.setUIDList('section_category_strict_membership_uid', + section_category_strict_membership) related_key_dict.setUIDList( 'mirror_section_category_strict_membership_uid', mirror_section_category_strict_membership) diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventory.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventory.xml index 6518a08b41c7c72457e2fcbf701198393da76d39..a14b8dd030b02c17ca9db9156bc735cd3edd49ea 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventory.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventory.xml @@ -24,7 +24,6 @@ standardize\r\n omit_simulation\r\n omit_input\r\n omit_output\r\n -section_filtered\r\n input_simulation_state:list\r\n output_simulation_state:list\r\n group_by_expression\r\n @@ -78,12 +77,6 @@ SELECT\n SUM(stock.quantity) AS total_quantity,\n SUM(stock.total_price) AS total_price,\n </dtml-if>\n - COUNT(DISTINCT node.title) AS node_title,\n - COUNT(DISTINCT node.relative_url) AS node_relative_url,\n - COUNT(DISTINCT section.title) AS section_title,\n - COUNT(DISTINCT section.relative_url) AS section_relative_url,\n - COUNT(DISTINCT resource.title) AS resource_title,\n - COUNT(DISTINCT resource.relative_url) AS resource_relative_url,\n COUNT(DISTINCT stock.variation_text) AS variation_text,\n MAX(stock.resource_uid) AS resource_uid,\n COUNT(DISTINCT stock.uid) AS stock_uid,\n @@ -91,8 +84,6 @@ SELECT\n \n FROM\n stock\n - <dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if>\n - JOIN catalog AS section ON (section.uid = stock.section_uid)\n <dtml-in prefix="table" expr="from_table_list"> \n <dtml-if expr="table_key != \'stock\'">\n , <dtml-var table_item> AS <dtml-var table_key>\n @@ -102,16 +93,12 @@ FROM\n <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n <dtml-if selection_report>,\n <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n - , catalog as node, catalog as resource\n \n WHERE\n 1 = 1\n <dtml-if where_expression>\n AND <dtml-var where_expression>\n </dtml-if>\n -\n - AND node.uid = stock.node_uid\n - AND resource.uid = stock.resource_uid\n \n <dtml-if omit_simulation>\n AND catalog.portal_type != \'Simulation Movement\'\n diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml index d51348167d4e7f2b9abf63655477ac5973ffd34b..33f23d6901a2a8e5217808a76da3b423f7859a61 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetInventoryList.xml @@ -33,7 +33,6 @@ ignore_variation\r\n standardize\r\n omit_simulation\r\n only_accountable\r\n -section_filtered\r\n omit_input\r\n omit_output\r\n input_simulation_state:list\r\n @@ -92,7 +91,7 @@ SELECT\n SUM(ROUND(<dtml-var stock_table_id>.quantity\n <dtml-if transformed_uid> * transformation.quantity</dtml-if>, <dtml-var precision>)) AS total_quantity,\n <dtml-if convert_quantity_result>\n - SUM(ROUND(<dtml-var stock_table_id>.quantity * measure.quantity \n + SUM(ROUND(<dtml-var stock_table_id>.quantity * measure.quantity\n <dtml-if quantity_unit_uid> / quantity_unit_conversion.quantity</dtml-if>\n <dtml-if transformed_uid> * transformation.quantity</dtml-if>, <dtml-var precision>))\n AS converted_quantity,\n @@ -110,20 +109,15 @@ SELECT\n IFNULL(SUM(<dtml-var stock_table_id>.total_price), 0) AS total_price\n </dtml-if>\n <dtml-if inventory_list>\n - ,node.title AS node_title,\n - node.uid AS node_uid,\n - node.relative_url AS node_relative_url,\n - section.title AS section_title,\n - section.uid AS section_uid,\n - section.relative_url AS section_relative_url,\n - resource.title AS resource_title,\n - resource.relative_url AS resource_relative_url,\n -<dtml-if transformed_uid>\n - transformed_resource.title AS transformed_resource_title,\n - transformed_resource.relative_url AS transformed_resource_relative_url,\n - transformation.transformed_uid AS transformed_resource_uid,\n - transformation.transformed_variation_text AS transformed_variation_text,\n -</dtml-if>\n + ,\n + <dtml-var stock_table_id>.node_uid AS node_uid,\n + <dtml-var stock_table_id>.section_uid AS section_uid,\n + <dtml-if transformed_uid>\n + transformed_resource.title AS transformed_resource_title,\n + transformed_resource.relative_url AS transformed_resource_relative_url,\n + transformation.transformed_uid AS transformed_resource_uid,\n + transformation.transformed_variation_text AS transformed_variation_text,\n + </dtml-if>\n <dtml-var stock_table_id>.resource_uid AS resource_uid,\n <dtml-var stock_table_id>.variation_text AS variation_text,\n <dtml-var stock_table_id>.sub_variation_text AS sub_variation_text,\n @@ -142,12 +136,6 @@ SELECT\n </dtml-if>\n <dtml-if statistic>\n ,\n - COUNT(DISTINCT node.title) AS node_title,\n - COUNT(DISTINCT node.relative_url) AS node_relative_url,\n - COUNT(DISTINCT section.title) AS section_title,\n - COUNT(DISTINCT section.relative_url) AS section_relative_url,\n - COUNT(DISTINCT resource.title) AS resource_title,\n - COUNT(DISTINCT resource.relative_url) AS resource_relative_url,\n COUNT(DISTINCT <dtml-var stock_table_id>.variation_text) AS variation_text,\n MAX(<dtml-var stock_table_id>.resource_uid) AS resource_uid,\n COUNT(DISTINCT <dtml-var stock_table_id>.uid) AS stock_uid,\n @@ -157,8 +145,6 @@ SELECT\n \n FROM\n catalog, <dtml-var stock_table_id>\n - <dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if> \n - JOIN catalog AS section ON (section.uid = <dtml-var stock_table_id>.section_uid)\n <dtml-if quantity_unit_uid>\n LEFT JOIN quantity_unit_conversion ON \n (quantity_unit_conversion.resource_uid = <dtml-var stock_table_id>.resource_uid\n @@ -171,19 +157,16 @@ FROM\n </dtml-in>\n <dtml-if selection_domain>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n <dtml-if selection_report>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n - , catalog as node, catalog as resource <dtml-if transformed_uid>, transformation, catalog as transformed_resource</dtml-if>\n + <dtml-if transformed_uid>, transformation, catalog as transformed_resource</dtml-if>\n \n WHERE\n <dtml-var stock_table_id>.uid = catalog.uid\n <dtml-if where_expression>\n AND <dtml-var where_expression>\n </dtml-if>\n -\n - AND node.uid = <dtml-var stock_table_id>.node_uid\n \n <dtml-if transformed_uid>\n AND transformation.uid = <dtml-var stock_table_id>.resource_uid\n - AND resource.uid = transformation.uid\n AND <dtml-var stock_table_id>.variation_text = transformation.variation_text\n \n AND transformed_resource.uid = transformation.transformed_uid\n @@ -191,8 +174,6 @@ WHERE\n <dtml-if transformed_variation_text>\n AND <dtml-sqltest transformed_variation_text column="transformation.transformed_variation_text" type=string>\n </dtml-if>\n -<dtml-else>\n - AND resource.uid = <dtml-var stock_table_id>.resource_uid\n </dtml-if>\n \n <dtml-if omit_simulation>\n @@ -212,7 +193,6 @@ WHERE\n AND concat(<dtml-var stock_table_id>.variation_text,\'\\n\') REGEXP measure.variation\n </dtml-if>\n \n -\n <dtml-if group_by_expression>\n GROUP BY\n <dtml-if transformed_uid>transformation.transformed_uid,</dtml-if>\n diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetMovementHistoryList.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetMovementHistoryList.xml index df8fc10cc28253944deac856d03108219cf0b90f..7189f8d647394f8ddb304284cc36d79d1a4e888a 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetMovementHistoryList.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Resource_zGetMovementHistoryList.xml @@ -432,7 +432,6 @@ omit_input\r\n omit_output\r\n omit_asset_increase\r\n omit_asset_decrease\r\n -section_filtered\r\n initial_running_total_quantity\r\n initial_running_total_price\r\n input_simulation_state:list\r\n @@ -510,16 +509,10 @@ SELECT\n stock.project_uid AS project_uid,\n stock.funding_uid AS funding_uid,\n stock.payment_request_uid AS payment_request_uid,\n - node.uid AS node_uid,\n - node.title AS node_title,\n - node.relative_url AS node_relative_url,\n - section.uid AS section_uid,\n - section.title AS section_title,\n - section.relative_url AS section_relative_url\n + stock.node_uid AS node_uid,\n + stock.section_uid AS section_uid\n FROM\n stock\n - <dtml-if section_filtered> INNER <dtml-else> LEFT </dtml-if> \n - JOIN catalog AS section ON (section.uid = stock.section_uid)\n <dtml-in prefix="table" expr="from_table_list"> \n <dtml-if expr="table_key != \'stock\'">\n , <dtml-var table_item> AS <dtml-var table_key>\n @@ -527,16 +520,12 @@ FROM\n </dtml-in>\n <dtml-if selection_domain>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_domain)"> </dtml-if>\n <dtml-if selection_report>, <dtml-var "portal_selections.buildSQLJoinExpressionFromDomainSelection(selection_report)"> </dtml-if>\n - , catalog as node, catalog as resource\n \n WHERE\n 1 = 1\n <dtml-if where_expression>\n AND <dtml-var where_expression>\n </dtml-if>\n -\n - AND node.uid = stock.node_uid\n - AND resource.uid = stock.resource_uid\n \n <dtml-if omit_simulation>\n AND catalog.portal_type != \'Simulation Movement\'\n @@ -615,7 +604,7 @@ WHERE\n \n <dtml-if group_by_expression>\n GROUP BY\n - <dtml-var group_by_expression>\n + <dtml-var group_by_expression>\n </dtml-if>\n \n <dtml-if order_by_expression>\n diff --git a/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_related_metric_type.xml b/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_related_metric_type.xml index 865573a29a6f21a72423b90017b52a0483f8b1ca..f5b5b776bfb5edeb857398facf83498e68976dfc 100644 --- a/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_related_metric_type.xml +++ b/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_related_metric_type.xml @@ -54,7 +54,7 @@ <key> <string>src</string> </key> <value> <string encoding="cdata"><![CDATA[ -<dtml-var table_0>.resource_uid = resource.uid +<dtml-var table_0>.resource_uid = stock.resource_uid ]]></string> </value> </item> diff --git a/product/ERP5/tests/testInventoryAPI.py b/product/ERP5/tests/testInventoryAPI.py index ba6e4bcb05b2443ea0f99564d2b52062efd35eff..68406b0cb56d55119e791e1eabceee303666c8ff 100644 --- a/product/ERP5/tests/testInventoryAPI.py +++ b/product/ERP5/tests/testInventoryAPI.py @@ -1436,7 +1436,7 @@ class TestMovementHistoryList(InventoryAPITestCase): """Test attributes exposed on brains.""" getMovementHistoryList = self.getSimulationTool().getMovementHistoryList self._makeMovement(quantity=100) - brain = getMovementHistoryList()[0] + brain = getMovementHistoryList(section_uid=self.section.getUid())[0] self.assertTrue(hasattr(brain, 'node_uid')) self.assertTrue(hasattr(brain, 'resource_uid')) self.assertTrue(hasattr(brain, 'section_uid')) @@ -1448,6 +1448,36 @@ class TestMovementHistoryList(InventoryAPITestCase): self.assertTrue(hasattr(brain, 'mirror_node_uid')) self.assertTrue(hasattr(brain, 'mirror_section_uid')) + # compatiblity names + self.assertTrue(hasattr(brain, 'section_title')) + self.assertEquals(brain.section_title, self.section.getTitle()) + self.assertTrue(hasattr(brain, 'section_relative_url')) + self.assertEquals(brain.section_relative_url, self.section.getRelativeUrl()) + self.assertTrue(hasattr(brain, 'node_title')) + self.assertEquals(brain.node_title, self.node.getTitle()) + self.assertTrue(hasattr(brain, 'node_relative_url')) + self.assertEquals(brain.node_relative_url, self.node.getRelativeUrl()) + self.assertTrue(hasattr(brain, 'resource_title')) + self.assertEquals(brain.resource_title, self.resource.getTitle()) + self.assertTrue(hasattr(brain, 'resource_relative_url')) + self.assertEquals(brain.resource_relative_url, self.resource.getRelativeUrl()) + + def testBrainGetItem(self): + """Test __getitem__ interface on brains.""" + getMovementHistoryList = self.getSimulationTool().getMovementHistoryList + self._makeMovement(quantity=100) + brain = getMovementHistoryList(section_uid=self.section.getUid())[0] + + self.assertEquals(brain['node_uid'], self.node.getUid()) + self.assertEquals(brain['node_relative_url'], self.node.getRelativeUrl()) + self.assertEquals(brain['node_title'], self.node.getTitle()) + self.assertEquals(brain['section_uid'], self.section.getUid()) + self.assertEquals(brain['section_relative_url'], self.section.getRelativeUrl()) + self.assertEquals(brain['section_title'], self.section.getTitle()) + self.assertEquals(brain['resource_uid'], self.resource.getUid()) + self.assertEquals(brain['resource_relative_url'], self.resource.getRelativeUrl()) + self.assertEquals(brain['resource_title'], self.resource.getTitle()) + def testSection(self): getMovementHistoryList = self.getSimulationTool().getMovementHistoryList mvt = self._makeMovement(quantity=100)