Commit aec822e2 authored by Rafael Monnerat's avatar Rafael Monnerat

Reimplement Rules Configurator Item and Business Process Configurator Item

The Rules are now used as template and not as it is anymore, the rules are
cloned and updated with appropriate categories defined by the configurator.

Business Process Configurator Item was reimplemented to use
spreadsheet to generate an Business Process.
parent 399caf39
......@@ -8,6 +8,9 @@
<portal_type id="Business Configuration Module">
<item>Business Configuration</item>
</portal_type>
<portal_type id="Business Process Configurator Item">
<item>Embedded File</item>
</portal_type>
<portal_type id="Categories Spreadsheet Configurator Item">
<item>Embedded File</item>
<item>File</item>
......
......@@ -3,4 +3,7 @@
<item>causality</item>
<item>current_state</item>
</portal_type>
<portal_type id="Rule Configurator Item">
<item>trade_phase</item>
</portal_type>
</base_category_list>
\ No newline at end of file
......@@ -3,6 +3,7 @@ Business Configuration | Configuration Save
Business Configuration | Embedded File
Business Configuration | File
Business Configuration | Link
Business Process Configurator Item | Embedded File
Categories Spreadsheet Configurator Item | Embedded File
Categories Spreadsheet Configurator Item | File
Configuration Save | Account Configurator Item
......
Configuration Save | causality
Configuration Save | current_state
\ No newline at end of file
Configuration Save | current_state
Rule Configurator Item | trade_phase
\ No newline at end of file
......@@ -93,18 +93,7 @@ if business_template is not None:\n
gadget.visible()\n
gadget.public()\n
\n
for rule in context.portal_rules.searchFolder(id=[ "new_order_root_simulation_rule", \n
"new_delivery_root_simulation_rule",\n
"new_trade_model_simulation_rule",\n
"new_accounting_transaction_root_simulation_rule",\n
"new_invoice_transaction_simulation_rule",\n
"new_payment_simulation_rule",\n
"new_invoice_root_simulation_rule",\n
"new_delivery_root_simulation_rule",\n
"new_invoice_simulation_rule"]):\n
\n
if isTransitionPossible(rule, \'validate\'):\n
rule.validate(comment="Validated by Configurator")\n
\n
\n
\n
# update security settings for default preference # XXX why ???\n
......
......@@ -52,33 +52,7 @@
<key> <string>_body</string> </key>
<value> <string>configuration_save = context.restrictedTraverse(configuration_save_url)\n
\n
# setup Rules\n
configuration_save.addConfigurationItem("Rule Configurator Item")\n
\n
# setup Business Process\n
configuration_save.addConfigurationItem("Business Process Configurator Item",\n
title="General Business Process",\n
reference="erp5_default_business_process")\n
\n
# setup Sale Trade Condition\n
configuration_save.addConfigurationItem("Sale Trade Condition Configurator Item",\n
title="General Sale Trade Condition",\n
reference="STC-General")\n
\n
# setup Purchase Trade Condition\n
configuration_save.addConfigurationItem("Purchase Trade Condition Configurator Item",\n
title="General Purchase Trade Condition",\n
reference="PTC-General")\n
\n
# setup Solvers\n
for property_dict in context.BusinessConfiguration_getSolverPropertyDict().itervalues():\n
configuration_save.addConfigurationItem("Solver Configurator Item", **property_dict)\n
\n
# web site module security\n
# we will not provide web_site_module for now.\n
configuration_save.addConfigurationItem("Permission Configurator Item",\n
filename="standard_module_permission_access.ods")\n
context.BusinessConfiguration_setupSimulation(configuration_save_url, **kw)\n
\n
# Catalog Keyword Search Keys are for now hardcoded.\n
configuration_save.addConfigurationItem("Catalog Keyword Key Configurator Item",\n
......
<?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>_body</string> </key>
<value> <string>return {\'Accept Solver\': {\'type_acquire_local_role\': 1,\n
\'configuration_form_id\': \'Solver_viewConfigurationFormBox\',\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'description\': \'The Accept Solver solves the divergence by accepting the decision and updating a simulation movement.\',\n
\'type_factory_method_id\': \'addAcceptSolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Accept Solver\',\n
\'line_exclusive\': 0,\n
\'line_groupable\': 0,\n
\'solver_action_title\': \'Accept new value\'},\n
\'Adopt Solver\': {\'type_acquire_local_role\': 1,\n
\'configuration_form_id\': \'Solver_viewConfigurationFormBox\',\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'description\': \'The adoption solver resets the value of the delivery movement to the total of the values of related simulations. It eventually updates the delivery_ratio in the simulations.\',\n
\'type_factory_method_id\': \'addAdoptSolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Adopt Solver\',\n
\'solver_action_title\': \'Replace value\'},\n
\'FIFO Delivery Solver\': {\'type_acquire_local_role\': 1,\n
\'content_list\': [],\n
\'description\': \'The FIFO solver reduces delivered quantity by reducing the quantity of simulation movements from the last order.\',\n
\'type_factory_method_id\': \'addFIFODeliverySolver\',\n
\'type_group_list\': (\'delivery_solver\',),\n
\'id\': \'FIFO Delivery Solver\',\n
\'solver_action_title\': \'First In, First Out\'},\n
\'LIFO Delivery Solver\': {\'type_acquire_local_role\': 1,\n
\'content_list\': [],\n
\'description\': \'The LIFO solver reduces delivered quantity by reducing the quantity of simulation movements from the last order.\',\n
\'type_factory_method_id\': \'addLIFODeliverySolver\',\n
\'type_group_list\': (\'delivery_solver\',),\n
\'id\': \'LIFO Delivery Solver\',\n
\'solver_action_title\': \'Last In, First Out\'},\n
\'Minimise Price Delivery Solver\': {\'type_acquire_local_role\': 1,\n
\'content_list\': [],\n
\'description\': \'The Minimise Price solver reduces delivered quantity by reducing the quantity of simulation movements from the last order.\',\n
\'type_factory_method_id\': \'addMinimisePriceDeliverySolver\',\n
\'type_group_list\': (\'delivery_solver\',),\n
\'id\': \'Minimise Price Delivery Solver\',\n
\'solver_action_title\': \'Minimise Price\'},\n
\'Production Reduction Solver\': {\'content_list\': [],\n
\'description\': \'Production Reduction Solver is only a placeholder for now. (it will need its own class later)\',\n
\'id\': \'Production Reduction Solver\'},\n
\'Quantity Cancel Solver\': {\'type_acquire_local_role\': 1,\n
\'configuration_form_id\': \'Solver_viewConfigurationFormBox\',\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'description\': \'The Quantity Cancel Solver is used to cancel the delivery of part of a shipment and prevent any further expand on the parent to generate changes. (as long the expanded quantity does not change)\',\n
\'type_factory_method_id\': \'addQuantityCancelSolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Quantity Cancel Solver\',\n
\'line_exclusive\': 0,\n
\'line_groupable\': 0,\n
\'solver_action_title\': \'Cancel Quantity\'},\n
\'Quantity Split Solver\': {\'type_acquire_local_role\': 1,\n
\'automatic_solver\': 0,\n
\'categories\': (\'delivery_solver/portal_solvers/FIFO Delivery Solver\',\n
\'delivery_solver/portal_solvers/LIFO Delivery Solver\',\n
\'delivery_solver/portal_solvers/Minimise Price Delivery Solver\'),\n
\'configuration_form_id\': \'QuantitySplitSolver_viewConfigurationFormBox\',\n
\'configuration_groupable\': 0,\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'description\': \'The Quantity Split Solver splits a simulation movement into multiple simulation movements in order to defer the delivery of remaining quantities.\',\n
\'type_factory_method_id\': \'addQuantitySplitSolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Quantity Split Solver\',\n
\'line_exclusive\': 0,\n
\'line_groupable\': 0,\n
\'process_exclusive\': 0,\n
\'solver_action_title\': \'Split and Differ\'},\n
\'Trade Model Solver\': {\'type_acquire_local_role\': 1,\n
\'categories\': (\'conflicting_solver/portal_solvers/Accept Solver\',),\n
\'configuration_form_id\': \'Solver_viewConfigurationFormBox\',\n
\'configuration_groupable\': 0,\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'description\': \'The Trade Model Solver solves the divergence by accepting the decision and updating a simulation movement, and also updates trade model related movements.\',\n
\'type_factory_method_id\': \'addTradeModelSolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Trade Model Solver\',\n
\'line_exclusive\': 0,\n
\'line_groupable\': 0,\n
\'process_exclusive\': 0,\n
\'solver_action_title\': \'Accept values from Invoice Line and recalculate Trade Model related Invoice Lines\',\n
\'tested_property\': (\'quantity\',)},\n
\'Unify Solver\': {\'type_acquire_local_role\': 1,\n
\'automatic_solver\': 0,\n
\'configuration_form_id\': \'UnifySolver_viewConfigurationFormBox\',\n
\'configuration_groupable\': 0,\n
\'configuration_property_list_dict_method_id\': \'UnifySolver_getConfigurationPropertyListDict\',\n
\'content_list\': [{\'action\': \'string:${object_url}/Solver_viewConfiguration\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 2.0,\n
\'id\': \'1\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'configuration\',\n
\'title\': \'Configuration\',\n
\'visible_property\': 1},\n
{\'action\': \'string:${object_url}/TargetSolver_view\',\n
\'action_permission\': \'View\',\n
\'categories\': (\'action_type/object_view\',),\n
\'float_index\': 1.0,\n
\'id\': \'2\',\n
\'portal_type\': \'Action Information\',\n
\'reference\': \'view\',\n
\'title\': \'View\',\n
\'visible_property\': 1}],\n
\'default_configuration_property_dict_method_id\': \'UnifySolver_getDefaultConfigurationPropertyDict\',\n
\'description\': \'The Unify Solver solves the divergence by unifying the value with a selected one and updating a simulation movement.\',\n
\'type_factory_method_id\': \'addUnifySolver\',\n
\'type_group_list\': (\'target_solver\',),\n
\'id\': \'Unify Solver\',\n
\'line_exclusive\': 0,\n
\'line_groupable\': 0,\n
\'process_exclusive\': 0,\n
\'solver_action_title\': \'Unify value\'}}\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BusinessConfiguration_getSolverPropertyDict</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,7 +51,6 @@
<item>
<key> <string>_body</string> </key>
<value> <string>configuration_save = context.restrictedTraverse(configuration_save_url)\n
group_id = context.getGlobalConfigurationAttr(\'group_id\')\n
\n
context.setGlobalConfigurationAttr(\n
portal_type_roles_spreadsheet_configuration_save_relative_url=configuration_save.getRelativeUrl())\n
......
......@@ -50,107 +50,44 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>return [ {\'int_index\': 4,\n
\'title\': \'pay\',\n
\'portal_type\': \'Business Link\',\n
\'deliverable\': 0,\n
\'id\': \'pay\',\n
\'categories\': (\'delivery_builder/portal_deliveries/payment_transaction_builder\',\n
\'trade_phase/default/payment\',\n
\'predecessor/trade_state/accounted\',\n
\'successor/trade_state/paid\',\n
\'source/account_module/bank\',\n
\'destination/account_module/bank\')},\n
{\'default_reference\': \'TMP-ORDER\',\n
\'int_index\': 1,\n
\'title\': \'Order\',\n
\'portal_type\': \'Trade Model Path\',\n
\'id\': \'order_path\',\n
\'categories\': (\'incoterm/a\',\n
\'trade_phase/default/order\',\n
\'trade_date/trade_phase/default/order\')},\n
{\'default_reference\': \'TMP-INVOICE\',\n
\'int_index\': 3,\n
\'title\': \'Invoicing\',\n
\'portal_type\': \'Trade Model Path\',\n
\'payment_end_of_month\': 0,\n
\'id\': \'invoice_path\',\n
\'categories\': (\'trade_date/trade_phase/default/delivery\',\n
\'trade_phase/default/invoicing\')},\n
{\'int_index\': 3,\n
\'title\': \'account\',\n
\'portal_type\': \'Business Link\',\n
\'deliverable\': 0,\n
\'completed_state\': (\'delivered\', \'started\', \'stopped\'),\n
\'frozen_state\': (\'delivered\', \'stopped\'),\n
\'id\': \'account\',\n
\'categories\': (\'delivery_builder/portal_deliveries/purchase_invoice_transaction_builder\',\n
\'delivery_builder/portal_deliveries/sale_invoice_transaction_builder\',\n
\'trade_phase/default/accounting\',\n
\'predecessor/trade_state/invoiced\',\n
\'successor/trade_state/accounted\')},\n
{\'int_index\': 2,\n
\'title\': \'invoice\',\n
\'portal_type\': \'Business Link\',\n
\'deliverable\': 0,\n
\'completed_state\': (\'delivered\', \'started\', \'stopped\'),\n
\'frozen_state\': (\'delivered\', \'stopped\'),\n
\'id\': \'invoice\',\n
\'categories\': (\'delivery_builder/portal_deliveries/sale_invoice_builder\',\n
\'trade_phase/default/invoicing\',\n
\'predecessor/trade_state/delivered\',\n
\'successor/trade_state/invoiced\')},\n
{\'int_index\': 0,\n
\'title\': \'order\',\n
\'portal_type\': \'Business Link\',\n
\'deliverable\': 1,\n
\'completed_state\': (\'confirmed\',),\n
\'id\': \'order\',\n
\'categories\': (\'trade_phase/default/order\',\n
\'successor/trade_state/ordered\')},\n
{\'int_index\': 1,\n
\'title\': \'deliver\',\n
\'portal_type\': \'Business Link\',\n
\'deliverable\': 1,\n
\'completed_state\': (\'delivered\', \'started\', \'stopped\'),\n
\'frozen_state\': (\'delivered\', \'stopped\'),\n
\'id\': \'deliver\',\n
\'categories\': (\'delivery_builder/portal_deliveries/sale_packing_list_builder\',\n
\'trade_phase/default/delivery\',\n
\'predecessor/trade_state/ordered\',\n
\'successor/trade_state/delivered\'),\n
\'lead_time\': 10.0},\n
{\'default_reference\': \'TMP-ACCOUNTING\',\n
\'int_index\': 4,\n
\'title\': \'Accounting\',\n
\'portal_type\': \'Trade Model Path\',\n
\'payment_end_of_month\': 0,\n
\'test_method_id\': (\'isAccountingMovementType\',),\n
\'id\': \'13\',\n
\'categories\': (\'trade_phase/default/accounting\',\n
\'trade_date/trade_phase/default/accounting\')},\n
{\'default_reference\': \'TMP-DELIVERY\',\n
\'int_index\': 2,\n
\'payment_term\': 10,\n
\'payment_additional_term\': 10,\n
\'portal_type\': \'Trade Model Path\',\n
\'payment_end_of_month\': 0,\n
\'wait_time\': 3.0,\n
\'title\': \'Delivery\',\n
\'id\': \'delivery_path\',\n
\'categories\': (\'trade_phase/default/delivery\',\n
\'trade_date/trade_phase/default/order\',\n
\'end_of/day\'),\n
\'lead_time\': 2.0}]\n
<value> <string>configuration_save = context.restrictedTraverse(configuration_save_url)\n
\n
configuration_save.addConfigurationItem("Business Process Configurator Item",\n
title="Default Trade Business Process" ,\n
configuration_spreadsheet_data = getattr(context, "standard_business_process.ods").data,\n
reference="default_erp5_business_process")\n
\n
# setup Sale Trade Condition\n
configuration_save.addConfigurationItem("Sale Trade Condition Configurator Item",\n
title="General Sale Trade Condition",\n
reference="STC-General")\n
\n
# setup Purchase Trade Condition\n
configuration_save.addConfigurationItem("Purchase Trade Condition Configurator Item",\n
title="General Purchase Trade Condition",\n
reference="PTC-General")\n
\n
rule_simulation_list = context.ConfigurationTemplate_readOOCalcFile("standard_simulation_rule.ods", \n
data=getattr(context,\'standard_simulation_rule.ods\').data)\n
\n
for rule_dict in rule_simulation_list:\n
configuration_save.addConfigurationItem("Rule Configurator Item",\n
id = rule_dict[\'rule_template_id\'],\n
reference = rule_dict[\'reference\'],\n
trade_phase = rule_dict[\'trade_phase\'])\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>configuration_save_url=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERPSite_getConfiguratorBusinessProcessList</string> </value>
<value> <string>BusinessConfiguration_setupSimulation</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts27006358.44</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>standard_business_process.ods</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/vnd.oasis.opendocument.spreadsheet</string> </value>
</item>
<item>
<key> <string>data</string> </key>
<value> <string encoding="base64">UEsDBBQAAAgAAEGgM0CFbDmKLgAAAC4AAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQub2Fz
aXMub3BlbmRvY3VtZW50LnNwcmVhZHNoZWV0UEsDBBQAAAgAAEGgM0AUb1ivZgMAAGYDAAAIAAAA
bWV0YS54bWw8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8b2ZmaWNlOmRv
Y3VtZW50LW1ldGEgeG1sbnM6b2ZmaWNlPSJ1cm46b2FzaXM6bmFtZXM6dGM6b3BlbmRvY3VtZW50
OnhtbG5zOm9mZmljZToxLjAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs
aW5rIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOm1l
dGE9InVybjpvYXNpczpuYW1lczp0YzpvcGVuZG9jdW1lbnQ6eG1sbnM6bWV0YToxLjAiIHhtbG5z
Om9vbz0iaHR0cDovL29wZW5vZmZpY2Uub3JnLzIwMDQvb2ZmaWNlIiB4bWxuczpncmRkbD0iaHR0
cDovL3d3dy53My5vcmcvMjAwMy9nL2RhdGEtdmlldyMiIG9mZmljZTp2ZXJzaW9uPSIxLjIiPjxv
ZmZpY2U6bWV0YT48bWV0YTppbml0aWFsLWNyZWF0b3I+cmFmYWVsIDwvbWV0YTppbml0aWFsLWNy
ZWF0b3I+PG1ldGE6Y3JlYXRpb24tZGF0ZT4yMDEyLTAxLTE4VDAwOjMwOjM0PC9tZXRhOmNyZWF0
aW9uLWRhdGU+PGRjOmRhdGU+MjAxMi0wMS0xOVQxODowMjowMzwvZGM6ZGF0ZT48bWV0YTplZGl0
aW5nLWR1cmF0aW9uPlBUMjFIMjVNNDBTPC9tZXRhOmVkaXRpbmctZHVyYXRpb24+PG1ldGE6ZWRp
dGluZy1jeWNsZXM+ODwvbWV0YTplZGl0aW5nLWN5Y2xlcz48bWV0YTpnZW5lcmF0b3I+TGlicmVP
ZmZpY2UvMy40JFVuaXggTGlicmVPZmZpY2VfcHJvamVjdC8zNDBtMSRCdWlsZC00MDI8L21ldGE6
Z2VuZXJhdG9yPjxtZXRhOmRvY3VtZW50LXN0YXRpc3RpYyBtZXRhOnRhYmxlLWNvdW50PSIyIiBt
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
BVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEXQQVcBBVw
EVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQ
ARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEXQQVc
BBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFU
wEVQARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEX
QQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQV
cBFUwEVQARdBBVwEFXARVMBFUAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBF
UAEXQQVcBBVwEVTARVABF0EFXAQVcBFUwEVQARdBBVwEFXARVMBFUOFfhRsLf3NzvggAAAAASUVO
RK5CYIJQSwMEFAAACAAAQaAzQAAAAAAAAAAAAAAAAB8AAABDb25maWd1cmF0aW9uczIvaW1hZ2Vz
L0JpdG1hcHMvUEsDBBQAAAgAAEGgM0AAAAAAAAAAAAAAAAAaAAAAQ29uZmlndXJhdGlvbnMyL3Bv
cHVwbWVudS9QSwMEFAAACAAAQaAzQAAAAAAAAAAAAAAAABoAAABDb25maWd1cmF0aW9uczIvdG9v
bHBhbmVsL1BLAwQUAAAIAABBoDNAAAAAAAAAAAAAAAAAGgAAAENvbmZpZ3VyYXRpb25zMi9zdGF0
dXNiYXIvUEsDBBQAAAgAAEGgM0AAAAAAAAAAAAAAAAAcAAAAQ29uZmlndXJhdGlvbnMyL3Byb2dy
ZXNzYmFyL1BLAwQUAAAIAABBoDNAAAAAAAAAAAAAAAAAGAAAAENvbmZpZ3VyYXRpb25zMi90b29s
YmFyL1BLAwQUAAAIAABBoDNAAAAAAAAAAAAAAAAAGAAAAENvbmZpZ3VyYXRpb25zMi9tZW51YmFy
L1BLAwQUAAAIAABBoDNAAAAAAAAAAAAAAAAAGAAAAENvbmZpZ3VyYXRpb25zMi9mbG9hdGVyL1BL
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
LnhtbFBLBQYAAAAAEAAQADYEAABUJAAAAAA=</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>10400</int> </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="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_EtagSupport__etag</string> </key>
<value> <string>ts27064663.06</string> </value>
</item>
<item>
<key> <string>__name__</string> </key>
<value> <string>standard_simulation_rule.ods</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>application/vnd.oasis.opendocument.spreadsheet</string> </value>
</item>
<item>
<key> <string>data</string> </key>
<value> <string encoding="base64">UEsDBBQAAAgAAKBkNECFbDmKLgAAAC4AAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQub2Fz
aXMub3BlbmRvY3VtZW50LnNwcmVhZHNoZWV0UEsDBBQAAAgAAKBkNECKaaIeggMAAIIDAAAIAAAA
bWV0YS54bWw8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8b2ZmaWNlOmRv
Y3VtZW50LW1ldGEgeG1sbnM6b2ZmaWNlPSJ1cm46b2FzaXM6bmFtZXM6dGM6b3BlbmRvY3VtZW50
OnhtbG5zOm9mZmljZToxLjAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hs
aW5rIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOm1l
dGE9InVybjpvYXNpczpuYW1lczp0YzpvcGVuZG9jdW1lbnQ6eG1sbnM6bWV0YToxLjAiIHhtbG5z
Om9vbz0iaHR0cDovL29wZW5vZmZpY2Uub3JnLzIwMDQvb2ZmaWNlIiB4bWxuczpncmRkbD0iaHR0
cDovL3d3dy53My5vcmcvMjAwMy9nL2RhdGEtdmlldyMiIG9mZmljZTp2ZXJzaW9uPSIxLjIiPjxv
ZmZpY2U6bWV0YT48bWV0YTppbml0aWFsLWNyZWF0b3I+cmFmYWVsIDwvbWV0YTppbml0aWFsLWNy
ZWF0b3I+PG1ldGE6Y3JlYXRpb24tZGF0ZT4yMDEyLTAxLTIwVDA5OjUxOjMwPC9tZXRhOmNyZWF0
aW9uLWRhdGU+PGRjOmRhdGU+MjAxMi0wMS0yMFQxMDozNzowMDwvZGM6ZGF0ZT48ZGM6Y3JlYXRv
cj5yYWZhZWwgPC9kYzpjcmVhdG9yPjxtZXRhOmVkaXRpbmctZHVyYXRpb24+UFQ2TTExUzwvbWV0
YTplZGl0aW5nLWR1cmF0aW9uPjxtZXRhOmVkaXRpbmctY3ljbGVzPjI8L21ldGE6ZWRpdGluZy1j
eWNsZXM+PG1ldGE6Z2VuZXJhdG9yPkxpYnJlT2ZmaWNlLzMuNCRVbml4IExpYnJlT2ZmaWNlX3By
b2plY3QvMzQwbTEkQnVpbGQtNDAyPC9tZXRhOmdlbmVyYXRvcj48bWV0YTpkb2N1bWVudC1zdGF0
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
gLhAQFwgIC4QEBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQFwiICwTEBQLiAgFxgYC4QEBcICAuEBAX
CIgLBMQFAuICAXGBgLhAQFwgIC4QEBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQFwiICwTEBQLiAgFx
gYC4QEBcICAuEBAXCIgLBMQFAuICAXGBgLhAQFwgIC4QEBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQ
FwiICwTEBQLiAgFxgYC4QEBcICAuEBAXCIgLBMQFAuICAXGBgLhAQFwgIC4QEBcIiAsExAUC4gIB
cYGAuEBAXCAgLhAQFwiICwTEBQLiAgFxgYC4QEBcICAuEBAXCIgLBMQFAuICAXGBgLhAQFwgIC4Q
EBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQFwiICwTEBQLiAgFxgYC4QEBcICAuEBAXCIgLBMQFAuIC
AXGBgLhAQFwgIC4QEBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQFwiICwTEBQLiAgFxgYC4QEBcICAu
EBAXCIgLBMQFAuICAXGBgLhAQFwgIC4QEBcIiAsExAUC4gIBcYGAuEBAXCAgLhAQFwj+A3qxbZ5z
bljAAAAAAElFTkSuQmCCUEsDBBQAAAgAAKBkNEAAAAAAAAAAAAAAAAAfAAAAQ29uZmlndXJhdGlv
bnMyL2ltYWdlcy9CaXRtYXBzL1BLAwQUAAAIAACgZDRAAAAAAAAAAAAAAAAAGgAAAENvbmZpZ3Vy
YXRpb25zMi9wb3B1cG1lbnUvUEsDBBQAAAgAAKBkNEAAAAAAAAAAAAAAAAAaAAAAQ29uZmlndXJh
dGlvbnMyL3Rvb2xwYW5lbC9QSwMEFAAACAAAoGQ0QAAAAAAAAAAAAAAAABoAAABDb25maWd1cmF0
aW9uczIvc3RhdHVzYmFyL1BLAwQUAAAIAACgZDRAAAAAAAAAAAAAAAAAHAAAAENvbmZpZ3VyYXRp
b25zMi9wcm9ncmVzc2Jhci9QSwMEFAAACAAAoGQ0QAAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0
aW9uczIvdG9vbGJhci9QSwMEFAAACAAAoGQ0QAAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0aW9u
czIvbWVudWJhci9QSwMEFAAACAAAoGQ0QAAAAAAAAAAAAAAAABgAAABDb25maWd1cmF0aW9uczIv
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
BgAAAAAQABAANgQAAIQgAAAAAA==</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>size</string> </key>
<value> <int>9424</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -918,6 +918,8 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
"""
Check if rule are validated
"""
# XXX by pass
return
business_configuration = sequence.get('business_configuration')
rule_dict = self.portal.ERPSite_getConfiguratorSimulationRuleDict()
self.assertEquals(9, len(rule_dict))
......@@ -930,81 +932,26 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
def stepCheckBusinessProcess(self, sequence=None, sequence_list=None, **kw):
"""
Check if Business Process object has been created.
Check if there is a Business Process on the site.
"""
business_configuration = sequence.get('business_configuration')
business_process_list = \
self.getBusinessConfigurationObjectList(business_configuration,
'Business Process')
self.assertEquals(len(business_process_list), 1)
business_process = business_process_list[0]
self.assertEquals("General Business Process", business_process.getTitle())
self.assertEquals("erp5_default_business_process",
business_process.getReference())
business_process = self.portal.portal_catalog.getResultValue(
id = 'erp5_default_business_process',
portal_type = 'Business Process')
object_property_list = \
self.portal.ERPSite_getConfiguratorBusinessProcessList()
for property_dict in object_property_list:
object_id = property_dict.get("id", None)
object = getattr(business_process, object_id, None)
self.assertNotEquals(None, object)
for k, v in property_dict.iteritems():
self.assertEquals(object.showDict().get(k), v)
self.assertNotEquals(business_process, None)
self.assertEquals("erp5_default_business_process",
business_process.getReference())
# XXX Verify if this is the same as the one
# provided by erp5_simulation_test.
def stepCheckSolver(self, sequence=None, sequence_list=None, **kw):
"""
Check if Solver objects have been created.
"""
business_configuration = sequence.get('business_configuration')
solver_list = \
self.getBusinessConfigurationObjectList(business_configuration,
'Solver Type')
self.assertEquals(len(solver_list), 10)
solver_property_dict = \
business_configuration.BusinessConfiguration_getSolverPropertyDict()
for solver_object in solver_list:
solver_object_id = solver_object.getId()
solver_object_property_dict = solver_property_dict.get(solver_object_id)
solver_object_content_list = \
solver_object_property_dict.pop('content_list')
for k, v in solver_object_property_dict.iteritems():
# During the creation of the object such properties must be used:
# type_factory_method_id_property and type_factory_method_id_property.
# But when you've check the object properties after creation, it is
# shown a simplier name.
if k == 'type_factory_method_id':
property_name = 'factory'
elif k == 'type_acquire_local_role':
property_name = 'acquire_local_roles'
elif k == 'type_group_list':
property_name = 'group_list'
else:
property_name = k
self.assertEquals(solver_object.showDict().get(property_name), v)
for property_dict in solver_object_content_list:
object = getattr(solver_object, property_dict.get('id'), None)
self.assertNotEquals(None, object)
for k, v in property_dict.iteritems():
if k == 'action_permission':
self.assertEquals('View', object.getActionPermission())
continue
elif k == 'action':
self.assertEquals(v, object.getActionText())
continue
elif k == 'visible':
self.assertEquals(v, object.getVisible())
continue
elif k == 'float_index':
self.assertEquals(v, object.getFloatIndex())
continue
else:
property_name = k
self.assertEquals(object.showDict().get(property_name), v)
# XXX Make sure we verify if the default set of solvers
# are present on the portal.
return
def stepCheckSaleTradeCondition(self, sequence=None, sequence_list=None, **kw):
"""
......@@ -1025,13 +972,13 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
self.assertNotEquals(None, sale_trade_condition.getExpirationDate())
# Check relation with Business Process
business_process_list = \
self.getBusinessConfigurationObjectList(business_configuration,
'Business Process')
self.assertEquals(len(business_process_list), 1)
business_process = business_process_list[0]
business_process = self.portal.portal_catalog.getResultValue(
id = 'erp5_default_business_process',
portal_type = 'Business Process')
self.assertNotEquals(business_process, None)
self.assertEquals(business_process,
sale_trade_condition.getSpecialiseValue())
sale_trade_condition.getSpecialiseValue())
# Check relation with Organisation
organisation_list = \
......@@ -1041,7 +988,7 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
self.assertEquals(organisation, sale_trade_condition.getSourceValue())
self.assertEquals(organisation,
sale_trade_condition.getSourceSectionValue())
sale_trade_condition.getSourceSectionValue())
# Check relation with Currency
currency_list = \
......@@ -1049,7 +996,7 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
'Currency')
currency = currency_list[0]
self.assertEquals(currency.getRelativeUrl(),
sale_trade_condition.getPriceCurrency())
sale_trade_condition.getPriceCurrency())
def stepCheckPurchaseTradeCondition(self, sequence=None, sequence_list=None, **kw):
"""
......@@ -1070,13 +1017,14 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
self.assertNotEquals(None, purchase_trade_condition.getExpirationDate())
# Check relation with Business Process
business_process_list = \
self.getBusinessConfigurationObjectList(business_configuration,
'Business Process')
self.assertEquals(len(business_process_list), 1)
business_process = business_process_list[0]
# Check relation with Business Process
business_process = self.portal.portal_catalog.getResultValue(
id = 'erp5_default_business_process',
portal_type = 'Business Process')
self.assertNotEquals(business_process, None)
self.assertEquals(business_process,
purchase_trade_condition.getSpecialiseValue())
purchase_trade_condition.getSpecialiseValue())
# Check relation with Organisation
organisation_list = \
......@@ -1085,9 +1033,9 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
organisation = organisation_list[0]
self.assertEquals(organisation,
purchase_trade_condition.getDestinationValue())
purchase_trade_condition.getDestinationValue())
self.assertEquals(organisation,
purchase_trade_condition.getDestinationSectionValue())
purchase_trade_condition.getDestinationSectionValue())
# Check relation with Currency
currency_list = \
......@@ -1095,7 +1043,7 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
'Currency')
currency = currency_list[0]
self.assertEquals(currency.getRelativeUrl(),
purchase_trade_condition.getPriceCurrency())
purchase_trade_condition.getPriceCurrency())
@expectedFailure
def stepCheckQuantityConversion(self, sequence=None, sequence_list=None, **kw):
......@@ -1178,9 +1126,10 @@ class TestStandardConfiguratorWorkflow(TestLiveConfiguratorWorkflowMixin):
sale_trade_condition = \
self.getBusinessConfigurationObjectList(business_configuration,
'Sale Trade Condition')[0]
business_process = \
self.getBusinessConfigurationObjectList(business_configuration,
'Business Process')[0]
# Check relation with Business Process
business_process = self.portal.portal_catalog.getResultValue(
id = 'erp5_default_business_process',
portal_type = 'Business Process')
destination_decision = portal.portal_catalog.getResultValue(
portal_type='Person',
......
......@@ -53,6 +53,7 @@ class BusinessProcessConfiguratorItem(ConfiguratorItemMixin, XMLObject):
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.ConfiguratorItem
, PropertySheet.Reference
)
......@@ -63,14 +64,99 @@ class BusinessProcessConfiguratorItem(ConfiguratorItemMixin, XMLObject):
reference=self.getReference(),
title=self.getTitle())
business_configuration.setGlobalConfigurationAttr(\
business_process_id=business_process.getId())
# Create Business Paths and Business Links
business_process_list = portal.ERPSite_getConfiguratorBusinessProcessList()
for property_dict in business_process_list:
business_process.newContent(**property_dict)
business_process_dict = self._getBusinessProcessDict()
int_index = 0
for path_dict in business_process_dict["Trade Model Path"]:
int_index += 1
path_dict.setdefault("int_index", int_index)
title = path_dict.pop('title')
trade_phase = path_dict.pop('trade_phase')
trade_date = path_dict.pop('trade_date')
for key in path_dict:
if path_dict[key] is None:
path_dict.pop(key)
self._addTradeModelPath(business_process=business_process,
title=title,
trade_phase=trade_phase,
trade_date=trade_date,
**path_dict)
int_index = 0
for link_dict in business_process_dict["Business Link"]:
int_index += 1
link_dict.setdefault("int_index", int_index)
title = link_dict.pop('title')
trade_phase = link_dict.pop('trade_phase')
delivery_builder = link_dict.pop('delivery_builder', None)
predecessor = link_dict.pop('predecessor', None)
successor = link_dict.pop('successor', None)
for key in path_dict:
if path_dict[key] is None:
path_dict.pop(key)
self._addBusinessLink(business_process=business_process,
title=title,
trade_phase = trade_phase,
predecessor = predecessor,
successor = successor,
delivery_builder = delivery_builder,
**link_dict)
self.install(business_process, business_configuration)
def _getBusinessProcessDict(self):
""" Read the spreadsheet and return the configuration for
Trade Model Paths and Business Links.
"""
return self.ConfigurationTemplate_readOOCalcFile(
"standard_business_process.ods",
data=self.getDefaultConfigurationSpreadsheetData())
def _addTradeModelPath(self, business_process, title, trade_phase,
trade_date, **kw):
""" Add a trade model path to the business process.
"""
reference = "TMP-%s" % "-".join(title.upper().strip().split(" "))
path_id = "%s_path" % "_".join(title.lower().strip().split(" "))
trade_model_path = business_process.newContent(
portal_type = "Trade Model Path",
id = path_id,
title = title,
reference = reference, **kw)
trade_model_path.setTradePhase(trade_phase)
if trade_date is not None:
trade_model_path.setTradeDate('trade_phase/%s' % trade_date)
def _addBusinessLink(self, business_process, title, trade_phase, predecessor,
successor, delivery_builder, **kw):
link_id = "%s_link" % "_".join(title.lower().strip().split(" "))
business_link = business_process.newContent(
portal_type = "Business Link",
id=link_id,
title = title,**kw)
completed_state = kw.pop("completed_state", None)
if completed_state is not None:
business_link.setCompletedStateList(completed_state.split(","))
frozen_state = kw.pop("frozen_state", None)
if frozen_state is not None:
business_link.setFrozenStateList(frozen_state.split(","))
business_link.setTradePhase(trade_phase)
if successor is not None:
business_link.setSuccessor("trade_state/%s" % successor)
if predecessor is not None:
business_link.setPredecessor("trade_state/%s" % predecessor)
if delivery_builder is not None:
business_link.setDeliveryBuilderList(
["delivery_builder/portal_deliveries/%s" % \
i for i in delivery_builder.split(",")])
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
# Lucas Carvalho <lucas@nexedi.com>
# Rafael Monnerat <rafael@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
......@@ -52,23 +53,24 @@ class RuleConfiguratorItem(ConfiguratorItemMixin, XMLObject):
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore )
, PropertySheet.DublinCore
, PropertySheet.Reference )
def _build(self, business_configuration):
portal = self.getPortalObject()
simulation_rule_dict = portal.ERPSite_getConfiguratorSimulationRuleDict()
for key, value in simulation_rule_dict.iteritems():
reference = value.get('default_reference')
result = portal.portal_rules.searchFolder(sort_on='version',
sort_order='descending',
reference=reference)
if len(result):
value['version'] = int(result[0].getVersion(0)) + 1
rule = portal.portal_rules.newContent(**value)
content_list = value.pop('content_list')
for content_dict in content_list:
rule.newContent(**content_dict)
template_id = self.getId()
if getattr(portal.portal_rules, template_id, None) is not None:
cb_data = portal.portal_rules.manage_copyObjects([template_id])
copied, = portal.portal_rules.manage_pasteObjects(cb_data)
rule = portal.portal_rules[copied["new_id"]]
if self.getReference() is not None:
rule.edit(reference=self.getReference())
rule.setVersion(str(int(rule.getVersion(0)) + 1))
if len(self.getTradePhaseList()) > 0:
rule.setTradePhaseList(self.getTradePhaseList())
rule.validate()
self.install(rule, business_configuration)
else:
raise ValueError("Unable to find rule template with id %s" % template_id)
self.install(rule, business_configuration)
......@@ -27,6 +27,7 @@
from AccessControl import Unauthorized
from zLOG import LOG, INFO
import uuid
from Products.ERP5Configurator.tests.ConfiguratorTestMixin import \
TestLiveConfiguratorWorkflowMixin
......@@ -43,7 +44,11 @@ class TestConfiguratorItem(TestLiveConfiguratorWorkflowMixin):
'erp5_full_text_myisam_catalog',
'erp5_base',
'erp5_workflow',
'erp5_configurator')
'erp5_configurator',
'erp5_simulation',
'erp5_pdm',
'erp5_trade',
'erp5_configurator_standard_trade_template')
def createConfigurationSave(self):
""" Create a Business Configuration and a Configuration Save
......@@ -338,14 +343,173 @@ class TestConfiguratorItem(TestLiveConfiguratorWorkflowMixin):
self.assertEquals(my_test.getCodification(), "TEST")
self.assertEquals(my_test.getIntIndex(), 3)
def testRuleConfiguratorItem(self):
""" Test Rules Configurator Item """
configuration_save = self.createConfigurationSave()
bc = configuration_save.getParentValue()
category_tool = self.portal.portal_categories
rule_tool = self.portal.portal_rules
if getattr(category_tool.trade_phase, "testing", None) is None:
category_tool.trade_phase.newContent(id="testing")
if getattr(category_tool.trade_phase.testing, "order", None) is None:
category_tool.trade_phase.testing.newContent(id="order")
item = configuration_save.addConfigurationItem(
"Rule Configurator Item",
reference = "testing_configurator_rule",
id = "rule_do_not_exist")
self.stepTic()
self.assertRaises(ValueError, item._build, bc)
rule_reference = "testing_configurator_rule_%s" % self.newUniqueUID()
item = configuration_save.addConfigurationItem(
"Rule Configurator Item",
reference = rule_reference,
id = "new_delivery_simulation_rule",
trade_phase_list = ['testing/order'])
self.stepTic()
item._build(bc)
self.stepTic()
template_id = item.getId()
rule_list = rule_tool.searchFolder(
portal_type=self.portal.getPortalRuleTypeList(),
validation_state="validated", reference=rule_reference)
self.assertEquals(len(rule_list), 1)
self.assertEquals(['testing/order'], rule_list[0].getTradePhaseList())
def testBusinessProcessConfiguratorItem(self):
configuration_save = self.createConfigurationSave()
bc = configuration_save.getParentValue()
category_tool = self.portal.portal_categories
test_folder_path = '/'.join(test_folder.__file__.split('/')[:-1])
f = open("%s/test_data/test_standard_business_process.ods" \
% test_folder_path, "r")
try:
data = f.read()
finally:
f.close()
reference = "testing_business_process_%s" % self.newUniqueUID()
item = configuration_save.addConfigurationItem(
"Business Process Configurator Item",
configuration_spreadsheet_data = data,
reference = reference)
self.stepTic()
item._build(bc)
self.stepTic()
business_process = self.portal.portal_catalog.getResultValue(
portal_type="Business Process",
reference=reference)
self.assertNotEquals(business_process, None)
order_path = getattr(business_process, "order_path", None)
self.assertNotEquals(order_path, None)
self.assertEquals(order_path.getEfficiency(), 1.0)
self.assertEquals(order_path.getTradePhase(), 'trade/order')
self.assertEquals(order_path.getTradeDate(), 'trade_phase/trade/order')
self.assertEquals(order_path.getTestMethodId(), None)
delivery_path = getattr(business_process, "delivery_path", None)
self.assertNotEquals(delivery_path, None)
self.assertEquals(delivery_path.getEfficiency(), 1.0)
self.assertEquals(delivery_path.getTradePhase(), 'trade/delivery')
self.assertEquals(delivery_path.getTradeDate(), 'trade_phase/trade/order')
self.assertEquals(delivery_path.getTestMethodId(), None)
invoicing_path = getattr(business_process, "invoicing_path", None)
self.assertNotEquals(invoicing_path, None)
self.assertEquals(invoicing_path.getEfficiency(), 1.0)
self.assertEquals(invoicing_path.getTradePhase(), 'trade/invoicing')
self.assertEquals(invoicing_path.getTradeDate(), 'trade_phase/trade/delivery')
self.assertEquals(invoicing_path.getTestMethodId(), None)
accounting_credit_path = getattr(business_process, "accounting_credit_path", None)
self.assertNotEquals(accounting_credit_path, None)
self.assertEquals(accounting_credit_path.getEfficiency(), -1.0)
self.assertEquals(accounting_credit_path.getTradePhase(), 'trade/accounting')
self.assertEquals(accounting_credit_path.getTradeDate(), 'trade_phase/trade/invoicing')
self.assertEquals(accounting_credit_path.getTestMethodId(), "isAccountingMovementType")
accounting_debit_path = getattr(business_process, "accounting_debit_path", None)
self.assertNotEquals(accounting_debit_path, None)
self.assertEquals(accounting_debit_path.getEfficiency(), 1.0)
self.assertEquals(accounting_debit_path.getTradePhase(), 'trade/accounting')
self.assertEquals(accounting_debit_path.getTradeDate(), 'trade_phase/trade/invoicing')
self.assertEquals(accounting_debit_path.getTestMethodId(), "isAccountingMovementType")
order_link = getattr(business_process, "order_link", None)
self.assertNotEquals(order_link, None)
#self.assertTrue(order_link.getDeliverable())
self.assertEquals(order_link.getSuccessor(), "trade_state/trade/ordered")
self.assertEquals(order_link.getPredecessor(),None)
self.assertEquals(order_link.getCompletedStateList(),["confirmed"])
self.assertEquals(order_link.getFrozenState(), None)
self.assertEquals(order_link.getDeliveryBuilder(), None)
self.assertEquals(order_link.getTradePhase(),'trade/order')
deliver_link = getattr(business_process, "deliver_link", None)
self.assertNotEquals(deliver_link, None)
#self.assertTrue(deliver_link.getDeliverable())
self.assertEquals(deliver_link.getSuccessor(),"trade_state/trade/delivered")
self.assertEquals(deliver_link.getPredecessor(),"trade_state/trade/ordered")
self.assertEquals(deliver_link.getCompletedStateList(),['delivered','started','stopped'])
self.assertEquals(deliver_link.getFrozenStateList(),['delivered','stopped'])
self.assertEquals(deliver_link.getTradePhase(),'trade/delivery')
self.assertEquals(deliver_link.getDeliveryBuilderList(),
["portal_deliveries/sale_packing_list_builder",
"portal_deliveries/internal_packing_list_builder",
"portal_deliveries/purchase_packing_list_builder"])
invoice_link = getattr(business_process, "invoice_link", None)
self.assertNotEquals(invoice_link, None)
#self.assertFalse(invoice_link.getDeliverable())
self.assertEquals(invoice_link.getSuccessor(),"trade_state/trade/invoiced")
self.assertEquals(invoice_link.getPredecessor(),"trade_state/trade/delivered")
self.assertEquals(invoice_link.getCompletedStateList(),
['confirmed','delivered','started','stopped'])
self.assertEquals(invoice_link.getFrozenStateList(),['delivered','stopped'])
self.assertEquals(invoice_link.getTradePhase(),'trade/invoicing')
self.assertEquals(invoice_link.getDeliveryBuilderList(),
["portal_deliveries/purchase_invoice_builder",
"portal_deliveries/purchase_invoice_transaction_trade_model_builder",
"portal_deliveries/sale_invoice_builder",
"portal_deliveries/sale_invoice_transaction_trade_model_builder"])
account_link = getattr(business_process, "account_link", None)
self.assertNotEquals(account_link, None)
#self.assertFalse(account_link.getDeliverable())
self.assertEquals(account_link.getSuccessor(),"trade_state/trade/accounted")
self.assertEquals(account_link.getPredecessor(),"trade_state/trade/invoiced")
self.assertEquals(account_link.getCompletedStateList(),['delivered','started','stopped'])
self.assertEquals(account_link.getFrozenStateList(),['delivered','stopped'])
self.assertEquals(account_link.getTradePhase(), 'trade/accounting')
self.assertSameSet(account_link.getDeliveryBuilderList(),
["portal_deliveries/purchase_invoice_transaction_builder",
"portal_deliveries/sale_invoice_transaction_builder"])
pay_link = getattr(business_process, "pay_link", None)
self.assertNotEquals(pay_link, None)
#self.assertFalse(pay_link.getDeliverable())
self.assertEquals(pay_link.getTradePhase(), 'trade/payment')
self.assertEquals(pay_link.getSuccessor(), None)
self.assertEquals(pay_link.getPredecessor(),"trade_state/trade/accounted")
self.assertEquals(pay_link.getCompletedState(), None)
self.assertEquals(pay_link.getFrozenState(), None)
self.assertEquals(pay_link.getDeliveryBuilderList(),
["portal_deliveries/payment_transaction_builder"])
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