Commit f01c030e authored by Georgios Dagkakis's avatar Georgios Dagkakis

SimulationTool: try more flexible definition of join_column for selection_domain

Changes also in erp5_core:
- Resource_zGetInventoryList and Resource_zGetMovementHistoryList
to provide support for receiving the join_column as arg
- Add a generic Base_getJoinColumnForSelectionDomain mapping

Finally, in add two tests to testInventoryAPI to check queries
with selection_domain for getInventoryList and getMovementHistoryList
parent 0eed2ec0
......@@ -59,6 +59,7 @@ from warnings import warn
from cPickle import loads, dumps
from copy import deepcopy
from sys import exc_info
from Products.ERP5Form.Selection import DomainSelection
MYSQL_MIN_DATETIME_RESOLUTION = 1/86400.
......@@ -1187,6 +1188,12 @@ class SimulationTool(BaseTool):
new_group_by_dict['group_by_resource'] = 1
return new_group_by_dict
security.declarePrivate('_getJoinColumnForSelectionDomain')
def _getJoinColumnForSelectionDomain(self, selection_domain_dict):
if not selection_domain_dict:
return
return self.getPortalObject().Base_getJoinColumnForSelectionDomain(selection_domain_dict.keys()[0])
security.declareProtected(Permissions.AccessContentsInformation,
'getInventoryList')
def getInventoryList(self, src__=0, optimisation__=True,
......@@ -1268,6 +1275,8 @@ class SimulationTool(BaseTool):
sql_source_list = []
# If no group at all, give a default sort group by
kw.update(self._getDefaultGroupByParameters(**kw))
if isinstance(selection_domain, DomainSelection):
selection_domain = selection_domain.asDomainDict()
base_inventory_kw = {
'stock_table_id': default_stock_table,
'src__': src__,
......@@ -1276,6 +1285,7 @@ class SimulationTool(BaseTool):
'omit_simulation': omit_simulation,
'only_accountable': only_accountable,
'selection_domain': selection_domain,
'join_column': self._getJoinColumnForSelectionDomain(selection_domain),
'selection_report': selection_report,
'precision': precision,
'inventory_list': inventory_list,
......@@ -1972,6 +1982,8 @@ class SimulationTool(BaseTool):
for x in extra_column_set if not x.endswith('__score__'))
sql_kw = self._generateSQLKeywordDict(**kw)
if isinstance(selection_domain, DomainSelection):
selection_domain = selection_domain.asDomainDict()
return self.Resource_zGetMovementHistoryList(
src__=src__, ignore_variation=ignore_variation,
......@@ -1982,6 +1994,7 @@ class SimulationTool(BaseTool):
omit_asset_increase=omit_asset_increase,
omit_asset_decrease=omit_asset_decrease,
selection_domain=selection_domain,
join_column=self._getJoinColumnForSelectionDomain(selection_domain),
selection_report=selection_report,
initial_running_total_quantity=
initial_running_total_quantity,
......
'''
This script is called by Simulation Tool in order to obtain the
join_column for a getInventoryList or getMovementHistoryList query.
In the past this supported only node_uid, so keep this as default value
for backwards compatibility. Script can be customized for projects.
XXX, Still it is not flexible, i.e. it does not support the case of e.g.
have a domain on site category sometime used as node_category
and sometimes as mirror_node_category or section_category.
'''
return {
'ledger': 'ledger_uid',
}.get(selection_key, 'node_uid')
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>selection_key</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Base_getJoinColumnForSelectionDomain</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -63,7 +63,7 @@ FROM
<dtml-var from_expression>
<dtml-else>
catalog
<dtml-in prefix="table" expr="from_table_list">
<dtml-in prefix="table" expr="from_table_list">
<dtml-if expr="table_key not in ('catalog', stock_table_id)">
, <dtml-var table_item> AS <dtml-var table_key>
</dtml-if>
......@@ -71,7 +71,7 @@ FROM
, <dtml-var stock_table_id>
</dtml-if>
<dtml-if quantity_unit_uid> <dtml-comment>XXX quantity unit conversion will not work when using implict_join=False</dtml-comment>
LEFT JOIN quantity_unit_conversion ON
LEFT JOIN quantity_unit_conversion ON
(quantity_unit_conversion.resource_uid = <dtml-var stock_table_id>.resource_uid
AND quantity_unit_conversion.quantity_unit_uid = <dtml-sqlvar quantity_unit_uid type=int>)
</dtml-if>
......@@ -103,7 +103,8 @@ WHERE
AND <dtml-var stock_table_id>.is_accountable
</dtml-if>
<dtml-if selection_domain>
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_domain, category_table_alias='domain_category', join_table=stock_table_id, join_column='node_uid')">
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_domain, category_table_alias='domain_category', join_table=stock_table_id, join_column=join_column)">
</dtml-if>
<dtml-if selection_report>
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_report, category_table_alias='report_category', strict_membership=1)">
......
......@@ -22,29 +22,30 @@
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>from_table_list:list\r\n
from_expression\r\n
where_expression\r\n
order_by_expression\r\n
group_by_expression\r\n
selection_domain\r\n
select_expression\r\n
selection_report\r\n
ignore_variation\r\n
standardize\r\n
omit_simulation\r\n
only_accountable\r\n
omit_input\r\n
omit_output\r\n
input_simulation_state:list\r\n
output_simulation_state:list\r\n
precision\r\n
inventory_list\r\n
statistic\r\n
convert_quantity_result\r\n
quantity_unit_uid\r\n
stock_table_id=stock\r\n
transformed_uid\r\n
<value> <string>from_table_list:list\n
from_expression\n
where_expression\n
order_by_expression\n
group_by_expression\n
selection_domain\n
join_column\n
select_expression\n
selection_report\n
ignore_variation\n
standardize\n
omit_simulation\n
only_accountable\n
omit_input\n
omit_output\n
input_simulation_state:list\n
output_simulation_state:list\n
precision\n
inventory_list\n
statistic\n
convert_quantity_result\n
quantity_unit_uid\n
stock_table_id=stock\n
transformed_uid\n
transformed_variation_text</string> </value>
</item>
<item>
......
......@@ -8,11 +8,11 @@ SET @running_total_quantity := <dtml-var initial_running_total_quantity>,
@running_total_price := <dtml-var initial_running_total_price>;
<dtml-var sql_delimiter>
SELECT
SELECT
q1.*,
@running_total_quantity := q1.total_quantity +
@running_total_quantity := q1.total_quantity +
@running_total_quantity AS running_total_quantity,
@running_total_price := IFNULL(q1.total_price, 0) +
@running_total_price := IFNULL(q1.total_price, 0) +
@running_total_price AS running_total_price
FROM (
SELECT
......@@ -133,7 +133,7 @@ WHERE
</dtml-if>
<dtml-if selection_domain>
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_domain, category_table_alias='domain_category', join_table='stock', join_column='node_uid')">
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_domain, category_table_alias='domain_category', join_table='stock', join_column=join_column)">
</dtml-if>
<dtml-if selection_report>
AND <dtml-var "portal_selections.buildSQLExpressionFromDomainSelection(selection_report, category_table_alias='report_category', strict_membership=1)">
......
......@@ -417,27 +417,28 @@
</item>
<item>
<key> <string>arguments_src</string> </key>
<value> <string>from_expression\r\n
from_table_list:list\r\n
select_expression\r\n
where_expression\r\n
order_by_expression\r\n
group_by_expression\r\n
limit_expression\r\n
selection_domain\r\n
selection_report\r\n
ignore_variation\r\n
standardize\r\n
omit_simulation\r\n
only_accountable\r\n
omit_input\r\n
omit_output\r\n
omit_asset_increase\r\n
omit_asset_decrease\r\n
initial_running_total_quantity\r\n
initial_running_total_price\r\n
input_simulation_state:list\r\n
output_simulation_state:list\r\n
<value> <string>from_expression\n
from_table_list:list\n
select_expression\n
where_expression\n
order_by_expression\n
group_by_expression\n
limit_expression\n
selection_domain\n
join_column\n
selection_report\n
ignore_variation\n
standardize\n
omit_simulation\n
only_accountable\n
omit_input\n
omit_output\n
omit_asset_increase\n
omit_asset_decrease\n
initial_running_total_quantity\n
initial_running_total_price\n
input_simulation_state:list\n
output_simulation_state:list\n
precision</string> </value>
</item>
<item>
......
......@@ -188,7 +188,7 @@ class InventoryAPITestCase(ERP5TypeTestCase):
'erp5_dummy_movement', 'erp5_simulation',
'erp5_trade', 'erp5_apparel', 'erp5_project',
'erp5_configurator_standard_trade_template',
'erp5_simulation_test', 'erp5_stock_cache')
'erp5_simulation_test', 'erp5_stock_cache', )
# TODO: move this to a base class {{{
@reindex
......@@ -1571,6 +1571,49 @@ class TestInventoryList(InventoryAPITestCase):
self.assertTrue(result is not None)
self.assertEqual(internal_data[cur]['after']['total_price'], round(result))
def testGetInventoryListWithSelectionDomain(self):
'''
check getInventoryList queries with selection_domain
'''
getInventoryList = self.getSimulationTool().getInventoryList
ledger_accounting_category = self.portal.portal_categories.ledger.accounting
self.node.setGroup('level1')
movement1 = self._makeMovement(
ledger_value=ledger_accounting_category.general,
destination_value=None,
quantity=2,
)
movement2 = self._makeMovement(
ledger_value=ledger_accounting_category.detailed,
destination_value=None,
source_value=self.node,
quantity=3,
)
# query without selection. Result should contain both movements
result_no_selection = getInventoryList()
self.assertEquals(len(result_no_selection), 2)
self.assertEquals(
sum([x.total_quantity for x in result_no_selection]),
-5.0
)
# query using ledger in the selection_domain (so ledger_uid).
# Check only the corresponding movement is found
result_ledger_accounting_general = getInventoryList(
selection_domain={'ledger': ('portal_categories', 'ledger/accounting/general')})
self.assertEquals(len(result_ledger_accounting_general), 1)
self.assertEquals(result_ledger_accounting_general[0].total_quantity, -2.0)
result_ledger_accounting_detailed = getInventoryList(
selection_domain={'ledger': ('portal_categories', 'ledger/accounting/detailed')})
self.assertEquals(len(result_ledger_accounting_detailed), 1)
self.assertEquals(result_ledger_accounting_detailed[0].total_quantity, -3.0)
# query using group in the selection_domain (so node_uid).
# Check only the corresponding movement is found
result_group1 = getInventoryList(
selection_domain={'group': ('portal_categories', 'group/level1')})
self.assertEquals(len(result_group1), 1)
self.assertEquals(result_group1[0].total_quantity, -3.0)
class TestMovementHistoryList(InventoryAPITestCase):
"""Tests Movement history list methods.
......@@ -2471,6 +2514,50 @@ class TestMovementHistoryList(InventoryAPITestCase):
self.assertEqual(7, mvt_history_list[1].total_price)
self.assertEqual(12, mvt_history_list[1].running_total_price)
def testGetMovementHistoryListWithSelectionDomain(self):
'''
check getMovementHistoryList queries with selection_domain
'''
getMovementHistoryList = self.getSimulationTool().getMovementHistoryList
ledger_accounting_category = self.portal.portal_categories.ledger.accounting
self.node.setGroup('level1')
movement1 = self._makeMovement(
ledger_value=ledger_accounting_category.general,
destination_value=None,
quantity=2,
)
movement2 = self._makeMovement(
ledger_value=ledger_accounting_category.detailed,
destination_value=None,
source_value=self.node,
quantity=3,
)
# query without selection. Result should contain both movements
result_no_selection = getMovementHistoryList()
self.assertEquals(len(result_no_selection), 2)
self.assertEquals(
sum([x.total_quantity for x in result_no_selection]),
-5.0
)
# query using ledger in the selection_domain (so ledger_uid).
# Check only the corresponding movement is found
result_ledger_accounting_general = getMovementHistoryList(
selection_domain={'ledger': ('portal_categories', 'ledger/accounting/general')})
self.assertEquals(len(result_ledger_accounting_general), 1)
self.assertEquals(result_ledger_accounting_general[0].total_quantity, -2.0)
result_ledger_accounting_detailed = getMovementHistoryList(
selection_domain={'ledger': ('portal_categories', 'ledger/accounting/detailed')})
self.assertEquals(len(result_ledger_accounting_detailed), 1)
self.assertEquals(result_ledger_accounting_detailed[0].total_quantity, -3.0)
# query using group in the selection_domain (so node_uid).
# Check only the corresponding movement is found
result_group1 = getMovementHistoryList(
selection_domain={'group': ('portal_categories', 'group/level1')})
self.assertEquals(len(result_group1), 1)
self.assertEquals(result_group1[0].total_quantity, -3.0)
class TestNextNegativeInventoryDate(InventoryAPITestCase):
"""Tests getInventory methods.
......
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