Commit a39bcc3e authored by Julien Muchembled's avatar Julien Muchembled Committed by Sebastien Robin

Reimplement MRP for new simulation

MRP was broken and unused for a long time, since legacy simulation was dropped.
This commits resuscitates MRP, at least:
- expanding, for both operation and sourcing
- building of production reports & production packing lists

Business Processes replaces Supply Chains.
trade_phase replaces industrial_phase
industrial_phase is now used to variate partially produced resources.
parent 1b265a60
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Rule_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/Rule_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -96,7 +96,7 @@
</item>
<item>
<key> <string>simulation_select_method_id</string> </key>
<value> <string>TransformationSourcingRule_selectMovement</string> </value>
<value> <string>ProductionPackingList_selectMovement</string> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -45,6 +45,7 @@
<string>destination_project</string>
<string>source_payment</string>
<string>destination_payment</string>
<string>specialise</string>
</tuple>
</value>
</item>
......@@ -52,6 +53,10 @@
<key> <string>title</string> </key>
<value> <string>category_movement_group_on_delivery</string> </value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -38,6 +38,10 @@
<value>
<tuple>
<string>resource</string>
<string>base_contribution</string>
<string>base_application</string>
<string>industrial_phase</string>
<string>quantity_unit</string>
</tuple>
</value>
</item>
......@@ -45,6 +49,10 @@
<key> <string>title</string> </key>
<value> <string>category_movement_group_on_line</string> </value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -96,7 +96,7 @@
</item>
<item>
<key> <string>simulation_select_method_id</string> </key>
<value> <string>TransformationRule_selectMovement</string> </value>
<value> <string>ProductionReport_selectMovement</string> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -45,6 +45,7 @@
<string>destination_project</string>
<string>source_payment</string>
<string>destination_payment</string>
<string>specialise</string>
</tuple>
</value>
</item>
......@@ -52,6 +53,10 @@
<key> <string>title</string> </key>
<value> <string>category_movement_group_on_delivery</string> </value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -38,6 +38,10 @@
<value>
<tuple>
<string>resource</string>
<string>base_contribution</string>
<string>base_application</string>
<string>industrial_phase</string>
<string>quantity_unit</string>
</tuple>
</value>
</item>
......@@ -45,6 +49,10 @@
<key> <string>title</string> </key>
<value> <string>category_movement_group_on_line</string> </value>
</item>
<item>
<key> <string>update_always</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</pickle>
</record>
......
......@@ -27,6 +27,10 @@
<portal_type id="Production Report Module">
<item>Production Report</item>
</portal_type>
<portal_type id="Rule Tool">
<item>Transformation Simulation Rule</item>
<item>Transformation Sourcing Simulation Rule</item>
</portal_type>
<portal_type id="Supply Chain">
<item>Supply Link</item>
<item>Supply Node</item>
......@@ -34,4 +38,24 @@
<portal_type id="Supply Chain Module">
<item>Supply Chain</item>
</portal_type>
<portal_type id="Transformation Simulation Rule">
<item>Category Membership Divergence Tester</item>
<item>DateTime Divergence Tester</item>
<item>Float Divergence Tester</item>
<item>Mapped Property</item>
<item>Net Converted Quantity Divergence Tester</item>
<item>Specialise Divergence Tester</item>
<item>String Divergence Tester</item>
<item>Variation Divergence Tester</item>
</portal_type>
<portal_type id="Transformation Sourcing Simulation Rule">
<item>Category Membership Divergence Tester</item>
<item>DateTime Divergence Tester</item>
<item>Float Divergence Tester</item>
<item>Mapped Property</item>
<item>Net Converted Quantity Divergence Tester</item>
<item>Specialise Divergence Tester</item>
<item>String Divergence Tester</item>
<item>Variation Divergence Tester</item>
</portal_type>
</allowed_content_type_list>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>rule_icon.gif</string> </value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Transformation Simulation Rule</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>rule</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Transformation Simulation Rule</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>TransformationSimulationRule</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_property_domain_dict</string> </key>
<value>
<dictionary>
<item>
<key> <string>short_title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>acquire_local_roles</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>content_icon</string> </key>
<value> <string>rule_icon.gif</string> </value>
</item>
<item>
<key> <string>content_meta_type</string> </key>
<value> <string>ERP5 Transformation Sourcing Simulation Rule</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>filter_content_types</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<tuple>
<string>rule</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Transformation Sourcing Simulation Rule</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>TransformationSourcingSimulationRule</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>short_title</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<tuple>
<global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>domain_name</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>property_name</string> </key>
<value> <string>title</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -47,4 +47,12 @@
<type>Supply Node</type>
<workflow>edit_workflow</workflow>
</chain>
<chain>
<type>Transformation Simulation Rule</type>
<workflow>edit_workflow, rule_validation_workflow</workflow>
</chain>
<chain>
<type>Transformation Sourcing Simulation Rule</type>
<workflow>edit_workflow, rule_validation_workflow</workflow>
</chain>
</workflow_chain>
\ No newline at end of file
......@@ -50,26 +50,23 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>portal = context.getPortalObject()\n
from ZTUtils import LazyFilter\n
resource = context.getResourceValue()\n
<value> <string>resource = context.getResourceValue()\n
\n
result = []\n
\n
if include_empty:\n
result.append([\'\',\'\'])\n
if resource is None:\n
return result\n
result.append((\'\',\'\'))\n
\n
# XXX: Is it possible to use cache? Hook it on resource?\n
for transformation in LazyFilter(\n
resource.getResourceRelatedValueList(portal_type=portal.getPortalTransformationTypeList()),\n
skip=\'View\'\n
):\n
if not skip_invalidated or transformation.getProperty(\'validation_state\',\'default\') != \'invalidated\':\n
result.append( (transformation.getTitle(),transformation.getRelativeUrl()) )\n
if resource is not None:\n
portal = context.getPortalObject()\n
kw = {\'validation_state\': \'!=invalidated\'} if skip_invalidated else {}\n
result.extend((transformation.title, transformation.relative_url)\n
for transformation in portal.portal_catalog(\n
select_list=(\'title\', \'relative_url\'),\n
portal_type=portal.getPortalTransformationTypeList(),\n
strict_resource_uid=resource.getUid(),\n
sort_on=(\'title\', \'relative_url\'),\n
**kw))\n
\n
result.sort(key=lambda x: x[0])\n
return result\n
</string> </value>
</item>
......
......@@ -50,25 +50,14 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>packing_list = context\n
<value> <string>if context.getSimulationState() == \'draft\':\n
order = context.getCausalityValue()\n
context.edit(\n
comment=order.getComment(),\n
title=order.getTitle(),\n
)\n
\n
tag = packing_list.getPath() + \'_confirm\'\n
\n
# First, copy Order properties\n
related_order = packing_list.getCausalityValue()\n
packing_list.edit(\n
comment=related_order.getComment(),\n
delivery_mode=related_order.getDeliveryMode(),\n
incoterm=related_order.getIncoterm(),\n
destination_administration_value=\\\n
related_order.getDestinationAdministrationValue(),\n
activate_kw={\'tag\':tag},\n
)\n
\n
packing_list.startBuilding()\n
packing_list.activate(after_tag=tag).updateCausalityState()\n
\n
packing_list.activate(after_tag=tag).ProductionDelivery_confirm()\n
context.Delivery_confirm()\n
</string> </value>
</item>
<item>
......
......@@ -50,48 +50,24 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>kw[\'explanation_portal_type\'] = \'Production Order\'\n
kw[\'parent_specialise_portal_type\'] = \'Transformation Rule\'\n
kw[\'grand_parent_simulation_state\'] = \'confirmed\'\n
\n
kw[\'delivery_uid\'] = None\n
kw[\'left_join_list\'] = [\'delivery_uid\']\n
kw[\'select_dict\'] = dict(delivery_uid=None)\n
kw[\'group_by\'] = (\'uid\',)\n
\n
kw[\'src__\'] = src__ \n
result = context.portal_catalog(**kw)\n
if src__:\n
result\n
\n
movement_list = []\n
for movement in result:\n
movement = movement.getObject()\n
root_movement = movement.getRootSimulationMovement()\n
root_rule = root_movement.getParentValue().getSpecialiseValue()\n
if root_rule.getPortalType() in ("Production Order Rule",\n
"Production Order Root Simulation Rule") \\\n
and root_movement.getSimulationState() == "confirmed":\n
movement_list.append(movement)\n
\n
return movement_list\n
<value> <string>return context.portal_catalog(\n
explanation_portal_type="Production Order",\n
parent_specialise_portal_type=("Delivery Simulation Rule",\n
"Transformation Sourcing Simulation Rule"),\n
delivery_uid=None,\n
left_join_list=("delivery_uid",),\n
select_list=("delivery_uid",),\n
group_by=("uid",),\n
**kw)\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>src__=0, **kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
<value> <string>**kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>TransformationRule_selectMovement</string> </value>
<value> <string>ProductionPackingList_selectMovement</string> </value>
</item>
<item>
<key> <string>title</string> </key>
......
......@@ -50,24 +50,27 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>from Products.ERP5Type.Message import Message\n
packing_list = context\n
\n
packing_list_state = packing_list.getSimulationState()\n
if packing_list_state == "draft":\n
packing_list.portal_workflow.doActionFor(\n
packing_list,\n
\'confirm_action\',\n
comment=Message(\'erp5_ui\', \'Initialised by Delivery Builder\'))\n
<value> <string>return context.portal_catalog(\n
explanation_portal_type="Production Order",\n
parent_specialise_portal_type="Transformation Simulation Rule",\n
delivery_uid=None,\n
left_join_list=("delivery_uid",),\n
select_list=("delivery_uid",),\n
group_by=("uid",),\n
**kw)\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>**kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ProductionDelivery_confirm</string> </value>
<value> <string>ProductionReport_selectMovement</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -50,55 +50,24 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>kw[\'explanation_portal_type\'] = \'Production Order\'\n
<value> <string>specialise_list = context.getSpecialiseValueList(portal_type="Transformation")\n
if (len(specialise_list) == 1 and\n
context.getResource() == specialise_list[0].getResource()):\n
parent = context.getParentValue()\n
if parent.getSpecialiseValue().getPortalType() == "Delivery Simulation Rule":\n
movement = context.getParentValue().getDeliveryValue()\n
\n
kw[\'delivery_uid\'] = None\n
kw[\'left_join_list\'] = [\'delivery_uid\']\n
kw[\'select_dict\'] = dict(delivery_uid=None)\n
kw[\'group_by\'] = (\'uid\',)\n
\n
kw[\'src__\'] = src__ \n
result = context.portal_catalog(**kw)\n
if src__:\n
result\n
\n
movement_list = []\n
for movement in result:\n
movement = movement.getObject()\n
root_movement = movement.getRootSimulationMovement()\n
root_type = root_movement.getParentValue().getSpecialiseValue().getPortalType()\n
parent_type = movement.getParentValue().getSpecialiseValue().getPortalType()\n
if (root_type == "Production Order Rule" and\n
parent_type in ("Transformation Sourcing Rule",\n
"Production Order Rule") or\n
root_type == "Production Order Root Simulation Rule" and\n
parent_type in ("Transformation Sourcing Rule",\n
"Delivery Simulation Rule")) \\\n
and root_movement.getSimulationState() == "confirmed":\n
movement_list.append(movement)\n
\n
return movement_list\n
return movement is not None and movement.getPortalType() in (\n
"Production Order Line", "Production Order Cell")\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>src__=0, **kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
<value> <string>rule</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>TransformationSourcingRule_selectMovement</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
<value> <string>SimulationMovement_testTransformationSimulationRule</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -50,16 +50,18 @@
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>return \'default_production_order_rule\'\n
<value> <string>rule = context.getParentValue().getSpecialiseValue()\n
return rule.getPortalType() == "Transformation Simulation Rule" \\\n
and rule.testTransformationSourcing(context)\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string></string> </value>
<value> <string>rule</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ProductionOrder_getRuleReference</string> </value>
<value> <string>SimulationMovement_testTransformationSourcingSimulationRule</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -37,3 +37,5 @@ Supply Chain | view
Supply Chain | view_supply_node
Supply Link | view
Supply Node | view
Transformation Simulation Rule | view
Transformation Sourcing Simulation Rule | view
\ No newline at end of file
......@@ -8,6 +8,24 @@ Production Packing List | Production Packing List Line
Production Report Line | Production Report Cell
Production Report Module | Production Report
Production Report | Production Report Line
Rule Tool | Transformation Simulation Rule
Rule Tool | Transformation Sourcing Simulation Rule
Supply Chain Module | Supply Chain
Supply Chain | Supply Link
Supply Chain | Supply Node
Transformation Simulation Rule | Category Membership Divergence Tester
Transformation Simulation Rule | DateTime Divergence Tester
Transformation Simulation Rule | Float Divergence Tester
Transformation Simulation Rule | Mapped Property
Transformation Simulation Rule | Net Converted Quantity Divergence Tester
Transformation Simulation Rule | Specialise Divergence Tester
Transformation Simulation Rule | String Divergence Tester
Transformation Simulation Rule | Variation Divergence Tester
Transformation Sourcing Simulation Rule | Category Membership Divergence Tester
Transformation Sourcing Simulation Rule | DateTime Divergence Tester
Transformation Sourcing Simulation Rule | Float Divergence Tester
Transformation Sourcing Simulation Rule | Mapped Property
Transformation Sourcing Simulation Rule | Net Converted Quantity Divergence Tester
Transformation Sourcing Simulation Rule | Specialise Divergence Tester
Transformation Sourcing Simulation Rule | String Divergence Tester
Transformation Sourcing Simulation Rule | Variation Divergence Tester
\ No newline at end of file
......@@ -14,3 +14,5 @@ Supply Chain
Supply Chain Module
Supply Link
Supply Node
Transformation Simulation Rule
Transformation Sourcing Simulation Rule
\ No newline at end of file
......@@ -37,3 +37,7 @@ Supply Chain | edit_workflow
Supply Chain | validation_workflow
Supply Link | edit_workflow
Supply Node | edit_workflow
Transformation Simulation Rule | edit_workflow
Transformation Simulation Rule | rule_validation_workflow
Transformation Sourcing Simulation Rule | edit_workflow
Transformation Sourcing Simulation Rule | rule_validation_workflow
\ No newline at end of file
......@@ -28,6 +28,7 @@
##############################################################################
import zope.interface
from collections import defaultdict
from math import log
from AccessControl import ClassSecurityInfo
from Products.ERP5.mixin.variated import VariatedMixin
......@@ -85,19 +86,15 @@ class Amount(Base, VariatedMixin):
" omit_option_base_category.", DeprecationWarning)
omit_optional_variation = omit_option_base_category
result = []
resource = self.getDefaultResourceValue()
if resource is not None:
resource_variation_list = resource.getVariationBaseCategoryList(
if resource is None:
return []
variation_list = resource.getVariationBaseCategoryList(
omit_optional_variation=omit_optional_variation)
if len(base_category_list) > 0 :
variation_list = filter(lambda x: x in base_category_list,
resource_variation_list)
else :
variation_list = resource_variation_list
if len(variation_list) > 0:
result = self.getAcquiredCategoryMembershipList(variation_list, base=1)
return result
variation_list.append('industrial_phase')
if base_category_list:
variation_list = filter(base_category_list.__contains__, variation_list)
return self.getAcquiredCategoryMembershipList(variation_list, base=1)
security.declareProtected(Permissions.AccessContentsInformation,
'getVariationCategoryItemList')
......@@ -110,43 +107,32 @@ class Amount(Base, VariatedMixin):
Result is left display.
"""
variation_category_item_list = []
if base_category_list == ():
base_category_list = self.getVariationRangeBaseCategoryList()
for base_category in base_category_list:
variation_category_list = self.getVariationCategoryList(
base_category_list=[base_category])
resource_list = [self.portal_categories.resolveCategory(x) for x in\
variation_category_list]
category_list = [x for x in resource_list \
if x.getPortalType() == 'Category']
variation_category_item_list.extend(Renderer(
is_right_display=0,
display_none_category=0, base=base,
current_category=current_category,
display_id=display_id, **kw).\
render(category_list))
object_list = [x for x in resource_list \
if x.getPortalType() != 'Category']
variation_category_item_list.extend(Renderer(
is_right_display=0,
base_category=base_category,
display_none_category=0, base=base,
current_category=current_category,
display_id='title', **kw).\
render(object_list))
category_list = self.getVariationCategoryList()
if category_list:
variation_dict = defaultdict(lambda: ([], []))
resolveCategory = self.getPortalObject().portal_categories.resolveCategory
for category in category_list:
resource = resolveCategory(category)
variation_dict[category.split('/', 1)[0]] \
[resource.getPortalType() == 'Category'].append(resource)
kw = dict(is_right_display=0, display_none_category=0, base=base,
current_category=current_category, **kw)
render_category_list = Renderer(display_id=display_id, **kw).render
kw['display_id'] = 'title'
for base_category, (object_list,
category_list) in variation_dict.iteritems():
variation_category_item_list += render_category_list(category_list)
variation_category_item_list += Renderer(base_category=base_category,
**kw).render(object_list)
return variation_category_item_list
security.declareProtected(Permissions.ModifyPortalContent,
'_setVariationCategoryList')
def _setVariationCategoryList(self, value):
result = []
resource = self.getDefaultResourceValue()
if resource is not None:
variation_list = resource.getVariationBaseCategoryList()
if len(variation_list) > 0:
self._setCategoryMembership(variation_list, value, base = 1)
variation_list.append('industrial_phase')
self._setCategoryMembership(variation_list, value, base=1)
security.declareProtected(Permissions.ModifyPortalContent,
'setVariationCategoryList')
......@@ -209,7 +195,6 @@ class Amount(Base, VariatedMixin):
"""
return VariationValue(context = self)
security.declareProtected(Permissions.ModifyPortalContent, '_setVariationValue')
def _setVariationValue(self, variation_value):
return variation_value.setVariationValue(self)
......
......@@ -28,6 +28,7 @@
#
##############################################################################
from collections import defaultdict
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
......@@ -844,3 +845,41 @@ class BusinessProcess(Path, XMLObject):
"""
for business_link in self.getBuildableBusinessLinkValueList(explanation):
business_link.build(explanation=explanation)
security.declareProtected(Permissions.AccessContentsInformation,
'getPreviousTradePhaseDict')
def getPreviousTradePhaseDict(self, trade_phase_list=None):
"""Return a dict mapping each phase to a set of previous ones
If trade_phase_list is given, the return graph is reduced to only keep
phases in this list.
"""
state_dict = defaultdict(set)
phase_list = []
for link in self.getBusinessLinkValueList(sort_on=None):
phase, = link.getTradePhaseList() # BL must have exactly 1 TP
phase_list.append((phase, link.getPredecessor()))
state_dict[link.getSuccessor()].add(phase)
result = dict((phase, state_dict[state]) for phase, state in phase_list)
if trade_phase_list: # reduce graph
next_dict = defaultdict(set)
# build {phase: next_set} (i.e. reverse result)
for next, phase_set in result.iteritems():
for phase in phase_set:
next_dict[phase].add(next)
# for each phase to remove
for phase in set(result).difference(trade_phase_list):
# edit the graph like we would do for a doubly linked list
previous_set = result.pop(phase)
next_set = next_dict[phase]
# i.e. edit next phases to replace current phase by previous ones
for next in next_set:
phase_set = result[next]
phase_set.remove(phase)
phase_set |= previous_set
# and previous phases to replace current by next ones
for previous in previous_set:
phase_set = next_dict[previous]
phase_set.remove(phase)
phase_set |= next_set
return result
......@@ -86,13 +86,10 @@ class TradeModelRuleMovementGenerator(MovementGeneratorMixin):
def _getInputMovementList(self, movement_list=None, rounding=False):
simulation_movement = self._applied_rule.getParentValue()
portal = self._applied_rule.getPortalObject()
# List of types passed to simulation_movemet.asComposedDocument()
# it needs to include portal types of all 'amount_generator*' groups:
composition_type_list = (portal.getPortalAmountGeneratorTypeList() +
portal.getPortalAmountGeneratorLineTypeList() +
portal.getPortalAmountGeneratorCellTypeList())
amount_list = simulation_movement.getAggregatedAmountList(
amount_generator_type_list=composition_type_list)
# List of types passed to simulation_movement.asComposedDocument()
# it needs to include portal types of all 'amount_generator*' groups:
amount_generator_type_list=portal.getPortalAmountGeneratorAllTypeList(0))
input_movement = aq_base(simulation_movement).__of__(self._applied_rule)
for amount in amount_list:
# Do not ignore amount with price = 0 (such behaviour can be obtained by
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2014 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class TransformationSimulationRule(RuleMixin, MovementCollectionUpdaterMixin):
"""
"""
# CMF Type Definition
meta_type = 'ERP5 Transformation Simulation Rule'
portal_type = 'Transformation Simulation Rule'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return TransformationRuleMovementGenerator(applied_rule=context, rule=self)
def testTransformationSourcing(self, context):
if context.getReference().split('/', 1)[0] == 'pr':
return False
# context consumes a resource: maybe sourcing is required.
# Let's see if the business process defines any trade phase that:
# - is not yet expanded (well, we only checks parents and siblings)
# - and precedes a phase of the current transformation
phase = context.getTradePhase()
parent = context.getParentValue()
tv = getTransactionalVariable()
key = 'isSourcingNeeded', parent.getUid()
try:
needed_set = tv[key]
except KeyError:
phase_set = set(x.getTradePhase() for x in parent.objectValues())
phase_list = phase_set.copy()
movement = parent.getParentValue()
while movement.getPortalType() == 'Simulation Movement':
phase_set.add(movement.getTradePhase())
movement = movement.getParentValue().getParentValue()
previous_dict = context.asComposedDocument().getPreviousTradePhaseDict()
needed_set = tv[key] = frozenset(x for x in phase_list
if previous_dict[x] - phase_set)
return phase in needed_set
class TransformationRuleMovementGenerator(MovementGeneratorMixin):
def _getUpdatePropertyDict(self, input_movement):
return {}
def _getInputMovementList(self, movement_list=None, rounding=None):
parent_movement = self._applied_rule.getParentValue()
portal = self._applied_rule.getPortalObject()
amount_list = parent_movement.getAggregatedAmountList(
amount_generator_type_list=portal.getPortalAmountGeneratorAllTypeList(1))
arrow_list = ['destination' + x[6:]
for x in parent_movement.getCategoryMembershipList(
('source', 'source_section'), base=True)]
def newMovement(reference, kw={}):
movement = aq_base(parent_movement.asContext(**kw)).__of__(
self._applied_rule)
movement._setReference(reference)
movement._setCategoryMembership(('destination', 'source_section',
'destination_section', 'source'),
arrow_list, base=True)
return movement
phase_set = set()
for amount in amount_list:
# Do not ignore amount with price = 0 (such behaviour can be obtained by
# specifying a predicate on the amount generator line/cell).
if amount.getResource():
phase_set.add(amount.getTradePhase())
# FIXME: Is it the right way to have source/destination and other
# non-Amount properties set on the generated movement ?
movement = newMovement(amount.getCausality(), dict((k, v)
for k, v in amount.__dict__.iteritems()
if k[0] != '_' and k != 'categories'))
base_category_set = set(amount.getBaseCategoryList())
base_category_set.remove('price_currency') # XXX
movement._setCategoryMembership(base_category_set,
amount.getCategoryList(),
base=True)
movement.quantity = - movement.quantity
yield movement
phase_dict = parent_movement.asComposedDocument() \
.getPreviousTradePhaseDict(phase_set)
final_set = phase_set.copy()
previous_set = final_set.copy()
while previous_set:
phase_list = phase_dict[previous_set.pop()]
final_set.difference_update(phase_list)
previous_set.update(phase_list)
# We should not need an option not to generate movements for intermediate
# resources. This can be configured on Trade Model Paths by filtering out
# movement with an industrial_phase (other properties like reference
# starting with "pr/" is possible). The drawback with such filter is that
# Same Total Quantity check must be disabled.
if 1:
cr_quantity = - parent_movement.getQuantity()
def newIntermediateMovement(reference_prefix, industrial_phase, **kw):
movement = newMovement(reference_prefix + phase, kw)
movement._setTradePhase(phase)
movement._setIndustrialPhase('trade_phase/' + industrial_phase)
return movement
for phase in phase_set:
for previous in phase_dict[phase]:
yield newIntermediateMovement('cr/', previous, quantity=cr_quantity)
if phase not in final_set:
yield newIntermediateMovement('pr/', phase)
movement = newMovement('pr')
movement._setTradePhaseList(final_set)
yield movement
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2014 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import zope.interface
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5.mixin.rule import RuleMixin, MovementGeneratorMixin
from Products.ERP5.mixin.movement_collection_updater import \
MovementCollectionUpdaterMixin
class TransformationSourcingSimulationRule(RuleMixin, MovementCollectionUpdaterMixin):
"""
Transformation Sourcing Rule makes sure
items required in a Transformation are sourced.
"""
# CMF Type Definition
meta_type = 'ERP5 Transformation Sourcing Simulation Rule'
portal_type = 'Transformation Sourcing Simulation Rule'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Default Properties
property_sheets = (
PropertySheet.Base,
PropertySheet.XMLObject,
PropertySheet.CategoryCore,
PropertySheet.DublinCore,
PropertySheet.Task,
PropertySheet.Predicate,
PropertySheet.Reference,
PropertySheet.Version,
PropertySheet.Rule
)
def _getMovementGenerator(self, context):
"""
Return the movement generator to use in the expand process
"""
return TransformationSourcingRuleMovementGenerator(applied_rule=context, rule=self)
class TransformationSourcingRuleMovementGenerator(MovementGeneratorMixin):
def _getUpdatePropertyDict(self, input_movement):
return {}
def _getInputMovementList(self, movement_list=None, rounding=None):
parent_movement = self._applied_rule.getParentValue()
phase_dict = parent_movement.asComposedDocument() \
.getPreviousTradePhaseDict()
movement = aq_base(parent_movement).__of__(self._applied_rule)
movement = movement.asContext(quantity=-movement.getQuantity())
movement._setReference(None)
movement._setTradePhaseList(phase_dict[parent_movement.getTradePhase()])
if parent_movement.getReference().startswith('cr/'):
# For partially produced resources, automatically guess source from other
# movements of the transformation. This avoids duplicate information
# on Trade Model Paths.
# 'here/getSource' condition can be used to match such movements.
# The opposite condition can be used to match raw materials.
reference = 'pr' + parent_movement.getIndustrialPhase()[11:]
for pr in parent_movement.getParentValue().objectValues():
if pr.getReference() == reference:
movement._setSource(pr.getDestination())
movement._setSourceSection(pr.getDestinationSection())
break
return movement,
......@@ -1419,6 +1419,20 @@ class ERP5Site(FolderMixIn, CMFSite, CacheCookieMixin):
"""
return self._getPortalGroupedTypeList('amount_generator_cell')
security.declareProtected(Permissions.AccessContentsInformation,
'getPortalAmountGeneratorAllTypeList')
def getPortalAmountGeneratorAllTypeList(self, transformation):
"""
Return amount generator types, including lines & cells,
but only or without those related to transformations.
"""
result = list(self.getPortalAmountGeneratorTypeList())
result += self.getPortalAmountGeneratorLineTypeList()
result += self.getPortalAmountGeneratorCellTypeList()
if transformation:
return tuple(x for x in result if x.startswith('Transformation'))
return tuple(x for x in result if not x.startswith('Transformation'))
security.declareProtected(Permissions.AccessContentsInformation,
'getPortalBusinessProcessTypeList')
def getPortalBusinessProcessTypeList(self):
......
......@@ -27,90 +27,102 @@
##############################################################################
import unittest
from unittest import skip
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from DateTime import DateTime
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.tests.utils import reindex
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5.tests.testBPMCore import TestBPMMixin
from Products.ERP5.tests.utils import newSimulationExpectedFailure
class TestMRPMixin(TestBPMMixin):
transformation_portal_type = 'Transformation'
transformed_resource_portal_type = 'Transformation Transformed Resource'
product_portal_type = 'Product'
organisation_portal_type = 'Organisation'
order_portal_type = 'Production Order'
order_line_portal_type = 'Production Order Line'
def afterSetUp(self):
super(TestMRPMixin, self).afterSetUp()
self._createRule("Transformation Simulation Rule")
rule = self._createRule("Transformation Sourcing Simulation Rule")
rule._setSameTotalQuantity(False)
def getBusinessTemplateList(self):
return TestBPMMixin.getBusinessTemplateList(self) + ('erp5_mrp', )
def invalidateRules(self):
"""
do reversely of validateRules
"""
rule_tool = self.getRuleTool()
for rule in rule_tool.contentValues(
portal_type=rule_tool.getPortalRuleTypeList()):
if rule.getValidationState() == 'validated':
rule.invalidate()
def _createRule(self, portal_type):
x = portal_type.replace(' Simulation ', ' ').replace(' ', '_').lower()
reference = "default_" + x
id = "testMRP_" + x
rule_tool = self.portal.portal_rules
try:
rule = self.getRule(reference=reference)
self.assertEqual(rule.getId(), id)
except IndexError:
rule = rule_tool.newContent(id, portal_type,
reference=reference,
test_method_id="SimulationMovement_test" + portal_type.replace(' ', ''))
def newTester(p, t, **kw):
kw.setdefault("tested_property", (p,))
return rule.newContent(p + "_tester", t + " Divergence Tester",
title=p + " divergence tester", **kw)
for x in ("aggregate",
"base_application",
"base_contribution",
"destination_section",
"destination",
"price_currency",
"resource",
"source_section",
"source",
"use"):
newTester(x, "Category Membership")
for x in ("start_date", "stop_date"):
newTester(x, "DateTime")
newTester("price", "Float")
newTester("quantity", "Net Converted Quantity",
tested_property=("quantity", "quantity_unit"))
newTester("specialise", "Specialise")
newTester("variation", "Variation",
tested_property=("variation_category_list",
"variation_property_dict"))
newTester("reference", "String", matching_provider=1,
divergence_provider=0)
if rule.getValidationState() != 'validated':
rule.validate()
return rule
def _createDocument(self, portal_type, **kw):
module = self.portal.getDefaultModule(
portal_type=portal_type)
return self._createObject(module, portal_type, **kw)
def _createObject(self, parent, portal_type, id=None, **kw):
o = None
if id is not None:
o = parent.get(str(id), None)
if o is None:
o = parent.newContent(portal_type=portal_type)
o.edit(**kw)
return o
return self.portal.getDefaultModule(portal_type=portal_type).newContent(
portal_type=portal_type, **kw)
def createTransformation(self, **kw):
return self._createDocument(self.transformation_portal_type, **kw)
return self._createDocument('Transformation', **kw)
def createProduct(self, **kw):
return self._createDocument(self.product_portal_type, **kw)
return self._createDocument('Product', **kw)
def createOrganisation(self, **kw):
return self._createDocument(self.organisation_portal_type, **kw)
def createNode(self, **kw):
return self._createDocument('Organisation', **kw)
def createOrder(self, **kw):
return self._createDocument(self.order_portal_type, **kw)
return self._createDocument('Production Order', **kw)
def createOrderLine(self, order, **kw):
return self._createObject(order, self.order_line_portal_type, **kw)
return order.newContent(portal_type=order.getPortalType() + ' Line', **kw)
def createTransformedResource(self, transformation, **kw):
return self._createObject(transformation, self.transformed_resource_portal_type, **kw)
return transformation.newContent(
portal_type='Transformation Transformed Resource', **kw)
@reindex
def createCategories(self):
category_tool = getToolByName(self.portal, 'portal_categories')
self.createCategoriesInCategory(category_tool.base_amount, ['weight'])
self.createCategoriesInCategory(category_tool.base_amount.weight, ['kg'])
category_tool = self.portal.portal_categories
self.createCategoriesInCategory(category_tool.quantity_unit, ['weight'])
self.createCategoriesInCategory(category_tool.quantity_unit.weight, ['kg'])
self.createCategoriesInCategory(category_tool.trade_phase, ['mrp',])
self.createCategoriesInCategory(category_tool.trade_phase.mrp,
['p' + str(i) for i in range(5)]) # phase0 ~ 4
('p' + str(i) for i in xrange(2)))
self.createCategoriesInCategory(category_tool.trade_phase.mrp,
('s' + str(i) for i in xrange(1)))
self.createCategoriesInCategory(category_tool.trade_state,
('ready', 'partial', 'done'))
('s' + str(i) for i in xrange(5)))
@reindex
def createDefaultOrder(self, transformation=None, business_process=None):
def createDefaultOrder(self, business_process, transformation=None):
if transformation is None:
transformation = self.createDefaultTransformation()
if business_process is None:
business_process = self.createSimpleBusinessProcess()
base_date = DateTime()
order = self.createOrder(specialise_value=business_process,
start_date=base_date,
stop_date=base_date+3)
......@@ -118,10 +130,8 @@ class TestMRPMixin(TestBPMMixin):
quantity=10,
resource=transformation.getResource(),
specialise_value=transformation)
# XXX in some case, specialise_value is not related to order_line by edit,
# but by setSpecialise() is ok, Why?
order_line.setSpecialiseValue(transformation)
return order
<<<<<<< HEAD
@reindex
def createDefaultTransformation(self):
......@@ -130,378 +140,167 @@ class TestMRPMixin(TestBPMMixin):
resource3 = self.createProduct(id='3', quantity_unit_list=['weight/kg'])
resource4 = self.createProduct(id='4', quantity_unit_list=['weight/kg'])
resource5 = self.createProduct(id='5', quantity_unit_list=['weight/kg'])
=======
>>>>>>> f2130ed... Reimplement MRP for new simulation
transformation = self.createTransformation(resource_value=resource5)
def createDefaultTransformation(self):
resource = lambda: self.createProduct(quantity_unit_list=['weight/kg'])
transformation = self.createTransformation(resource_value=resource())
self.createTransformedResource(transformation=transformation,
resource_value=resource1,
resource_value=resource(),
quantity=3,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p2')
trade_phase='mrp/p0')
self.createTransformedResource(transformation=transformation,
resource_value=resource2,
resource_value=resource(),
quantity=1,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p2')
trade_phase='mrp/p0')
self.createTransformedResource(transformation=transformation,
resource_value=resource3,
resource_value=resource(),
quantity=4,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p3')
trade_phase='mrp/p1')
self.createTransformedResource(transformation=transformation,
resource_value=resource4,
resource_value=resource(),
quantity=1,
quantity_unit_list=['weight/kg'],
trade_phase='mrp/p3')
trade_phase='mrp/p1')
return transformation
@reindex
def createSimpleBusinessProcess(self):
""" mrp/p2 mrp/3
ready -------- partial_produced ------- done
"""
# organisations
source_section = self.createOrganisation(title='source_section')
source = self.createOrganisation(title='source')
destination_section = self.createOrganisation(title='destination_section')
destination = self.createOrganisation(title='destination')
business_process = self.createBusinessProcess(referential_date='stop_date')
self.createBusinessLink(business_process,
id='p2',
predecessor='trade_state/ready',
successor='trade_state/partial',
quantity=1,
trade_phase=['mrp/p2'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination)
self.createBusinessLink(business_process,
id='p3',
predecessor='trade_state/partial',
successor='trade_state/done',
quantity=1,
deliverable=1, # root explanation
trade_phase=['mrp/p3'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination)
return business_process
@reindex
def createConcurrentBusinessProcess(self):
""" mrp/p2
ready ======== partial_produced
mrp/p3
def createBusinessProcess1(self, node_p0=None):
""" order p0 s0 p1 deliver
------- S0 ---- S1 ---- S2 ---- S3 ------- S4
PO PR PPL PR PPL
"""
# organisations
source_section = self.createOrganisation(title='source_section')
source = self.createOrganisation(title='source')
destination_section = self.createOrganisation(title='destination_section')
destination = self.createOrganisation(title='destination')
business_process = self.createBusinessProcess(referential_date='stop_date')
self.createBusinessLink(business_process,
id='p2',
predecessor='trade_state/ready',
successor='trade_state/partial',
quantity=1,
trade_phase=['mrp/p2'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination)
business_process = self._createDocument("Business Process")
builder = 'portal_deliveries/production_packing_list_builder'
completed = 'delivered', 'started', 'stopped'
phase_list = [('default/order', None, ('confirmed',)),
('default/delivery', builder, completed)]
phase_list[1:1] = [('mrp/p' + str(i),
'portal_deliveries/production_report_builder',
completed)
for i in xrange(2)]
if node_p0 is not None:
phase_list.insert(2, ('mrp/s0', builder, completed))
predecessor = None
for i, (phase, builder, completed) in enumerate(phase_list):
successor = 'trade_state/s' + str(i)
self.createBusinessLink(business_process,
id='p3',
predecessor='trade_state/ready',
successor='trade_state/partial',
quantity=1,
deliverable=1, # root explanation
trade_phase=['mrp/p3'],
source_section_value=source_section,
source_value=source,
destination_section_value=destination_section,
destination_value=destination)
completed_state=completed,
predecessor=predecessor,
successor=successor,
trade_phase=phase,
delivery_builder=builder)
predecessor = successor
phase_list = [x[0] for x in phase_list]
if node_p0 is not None:
self.createTradeModelPath(business_process,
destination_value=node_p0,
trade_phase=phase_list.pop(1))
self.createTradeModelPath(business_process,
test_tales_expression="here/getSource",
trade_phase=phase_list.pop(1))
self.createTradeModelPath(business_process, trade_phase_list=phase_list)
return business_process
def checkStock(self, resource, *node_variation_quantity):
if isinstance(resource, str):
resource = self.portal.unrestrictedTraverse(resource)
expected_dict = dict(((x[0].getUid(), x[1]), x[2])
for x in node_variation_quantity)
for r in resource.getCurrentInventoryList(group_by_node=1,
group_by_variation=1):
self.assertEqual(expected_dict.pop((r.node_uid, r.variation_text), 0),
r.inventory)
self.assertFalse(any(expected_dict.itervalues()), expected_dict)
class TestMRPImplementation(TestMRPMixin):
"""the test for implementation"""
@skip('Unfinished experimental feature')
def test_TransformationRule_getHeadProductionPathList(self):
rule = self.getRule(reference='default_transformation_model_rule')
transformation = self.createDefaultTransformation()
business_process = self.createSimpleBusinessProcess()
self.assertEqual([business_process.p2],
rule.getHeadProductionPathList(transformation, business_process))
business_process = self.createConcurrentBusinessProcess()
self.assertEqual(set([business_process.p2, business_process.p3]),
set(rule.getHeadProductionPathList(transformation, business_process)))
@newSimulationExpectedFailure
def test_TransformationRule_expand(self):
# mock order
order = self.createDefaultOrder()
order_line = order.objectValues()[0]
business_process = order.getSpecialiseValue()
# paths
path_p2 = '%s/p2' % business_process.getRelativeUrl()
path_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.p2
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
# don't need another rules, just need TransformationRule for test
self.invalidateRules()
def test(self):
workshop = self.createNode(title='workshop')
workshop2 = self.createNode(title='workshop2')
destination = self.createNode(title='destination')
business_process = self.createBusinessProcess1(workshop2)
order = self.createDefaultOrder(business_process)
order_line, = order.objectValues()
order._edit(source_value=workshop, destination_value=destination)
self.tic()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
quantity=order_line.getQuantity(),
resource=order_line.getResource())
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.getRule(reference='default_transformation_model_rule')
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((path_p2,), 'product_module/5', produced_organisations, 'mrp/p3', -10),
((path_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 30),
((path_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 10),
((path_p3,), 'product_module/5', consumed_organisations, 'mrp/p3', 10),
((path_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 40),
((path_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 10),
((path_p3,), 'product_module/5', produced_organisations, None, -10)])
movement_list = applied_rule.objectValues()
self.assertEqual(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEqual(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationRule_expand_concurrent(self):
business_process = self.createConcurrentBusinessProcess()
# mock order
order = self.createDefaultOrder(business_process=business_process)
order_line = order.objectValues()[0]
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
phase_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.p2
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
organisations = (source_section, source, destination_section, destination)
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
# don't need another rules, just need TransformationRule for test
self.invalidateRules()
order.plan()
self.tic()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
quantity=order_line.getQuantity(),
resource=order_line.getResource())
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.getRule(reference='default_transformation_model_rule')
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 30),
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 10),
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 40),
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 10),
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, -10)])
movement_list = applied_rule.objectValues()
self.assertEqual(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEqual(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationRule_expand_reexpand(self):
"""
test case of difference when any movement are frozen
by using above result
"""
self.test_TransformationRule_expand_concurrent()
ar, = order.getCausalityRelatedValueList(portal_type="Applied Rule")
sm, = ar.objectValues() # order
ar, = sm.objectValues()
sm, = ar.objectValues() # deliver
ar, = sm.objectValues()
movement_list = []
resource = order_line.getResource()
for sm in ar.objectValues():
self.assertEqual(sm.getSource(), None)
self.assertTrue(sm.getDestination())
# Reference is used to match movements when reexpanding.
reference = sm.getReference()
if reference.split('/', 1)[0] in ('pr', 'cr'):
self.assertEqual(sm.getResource(), resource)
else:
cr = self.portal.unrestrictedTraverse(reference).getResource()
self.assertTrue(None != sm.getResource() == cr != resource)
reference = None
movement_list.append((sm.getTradePhase(), sm.getQuantity(),
reference, sm.getIndustrialPhaseList()))
movement_list.sort()
self.assertEqual(movement_list, sorted((
('mrp/p0', -10, None, []),
('mrp/p0', -30, None, []),
('mrp/p0', 10, 'pr/mrp/p0', ['trade_phase/mrp/p0']),
('mrp/p1', -10, 'cr/mrp/p1', ['trade_phase/mrp/p0']),
('mrp/p1', -10, None, []),
('mrp/p1', -40, None, []),
('mrp/p1', 10, 'pr', []),
)))
order.confirm()
order.localBuild()
self.tic()
self.checkStock(resource)
applied_rule = self.portal.portal_simulation.objectValues()[0]
business_process = applied_rule.getCausalityValue().getSpecialiseValue()
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
phase_p3 = '%s/p3' % business_process.getRelativeUrl()
# organisations
path = business_process.p2
source_section = path.getSourceSection()
source = path.getSource()
destination_section = path.getDestinationSection()
destination = path.getDestination()
consumed_organisations = (source_section, source, destination_section, None)
produced_organisations = (source_section, None, destination_section, destination)
movement = applied_rule.objectValues()[0]
applied_rule = movement.objectValues()[0]
# these movements are made by transformation
for movement in applied_rule.objectValues():
movement.edit(quantity=1)
# set the state value of isFrozen to 1,
movement._baseSetFrozen(1)
# re-expand
rule = self.getRule(reference='default_transformation_model_rule')
rule.expand(applied_rule)
# assertion
expected_value_set = set([
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 1), # Frozen
((phase_p2,), 'product_module/1', consumed_organisations, 'mrp/p2', 29),
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 1), # Frozen
((phase_p2,), 'product_module/2', consumed_organisations, 'mrp/p2', 9),
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 1), # Frozen
((phase_p3,), 'product_module/3', consumed_organisations, 'mrp/p3', 39),
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 1), # Frozen
((phase_p3,), 'product_module/4', consumed_organisations, 'mrp/p3', 9),
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, 1), # Frozen
((phase_p2, phase_p3), 'product_module/5', produced_organisations, None, -11)])
movement_list = applied_rule.objectValues()
self.assertEqual(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getTradePhase(),
movement.getQuantity())])
self.assertEqual(expected_value_set, movement_value_set)
@skip('Unfinished experimental feature')
def test_TransformationSourcingRule_expand(self):
# mock order
order = self.createDefaultOrder()
order_line = order.objectValues()[0]
# don't need another rules, just need TransformationSourcingRule for test
self.invalidateRules()
def getRelatedDeliveryList(portal_type):
return order.getCausalityRelatedValueList(portal_type=portal_type)
pr1, = getRelatedDeliveryList("Production Report")
pr1.start()
pr1.deliver()
order.localBuild()
self.tic()
variation = 'industrial_phase/trade_phase/mrp/p0'
self.checkStock(resource, (workshop2, variation, 10))
business_process = order.getSpecialiseValue()
# get last path of a business process
# in simple business path, the last is between "partial_produced" and "done"
causality_path = None
for state in business_process.objectValues(
portal_type=self.portal.getPortalBusinessStateTypeList()):
if len(state.getRemainingTradePhaseList(self.portal)) == 0:
causality_path = state.getSuccessorRelatedValue()
# phases
phase_p2 = '%s/p2' % business_process.getRelativeUrl()
# organisations
source_section = causality_path.getSourceSection()
source = causality_path.getSource()
destination_section = causality_path.getDestinationSection()
destination = causality_path.getDestination()
organisations = (source_section, source, destination_section, destination)
# sourcing resource
sourcing_resource = order_line.getResource()
# alter simulations of the order
# root
applied_rule = self.portal.portal_simulation.newContent(portal_type='Applied Rule')
movement = applied_rule.newContent(portal_type='Simulation Movement')
applied_rule.edit(causality_value=order)
movement.edit(order_value=order_line,
causality_value=causality_path,
quantity=order_line.getQuantity(),
resource=sourcing_resource,
)
ppl1, = getRelatedDeliveryList("Production Packing List")
ppl1.start()
ppl1.deliver()
order.localBuild()
self.tic()
self.checkStock(resource, (workshop, variation, 10))
# test mock
applied_rule = movement.newContent(potal_type='Applied Rule')
rule = self.getRule(reference='default_transformation_sourcing_model_rule')
rule.expand(applied_rule)
pr2, = (x for x in getRelatedDeliveryList("Production Report")
if x.aq_base is not pr1.aq_base)
pr2.start()
pr2.deliver()
order.localBuild()
self.tic()
self.checkStock(resource, (workshop, '', 10))
# assertion
expected_value_set = set([
((phase_p2,), sourcing_resource, organisations, 10)])
movement_list = applied_rule.objectValues()
self.assertEqual(len(expected_value_set), len(movement_list))
movement_value_set = set([])
for movement in movement_list:
movement_value_set |= set([(tuple(movement.getCausalityList()),
movement.getResource(),
(movement.getSourceSection(),
movement.getSource(),
movement.getDestinationSection(),
movement.getDestination(),), # organisations
movement.getQuantity())])
self.assertEqual(expected_value_set, movement_value_set)
ppl2, = (x for x in getRelatedDeliveryList("Production Packing List")
if x.aq_base is not ppl1.aq_base)
ppl2.start()
ppl2.deliver()
self.tic()
self.checkStock(resource, (destination, '', 10))
def test_suite():
......
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