Commit 9b9c75c3 authored by Romain Courteaud's avatar Romain Courteaud

Rewrote the rule.

Need to finish the date calculation.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3421 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 42534e1b
############################################################################## ##############################################################################
# #
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. # Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com> # Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -32,6 +33,8 @@ from Products.CMFCore.utils import getToolByName ...@@ -32,6 +33,8 @@ from Products.CMFCore.utils import getToolByName
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.Rule import Rule from Products.ERP5.Document.Rule import Rule
from Products.ERP5.Document.TransformationSourcingRule import\
TransformationSourcingRuleMixin
from zLOG import LOG from zLOG import LOG
...@@ -40,91 +43,20 @@ class TransformationRule(Rule): ...@@ -40,91 +43,20 @@ class TransformationRule(Rule):
Order Rule object make sure an Order in the similation Order Rule object make sure an Order in the similation
is consistent with the real order is consistent with the real order
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Transformation Rule' meta_type = 'ERP5 Transformation Rule'
portal_type = 'Transformation Rule' portal_type = 'Transformation Rule'
add_permission = Permissions.AddPortalContent
isPortalContent = 1
isRADContent = 1
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.View) security.declareObjectProtected(Permissions.View)
# Default Properties # Default Properties
property_sheets = ( PropertySheet.Base property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject , PropertySheet.XMLObject
, PropertySheet.CategoryCore , PropertySheet.CategoryCore
, PropertySheet.DublinCore , PropertySheet.DublinCore
) )
# Class variable
# CMF Factory Type Information simulation_movement_portal_type = "Simulation Movement"
factory_type_information = \
{ 'id' : portal_type
, 'meta_type' : meta_type
, 'description' : """\
An ERP5 Rule..."""
, 'icon' : 'rule_icon.gif'
, 'product' : 'ERP5'
, 'factory' : 'addTransformationRule'
, 'immediate_view' : 'rule_view'
, 'allow_discussion' : 1
, 'allowed_content_types': ()
, 'filter_content_types' : 1
, 'global_allow' : 1
, 'actions' :
( { 'id' : 'view'
, 'name' : 'View'
, 'category' : 'object_view'
, 'action' : 'rule_view'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'list'
, 'name' : 'Object Contents'
, 'category' : 'object_action'
, 'action' : 'folder_contents'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'print'
, 'name' : 'Print'
, 'category' : 'object_print'
, 'action' : 'rule_print'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'metadata'
, 'name' : 'Metadata'
, 'category' : 'object_view'
, 'action' : 'metadata_edit'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'translate'
, 'name' : 'Translate'
, 'category' : 'object_action'
, 'action' : 'translation_template_view'
, 'permissions' : (
Permissions.TranslateContent, )
}
)
}
security.declareProtected(Permissions.ModifyPortalContent, 'constructNewAppliedRule')
def constructNewAppliedRule(self, context, id=None):
"""
Creates a new applied rule which points to self
"""
my_applied_rule = Rule.constructNewAppliedRule(self, context, id=id)
resource = context.getDefaultResourceValue()
# Find my related transformation
transformation = resource.getDefaultResourceRelatedValue(portal_type = ('Transformation',))
if transformation is not None:
#LOG('Transformation Causality', 0, str(transformation.getId()))
my_applied_rule.setCausalityValue(transformation )
return my_applied_rule
security.declareProtected(Permissions.AccessContentsInformation, 'test') security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self, movement): def test(self, movement):
...@@ -134,188 +66,224 @@ An ERP5 Rule...""" ...@@ -134,188 +66,224 @@ An ERP5 Rule..."""
# Test if we must transform # Test if we must transform
# The test should actually be based on nodes, paths # The test should actually be based on nodes, paths
# and capacities, which is not possible now # and capacities, which is not possible now
# so we just test if it is a "model" ! result = 1
# and if it is being source from the workshop
#LOG('Test Transformation Rule', 0, '')
# Only apply to Order applied rule # Only apply to Order applied rule
root_applied_rule = movement.getRootAppliedRule() root_applied_rule = movement.getRootAppliedRule()
root_rule = root_applied_rule.getSpecialiseValue() root_rule = root_applied_rule.getSpecialiseValue()
if root_rule is None: order = root_applied_rule.getCausalityValue()
return 0 root_movement = movement.getRootSimulationMovement()
if root_rule.getPortalType() != "Order Rule": # Test movement
return 0 if (root_rule is None) or\
(root_rule.getPortalType() != "Production Order Rule") or\
# Only apply to certain resources (order is None) or\
resource = movement.getResourceValue() (movement.getResourceValue() is None) or\
if resource is None: (movement.getSourceValue() is None) or\
return 0 (movement.getResourceValue() != root_movement.getResourceValue()):
module = resource.aq_parent # We only produced what is asked on the Production Order
result = 0
if module.id == 'modele': else:
# This is the modele ! supply_chain = self.getSupplyChain(movement.getParent())
# We must also test the nodes parent_supply_link = self.getCurrentSupplyLink(movement)
# if the source is a production node current_tranfo_link_list = supply_chain.\
source = movement.getSource() getPreviousProductionSupplyLinkList(parent_supply_link)
if type(source) is type('a'): length = len(current_tranfo_link_list)
if source.find('site/Piquage') >= 0 : if length == 0:
return 1 result = 0
return 0 elif length > 1:
elif module.id == 'assortiment': result = 0
destination = movement.getDestination() # XXX FIXME: implementation needed
if type(destination) is type('a'): raise "TransformationRuleError",\
if destination.find('site/Stock_PF/Gravelines') >= 0 : "TransformationRule not able to use multiple SupplyLink."
source = movement.getSource() return result
if type(source) is type('a'):
if source.find('site/Stock_PF/Gravelines') >= 0 :
return 1
return 0
# Simulation workflow # Simulation workflow
security.declareProtected(Permissions.ModifyPortalContent, 'expand') security.declareProtected(Permissions.ModifyPortalContent, 'expand')
def expand(self, applied_rule, **kw): def expand(self, applied_rule, **kw):
""" """
Expands the current movement downward. Expands the current movement downward.
-> new status -> expanded -> new status -> expanded
An applied rule can be expanded only if its parent movement An applied rule can be expanded only if its parent movement
is expanded. is expanded.
""" """
delivery_line_type = 'Simulation Movement' parent_movement = applied_rule.getParent()
# Get production node and production section
# Get the order where we come from production = parent_movement.getSource()
my_transformation = applied_rule.getCausalityValue() production_section = parent_movement.getSourceSection()
# Get the current supply link used to calculate consumed resource
# No transformation defined # The current supply link is calculated from the parent AppliedRule.
if my_transformation is None: supply_chain = self.getSupplyChain(parent_movement.getParent())
# Things stop here parent_supply_link = self.getCurrentSupplyLink(parent_movement)
applied_rule.diverge() current_supply_link_list = supply_chain.\
return getPreviousProductionSupplyLinkList(parent_supply_link)
if len(current_supply_link_list) != 1:
# Find production node # We shall no pass here.
my_context_movement = applied_rule.getParent() # The test method returned a wrong value !
production_node = my_context_movement.getSource() raise "TransformationRuleError",\
LOG('TransformationRule.expand my_context_movement.getPhysicalPath()',0,my_context_movement.getPhysicalPath()) "Expand must not be called on %r" %\
LOG('TransformationRule.expand my_context_movement.getSource()',0,my_context_movement.getSource()) applied_rule.getRelativeUrl()
LOG('TransformationRule.expand my_context_movement.getTargetSource()',0,my_context_movement.getTargetSource())
production_section = my_context_movement.getSourceSection()
# Generate production and consumption lines
my_quantity = my_context_movement.getTargetQuantity()
#LOG('Transformation', 0, str(my_transformation))
# We used to call this with context = my_context_movement
# but it still has some issue which need to be fixed XXX As
# a temp solution, we use the dict based API, but it is not general enough
# and will causse errors on countinuous variations
# suspected bug cause is probably related to the use of REQUEST where it should not
# ie. we acquire some unwanted context
amount_list , grand_total_base_price, grand_total_source_base_price,\
grand_total_duration, grand_total_duration_france, grand_total_variated_base_price,\
grand_total_variated_source_base_price = my_transformation.getAggregatedAmountList(
REQUEST = {'categories': my_context_movement.getVariationCategoryList()} )
# Coramy Specific
# Create a line for the resource produced by the transformation
new_id = 'produced_resource'
produced_resource = applied_rule.get(new_id)
if produced_resource is None:
my_context_movement.portal_types.constructContent(
type_name = delivery_line_type,
container = applied_rule,
id = new_id,
) # quantity
lost_quantity = 0.0
else: else:
lost_quantity = produced_resource.getLostQuantity() current_supply_link = current_supply_link_list[0]
# Generate produced movement
produced_resource = applied_rule[new_id] movement_dict = self._expandProducedResource(applied_rule,
produced_resource._edit( production,
start_date = my_context_movement.getStartDate(), production_section,
stop_date = my_context_movement.getStartDate(), current_supply_link)
resource = my_context_movement.getResource(), # Generate consumed movement
quantity = my_context_movement.getQuantity() + lost_quantity, consumed_mvt_dict = self._expandConsumedResource(applied_rule,
source_list = (), production,
source_section_list = (), production_section,
quantity_unit = my_context_movement.getQuantityUnit(), current_supply_link)
destination_section = production_section, movement_dict.update(consumed_mvt_dict)
destination = production_node, # Finally, build movement
deliverable = 1 self._buildMovementList(applied_rule, movement_dict)
) # Expand each movement created
# Mising quantity unit conversion for my_quantity !!!! XXXX Rule.expand(self, applied_rule, **kw)
produced_resource.setVariationCategoryList(my_context_movement.getVariationCategoryList())
# Add lines
line_number = 0
acceptable_id_list = ['produced_resource']
# getRootAppliedRules is not defined
#production_order = self.getRootAppliedRule().getCausalityValue() # get the production order
production_order = applied_rule.getRootAppliedRule().getCausalityValue() # get the production order
filter_list = production_order.contentValues(filter={'portal_type': 'Amount Filter'})
for amount_line in amount_list:
# Apply each amount filter
for f in filter_list:
f.update(amount_line)
new_id = 'transformed_resource_%s' % line_number
transformed_resource = applied_rule.get(new_id)
if transformed_resource is None:
my_context_movement.portal_types.constructContent(
type_name = delivery_line_type,
container = applied_rule,
id = new_id,
) # quantity
transformed_resource = applied_rule[new_id]
#LOG("amount_line", 0, str(amount_line))
if amount_line['quantity'] != 0.0:
# Only create line if it is not 0.0
transformed_resource._edit(
start_date = my_context_movement.getStartDate(),
stop_date = my_context_movement.getStartDate(),
quantity = amount_line['quantity'] * my_quantity,
efficiency = amount_line['efficiency'],
resource_value = amount_line['resource'],
quantity_unit = amount_line['quantity_unit'],
source = production_node,
source_section = production_section,
destination_list = (),
deliverable = 1
)
LOG('TransformationRule.expand transformed_resource.getPhysicalPath()',0,transformed_resource.getPhysicalPath())
LOG('TransformationRule.expand transformed_resource.getTargetSource()',0,transformed_resource.getTargetSource())
LOG('TransformationRule.expand transformed_resource.getSource()',0,transformed_resource.getSource())
#LOG('RESOURCE', 0, str(amount_line['resource'].getRelativeUrl()))
#LOG('VC List', 0, str(amount_line['variation_category_list']))
#LOG('Quantity', 0, str(amount_line['quantity']))
#LOG('Co Quantity', 0, str(amount_line['converted_quantity']))
variation_category_list = amount_line['variation_category_list']
# Verify each category
category_list = []
for category in variation_category_list:
value = self.portal_simulation.resolveCategory(category)
if value is not None:
category_list += [category]
transformed_resource.setVariationCategoryList(category_list)
acceptable_id_list += [new_id]
LOG('TransformationRule.expand transformed_resource.getPhysicalPath()',0,transformed_resource.getPhysicalPath())
LOG('TransformationRule.expand transformed_resource.getTargetSource()',0,transformed_resource.getTargetSource())
LOG('TransformationRule.expand transformed_resource.getSource()',0,transformed_resource.getSource())
LOG('TransformationRule.expand transformed_resource.showDict()',0,transformed_resource.showDict())
line_number += 1
# Remove each movement not in the transformation def _expandProducedResource(self, applied_rule, production,
for movement in applied_rule.objectValues(): production_section, current_supply_link):
if movement.getId() not in acceptable_id_list: """
movement.flushActivity(invoke=0) Produced resource.
applied_rule._delObject(movement.getId()) # XXXX Make sur this is not deleted if already in delivery Create a movement for the resource produced by the transformation.
LOG('TransformationRule.expand movement.getPhysicalPath()',0,movement.getPhysicalPath()) Only one produced movement can be created.
LOG('TransformationRule.expand movement.getTargetSource()',0,movement.getTargetSource()) """
LOG('TransformationRule.expand movement.getSource()',0,movement.getSource()) parent_movement = applied_rule.getParent()
LOG('TransformationRule.expand movement.getTargetSource',0,movement.getTargetSource) produced_movement_dict = {
LOG('TransformationRule.expand movement.showDict()',0,movement.showDict()) 'pr': {
"resource": parent_movement.getResource(),
# XXX what is lost quantity ?
"quantity": parent_movement.getQuantity(),# + lost_quantity,
"quantity_unit": parent_movement.getQuantityUnit(),
"variation_category_list":\
parent_movement.getVariationCategoryList(),
"source_list": (),
"source_section_list": (),
"destination": production,
"destination_section": production_section,
"deliverable": 1,
# XXX FIXME date not implemented
# "start_date": parent_movement.getStartDate(),
# "stop_date": parent_movement.getStartDate(),
'causality_value': current_supply_link,
}
}
return produced_movement_dict
# Pass to base class def _expandConsumedResource(self, applied_rule, production,
Rule.expand(self, applied_rule, **kw) production_section, current_supply_link):
"""
Consumed resource.
Create a movement for each resource consumed by the transformation,
and for the previous variation of the produced resource.
"""
# Calculate all consumed resource
# Store each value in a dictionnary before created them.
# { movement_id: {property_name: property_value,} ,}
consumed_movement_dict = {}
parent_movement = applied_rule.getParent()
supply_chain = self.getSupplyChain(parent_movement.getParent())
# Consumed previous variation
previous_variation_dict = self._expandConsumedPreviousVariation(
applied_rule,
production,
production_section,
supply_chain,
current_supply_link)
consumed_movement_dict.update(previous_variation_dict)
# Consumed raw materials
raw_material_dict = self._expandConsumedRawMaterials(
applied_rule,
production,
production_section,
supply_chain,
current_supply_link)
consumed_movement_dict.update(raw_material_dict)
return consumed_movement_dict
def _expandConsumedPreviousVariation(self, applied_rule, production,
production_section, supply_chain,
current_supply_link):
"""
Create a movement for the previous variation of the produced resource.
"""
consumed_movement_dict = {}
parent_movement = applied_rule.getParent()
# First, calculate the previous variation of the previous resource
previous_ind_phase_list = supply_chain.\
getPreviousProductionIndustrialPhaseList(current_supply_link)
for ind_phase_value in previous_ind_phase_list:
ind_phase = ind_phase_value.getLogicalPath()
consumed_mvt_id = "%s_%s" % ("mr", ind_phase_value.getId())
consumed_movement_dict[consumed_mvt_id] = {
# XXX FIXME: Not yet implemented
# start_date = parent_movement.getStartDate(),
# stop_date = parent_movement.getStartDate(),
"resource": parent_movement.getResource(),
# XXX Is the quantity value correct ?
"quantity": parent_movement.getQuantity(),
"quantity_unit": parent_movement.getQuantityUnit(),
"destination_list": (),
"destination_section_list": (),
"source": production,
"source_section": production_section,
"deliverable": 1,
"variation_category_list": \
parent_movement.getVariationCategoryList(),
'causality_value': current_supply_link,
"industrial_phase": ind_phase}
return consumed_movement_dict
def _expandConsumedRawMaterials(self, applied_rule, production,
production_section, supply_chain,
current_supply_link):
"""
Create a movement for each resource consumed by the transformation,
"""
parent_movement = applied_rule.getParent()
# Calculate the context for getAggregatedAmountList
base_category_list = parent_movement.getVariationBaseCategoryList()
if "industrial_phase" in base_category_list:
# We do not want to get the industrial phase variation
base_category_list.remove("industrial_phase")
category_list = parent_movement.getVariationCategoryList(
base_category_list=base_category_list)
# Get the transformation to use
production_order = applied_rule.getRootAppliedRule().\
getCausalityValue()
transformation = production_order.getSpecialiseValue(
portal_type=self.getPortalTransformationTypeList())
# Generate the fake context
tmp_context = parent_movement.asContext(
context=parent_movement,
REQUEST={'categories':category_list})
# Calculate the industrial phase list
previous_ind_phase_list = supply_chain.\
getPreviousPackingListIndustrialPhaseList(current_supply_link)
ind_phase_id_list = [x.getId() for x in previous_ind_phase_list]
# Call getAggregatedAmountList
amount_list = transformation.getAggregatedAmountList(
tmp_context,
ind_phase_id_list=ind_phase_id_list)
# Add entries in the consumed_movement_dict
consumed_movement_dict = {}
for amount in amount_list:
consumed_mvt_id = "%s_%s" % ("cr", amount.getId())
consumed_movement_dict[consumed_mvt_id] = {
# XXX FIXME date not implemented
# "start_date": movement.getStartDate(),
# "stop_date": movement.getStartDate(),
"resource": amount.getResource(),
"variation_category_list":\
amount.getVariationCategoryList(),
"quantity": amount.getQuantity() * parent_movement.getQuantity(),
"quantity_unit": amount.getQuantityUnit(),
"destination_list": (),
"destination_section_list": (),
"source": production,
"source_section": production_section,
"deliverable": 1,
'causality_value': current_supply_link,
}
return consumed_movement_dict
security.declareProtected(Permissions.ModifyPortalContent, 'solve') security.declareProtected(Permissions.ModifyPortalContent, 'solve')
def solve(self, applied_rule, solution_list): def solve(self, applied_rule, solution_list):
...@@ -361,3 +329,8 @@ An ERP5 Rule...""" ...@@ -361,3 +329,8 @@ An ERP5 Rule..."""
# Deliverability / orderability # Deliverability / orderability
def isDeliverable(self, m): def isDeliverable(self, m):
return 1 return 1
def isOrderable(self, m):
return 0
from Products.ERP5Type.Utils import monkeyPatch
monkeyPatch(TransformationSourcingRuleMixin, TransformationRule)
############################################################################## ##############################################################################
# #
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. # Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@nexedi.com> # Jean-Paul Smets-Solanes <jp@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -26,6 +27,8 @@ ...@@ -26,6 +27,8 @@
# #
############################################################################## ##############################################################################
import ExtensionClass
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire from Acquisition import aq_base, aq_parent, aq_inner, aq_acquire
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
...@@ -35,110 +38,107 @@ from Products.ERP5.Document.Rule import Rule ...@@ -35,110 +38,107 @@ from Products.ERP5.Document.Rule import Rule
from zLOG import LOG from zLOG import LOG
class TransformationSourcingRuleMixin(ExtensionClass.Base):
"""
Mixin class used by TransformationSourcingRule and TransformationRule
"""
# Declarative security
security = ClassSecurityInfo()
security.declareProtected(Permissions.View,
'getSupplyChain')
def getSupplyChain(self, applied_rule):
"""
Get the SupplyChain.
"""
# Get the SupplyChain to use
supply_chain_portal_type = "Supply Chain"
order = applied_rule.getRootAppliedRule().getCausalityValue()
supply_chain = order.getSpecialiseValue(
portal_type=supply_chain_portal_type)
if supply_chain is None:
raise "ProductionOrderError",\
"No SupplyChain defined on %s" % str(order)
else:
return supply_chain
def getCurrentSupplyLink(self, movement):
"""
Get the current SupplyLink
"""
# Get the current supply link
supply_link_portal_type = "Supply Link"
current_supply_link = movement.getCausalityValue(
portal_type=supply_link_portal_type)
return current_supply_link
security.declareProtected(Permissions.ModifyPortalContent,
'_buildMovementList')
def _buildMovementList(self, applied_rule, movement_dict):
"""
For each movement in the dictionnary, test if the movement already
exists.
If not, create it.
Then, update the movement attributes.
"""
for movement_id in movement_dict.keys():
movement = applied_rule.get(movement_id)
# Create the movement if it does not exist
if movement is None:
movement = applied_rule.newContent(
portal_type=self.simulation_movement_portal_type,
id=movement_id
)
# Update movement properties
movement.edit(**(movement_dict[movement_id]))
class TransformationSourcingRule(Rule): class TransformationSourcingRule(Rule):
""" """
Transformation Sourcing Rule object make sure Transformation Sourcing Rule object make sure
items required in a Transformation are sourced items required in a Transformation are sourced
""" """
# CMF Type Definition # CMF Type Definition
meta_type = 'ERP5 Transformation Sourcing Rule' meta_type = 'ERP5 Transformation Sourcing Rule'
portal_type = 'Transformation Sourcing Rule' portal_type = 'Transformation Sourcing Rule'
add_permission = Permissions.AddPortalContent
isPortalContent = 1
isRADContent = 1
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.View) security.declareObjectProtected(Permissions.View)
# Default Properties # Default Properties
property_sheets = ( PropertySheet.Base property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject , PropertySheet.XMLObject
, PropertySheet.CategoryCore , PropertySheet.CategoryCore
, PropertySheet.DublinCore , PropertySheet.DublinCore
) )
# Class variable
# CMF Factory Type Information simulation_movement_portal_type = "Simulation Movement"
factory_type_information = \
{ 'id' : portal_type
, 'meta_type' : meta_type
, 'description' : """\
An ERP5 Rule..."""
, 'icon' : 'rule_icon.gif'
, 'product' : 'ERP5'
, 'factory' : 'addTransformationSourcingRule'
, 'immediate_view' : 'rule_view'
, 'allow_discussion' : 1
, 'allowed_content_types': ()
, 'filter_content_types' : 1
, 'global_allow' : 1
, 'actions' :
( { 'id' : 'view'
, 'name' : 'View'
, 'category' : 'object_view'
, 'action' : 'rule_view'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'list'
, 'name' : 'Object Contents'
, 'category' : 'object_action'
, 'action' : 'folder_contents'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'print'
, 'name' : 'Print'
, 'category' : 'object_print'
, 'action' : 'rule_print'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'metadata'
, 'name' : 'Metadata'
, 'category' : 'object_view'
, 'action' : 'metadata_edit'
, 'permissions' : (
Permissions.View, )
}
, { 'id' : 'translate'
, 'name' : 'Translate'
, 'category' : 'object_action'
, 'action' : 'translation_template_view'
, 'permissions' : (
Permissions.TranslateContent, )
}
)
}
security.declareProtected(Permissions.AccessContentsInformation, 'test') security.declareProtected(Permissions.AccessContentsInformation, 'test')
def test(self, movement): def test(self, movement):
""" """
Tests if the rule (still) applies Tests if the rule (still) applies
""" """
# Test if some movements are "spendings" movements # Test if we must transform
# ie. with destination equal to None # The test should actually be based on nodes, paths
destination = movement.getDestination() # and capacities, which is not possible now
if destination is not None: result = 1
return 0 # Only apply to Order applied rule
root_applied_rule = movement.getRootAppliedRule()
# Is the resource sourceable (ie. tissue, composant in Coramy Case) root_rule = root_applied_rule.getSpecialiseValue()
# This must become generic in the future through path XXXX order = root_applied_rule.getCausalityValue()
resource = movement.getDefaultResourceValue() # Test some properties to see if we are really
if resource is None: # in a 'production' expand.
return 0 if (root_rule is None) or\
module = resource.aq_parent (root_rule.getPortalType() != "Production Order Rule") or\
(order is None) or\
# Source components and workforce at this point (movement.getResourceValue() is None) or\
# This must become generic in the future through path XXXX (movement.getSourceValue() is None):
if module.id in ('tissu', 'composant'): result = 0
return 1 else:
# We accept operations at this point supply_chain = self.getSupplyChain(movement.getParent())
resource = movement.getDefaultResource() parent_supply_link = self.getCurrentSupplyLink(movement)
if resource.find('operation/') >= 0: if not supply_chain.test(parent_supply_link, movement):
return 1 result = 0
return 0 return result
# Simulation workflow # Simulation workflow
def reset(self, applied_rule): def reset(self, applied_rule):
...@@ -156,73 +156,50 @@ An ERP5 Rule...""" ...@@ -156,73 +156,50 @@ An ERP5 Rule..."""
def expand(self, applied_rule, **kw): def expand(self, applied_rule, **kw):
""" """
Expands the current movement downward. Expands the current movement downward.
-> new status -> expanded -> new status -> expanded
An applied rule can be expanded only if its parent movement An applied rule can be expanded only if its parent movement
is expanded. is expanded.
""" """
delivery_line_type = 'Simulation Movement' parent_movement = applied_rule.getParent()
# Calculate the previous supply link
# Source that movement from the next node / stock supply_chain = self.getSupplyChain(parent_movement.getParent())
my_context_movement = applied_rule.getParent() parent_supply_link = self.getCurrentSupplyLink(parent_movement)
LOG('TransformationSourcingRule.expand, my_context_movement.getPhysicalPath()',0,my_context_movement.getPhysicalPath()) previous_supply_link_list = supply_chain.\
LOG('TransformationSourcingRule.expand, my_context_movement.getSource()',0,my_context_movement.getSource()) getPreviousPackingListSupplyLinkList(
LOG('TransformationSourcingRule.expand, my_context_movement.getTargetSource()',0,my_context_movement.getTargetSource()) parent_supply_link,
LOG('TransformationSourcingRule.expand, my_context_movement.showDict()',0,my_context_movement.showDict()) movement=parent_movement)
LOG('TransformationSourcingRule.expand, my_context_movement.getTargetSource',0,my_context_movement.getTargetSource) if len(previous_supply_link_list) == 0:
if my_context_movement.getSource() is not None: raise "TransformationSourcingRuleError",\
# We should only expand movements if they have a source "Expand must not be called on %r" %\
# otherwise, it creates infinite recursion applied_rule.getRelativeUrl()
# This happens for example whenever the source of a movement is acquired
# from an order which is deleted afterwards
# LOG('Sourcing', 0, str(my_context_movement.getDefaultResource()))
new_id = 'transformation_source'
transformation_source = getattr(aq_base(applied_rule), new_id, None)
if transformation_source is None:
my_context_movement.portal_types.constructContent(
type_name = delivery_line_type,
container = applied_rule,
id = new_id
)
transformation_source = applied_rule[new_id]
resource = my_context_movement.getResource()
if resource.find('operation/') >= 0:
# This is an operation - produce it
transformation_source._edit(
target_quantity = my_context_movement.getTargetQuantity(),
target_efficiency = my_context_movement.getTargetEfficiency(),
resource = resource,
target_start_date = my_context_movement.getTargetStartDate(),
target_stop_date = my_context_movement.getTargetStartDate(),
target_source_list = (),
target_source_section_list = (),
quantity_unit = my_context_movement.getQuantityUnit(),
target_destination = my_context_movement.getTargetSource(),
target_destination_section = my_context_movement.getTargetSourceSection(),
deliverable = 0 # We do not need to source explicitely operations
)
transformation_source.setVariationCategoryList(
my_context_movement.getVariationCategoryList())
else: else:
# This is a component - source from Stock movement_dict = {}
transformation_source._edit( for previous_supply_link in previous_supply_link_list:
target_quantity = my_context_movement.getTargetQuantity(), # Calculate the source
target_efficiency = my_context_movement.getTargetEfficiency(), source_value = None
resource = resource, source_node = previous_supply_link.getSourceValue()
target_start_date = my_context_movement.getTargetStartDate(), if source_node is not None:
target_stop_date = my_context_movement.getTargetStartDate(), source_value = source_node.getDestinationValue()
target_source = 'site/Stock_MP/Gravelines', # Generate the dict
target_source_section = 'group/Coramy', movement_dict.update({
quantity_unit = my_context_movement.getQuantityUnit(), "ts": {
target_destination = my_context_movement.getTargetSource(), 'source_value': source_value,
target_destination_section = my_context_movement.getTargetSourceSection(), 'destination_value': parent_movement.getSourceValue(),
deliverable = 1, 'resource_value': parent_movement.getResourceValue(),
) 'variation_category_list': parent_movement.\
transformation_source.setVariationCategoryList( getVariationCategoryList(),
my_context_movement.getVariationCategoryList()) 'quantity': parent_movement.getQuantity(),
'quantity_unit': parent_movement.getQuantityUnit(),
# XXX FIXME not implemented
# 'start_date': parent_movement.getStartDate(),
# 'stop_date': parent_movement.getStopDate(),
'deliverable': 1,
# Save the value of the current supply link
'causality_value': previous_supply_link,
}
})
# Build the movement
self._buildMovementList(applied_rule, movement_dict)
# Create one submovement which sources the transformation # Create one submovement which sources the transformation
Rule.expand(self, applied_rule, **kw) Rule.expand(self, applied_rule, **kw)
...@@ -276,3 +253,9 @@ An ERP5 Rule...""" ...@@ -276,3 +253,9 @@ An ERP5 Rule..."""
return 0 return 0
else: else:
return 1 return 1
def isOrderable(self, m):
return 0
from Products.ERP5Type.Utils import monkeyPatch
monkeyPatch(TransformationSourcingRuleMixin, TransformationSourcingRule)
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