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>
......
......@@ -36,4 +36,6 @@ Supply Chain Module | view
Supply Chain | view
Supply Chain | view_supply_node
Supply Link | view
Supply Node | view
\ No newline at end of file
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
\ No newline at end of file
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
......@@ -13,4 +13,6 @@ Production Report Module
Supply Chain
Supply Chain Module
Supply Link
Supply Node
\ No newline at end of file
Supply Node
Transformation Simulation Rule
Transformation Sourcing Simulation Rule
\ No newline at end of file
......@@ -36,4 +36,8 @@ Production Report | production_packing_list_workflow
Supply Chain | edit_workflow
Supply Chain | validation_workflow
Supply Link | edit_workflow
Supply Node | edit_workflow
\ No newline at end of file
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(
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
if resource is None:
return []
variation_list = resource.getVariationBaseCategoryList(
omit_optional_variation=omit_optional_variation)
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):
......
This diff is collapsed.
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