Commit 14da89cc authored by Jean-Paul Smets's avatar Jean-Paul Smets

Changes in this commit are the consequence of discussion between JPS and YO on...

Changes in this commit are the consequence of discussion between JPS and YO on the split of business path into business link and trade model path.
The notion of "completion date" does not make much sense anymore in a context in which Business Link are unrelated to dates. Instead, we provide helpers to gather lists and stats of simulation movements of an expanded simulation tree. This should be equivalet for usability at user level, with appropriate reports. The notion of "expected start date and stop date" is kept because it is useful. It is only related to Trade Model Path. A mockup (non working) implementation is provided and supports a bit of recursion so that it can handle the case of transformations. Various security declarations were added.
Next commit will make this work if everyone agrees on interfaces.

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@36833 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent eac4b445
...@@ -61,7 +61,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -61,7 +61,7 @@ class BusinessProcess(Path, XMLObject):
Business Process completion, dates, etc. are calculated Business Process completion, dates, etc. are calculated
always in the context of an explanation. Sometimes, always in the context of an explanation. Sometimes,
it is necessary to ignore certain business path to evaluate it is necessary to ignore certain business link to evaluate
completion or completion dates. This is very true for Union completion or completion dates. This is very true for Union
Business Processes. This is the concept of Business Link closure, Business Processes. This is the concept of Business Link closure,
ie. filtering out all Business Link which are not used in an explanation. ie. filtering out all Business Link which are not used in an explanation.
...@@ -83,12 +83,12 @@ class BusinessProcess(Path, XMLObject): ...@@ -83,12 +83,12 @@ class BusinessProcess(Path, XMLObject):
- add security declarations - add security declarations
- why are we using objectValues in some places ? - why are we using objectValues in some places ?
- add a property to rules in order to determine whether dates - add a property to rules in order to determine whether dates
are provided by the rule or by business path. This is an extension are provided by the rule or by business link / trade model path. This is an extension
of the idea that root applied rules provide date information. of the idea that root applied rules provide date information.
- use explanation cache more to optimize speed - use explanation cache more to optimize speed
- DateTime must be extended in ERP5 to support +infinite and -infinite - DateTime must be extended in ERP5 to support +infinite and -infinite
like floating points do like floating points do
- support conversions in business path - support conversions in trade model path
RENAMED: RENAMED:
getPathValueList -> getBusinessLinkValueList getPathValueList -> getBusinessLinkValueList
...@@ -115,11 +115,98 @@ class BusinessProcess(Path, XMLObject): ...@@ -115,11 +115,98 @@ class BusinessProcess(Path, XMLObject):
zope.interface.implements(interfaces.IBusinessProcess, zope.interface.implements(interfaces.IBusinessProcess,
interfaces.IArrowBase) interfaces.IArrowBase)
# ITradeModelPathProcess implementation
security.declareProtected(Permissions.AccessContentsInformation, 'getTradeModelPathValueList')
def getTradeModelPathValueList(self, trade_phase=None, context=None, **kw):
"""Returns all Trade Model Path of the current Business Process which
are matching the given trade_phase and the optional context.
trade_phase -- filter by trade phase
context -- a context to test each Business Link on
and filter out Business Link which do not match
**kw -- same arguments as those passed to searchValues / contentValues
"""
if trade_phase is None:
trade_phase = set()
elif not isinstance(trade_phase, (list, tuple)):
trade_phase = set((trade_phase,))
else:
trade_phase = set(trade_phase)
result = []
if kw.get('portal_type', None) is None:
kw['portal_type'] = self.getPortalTradeModelPathTypeList()
if kw.get('sort_on', None) is None:
kw['sort_on'] = 'int_index'
original_path_list = self.objectValues(**kw) # Why Object Values ??? XXX-JPS
# Separate the selection of trade model paths into two steps
# for easier debugging.
# First, collect trade model paths which can be applicable to a given context.
path_list = []
for path in original_path_list:
accept_path = True
if trade_phase is not None and not trade_phase.intersection(path.getTradePhaseList()):
accept_path = False # Filter our business path which trade phase does not match
if accept_path:
path_list.append(path)
# Then, filter trade model paths by Predicate API.
# FIXME: Ideally, we should use the Domain Tool to search business paths,
# and avoid using the low level Predicate API. But the Domain Tool does
# support the condition above without scripting?
for path in path_list:
if path.test(context):
result.append(path)
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getExpectedTradeModelPathStartAndStopDate')
def getExpectedTradeModelPathStartAndStopDate(self, explanation, trade_model_path,
delay_mode=None):
"""Returns the expected start and stop dates of given Trade Model Path
document in the context of provided explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_model_path -- a Trade Model Path document
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
if explanation.getPortalType() != 'Applied Rule':
raise TypeError('explanation must be an Applied Rule')
if explanation.getParentValue().getPortalType() == 'Simulation Tool':
raise ValueError('explanation must not be a Root Applied Rule')
trade_date = trade_model_path.getTradeDate()
if not trade_date:
raise ValueError('a trade_date must be defined on every Trade Model Path')
reference_date_method_id = trade_model_path.getReferenceDateMethodId()
if not reference_date_method_id:
raise ValueError('a reference date method must be defined on every Trade Model Path')
explanation_cache = _getExplanationCache(self, explanation)
reference_date = explanation_cache.getReferenceDate(self, trade_date, reference_date_method_id)
# Computer start_date and stop_date (XXX-JPS this could be cached and accelerated)
start_date = reference_date + trade_model_path.getPaymentTerm() # XXX-JPS Until better naming
if delay_mode == 'min':
delay = trade_model_path.getMinDelay()
elif delay_mode == 'max':
delay = trade_model_path.getMaxDelay()
else:
delay = (business_link.getMaxDelay() + trade_model_path.getMinDelay()) / 2.0
stop_date = start_date + delay
return start_date, stop_date
# IBusinessLinkProcess implementation # IBusinessLinkProcess implementation
security.declareProtected(Permissions.AccessContentsInformation, 'getBusinessLinkValueList') security.declareProtected(Permissions.AccessContentsInformation, 'getBusinessLinkValueList')
def getBusinessLinkValueList(self, trade_phase=None, context=None, def getBusinessLinkValueList(self, trade_phase=None, context=None,
predecessor=None, successor=None, **kw): predecessor=None, successor=None, **kw):
"""Returns all Path of the current BusinessProcess which """Returns all Business Links of the current BusinessProcess which
are matching the given trade_phase and the optional context. are matching the given trade_phase and the optional context.
trade_phase -- filter by trade phase trade_phase -- filter by trade phase
...@@ -145,22 +232,22 @@ class BusinessProcess(Path, XMLObject): ...@@ -145,22 +232,22 @@ class BusinessProcess(Path, XMLObject):
if kw.get('sort_on', None) is None: if kw.get('sort_on', None) is None:
kw['sort_on'] = 'int_index' kw['sort_on'] = 'int_index'
original_business_link_list = self.objectValues(**kw) # Why Object Values ??? XXX-JPS original_business_link_list = self.objectValues(**kw) # Why Object Values ??? XXX-JPS
# Separate the selection of business paths into two steps # Separate the selection of business links into two steps
# for easier debugging. # for easier debugging.
# First, collect business paths which can be applicable to a given context. # First, collect business links which can be applicable to a given context.
business_link_list = [] business_link_list = []
for business_link in original_business_link_list: for business_link in original_business_link_list:
accept_path = True accept_link = True
if predecessor is not None and business_link.getPredecessor() != predecessor: if predecessor is not None and business_link.getPredecessor() != predecessor:
accept_path = False # Filter our business path which predecessor does not match accept_link = False # Filter our business link which predecessor does not match
if successor is not None and business_link.getSuccessor() != successor: if successor is not None and business_link.getSuccessor() != successor:
accept_path = False # Filter our business path which predecessor does not match accept_link = False # Filter our business link which predecessor does not match
if trade_phase is not None and not trade_phase.intersection(business_link.getTradePhaseList()): if trade_phase is not None and not trade_phase.intersection(business_link.getTradePhaseList()):
accept_path = False # Filter our business path which trade phase does not match accept_link = False # Filter our business link which trade phase does not match
if accept_path: if accept_link:
business_link_list.append(business_link) business_link_list.append(business_link)
# Then, filter business paths by Predicate API. # Then, filter business links by Predicate API.
# FIXME: Ideally, we should use the Domain Tool to search business paths, # FIXME: Ideally, we should use the Domain Tool to search business links,
# and avoid using the low level Predicate API. But the Domain Tool does # and avoid using the low level Predicate API. But the Domain Tool does
# support the condition above without scripting? # support the condition above without scripting?
for business_link in business_link_list: for business_link in business_link_list:
...@@ -168,6 +255,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -168,6 +255,7 @@ class BusinessProcess(Path, XMLObject):
result.append(business_link) result.append(business_link)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'isBusinessLinkCompleted')
def isBusinessLinkCompleted(self, explanation, business_link): def isBusinessLinkCompleted(self, explanation, business_link):
"""Returns True if given Business Link document """Returns True if given Business Link document
is completed in the context of provided explanation. is completed in the context of provided explanation.
...@@ -182,7 +270,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -182,7 +270,7 @@ class BusinessProcess(Path, XMLObject):
return False return False
predecessor_state = business_link.getPredecessor() predecessor_state = business_link.getPredecessor()
if not predecessor_state: if not predecessor_state:
# This is a root business path, no predecessor # This is a root business links, no predecessor
# so no need to do any recursion # so no need to do any recursion
return True return True
if self.isTradeStateCompleted(explanation, predecessor_state): if self.isTradeStateCompleted(explanation, predecessor_state):
...@@ -190,16 +278,17 @@ class BusinessProcess(Path, XMLObject): ...@@ -190,16 +278,17 @@ class BusinessProcess(Path, XMLObject):
# given explanation, return True # given explanation, return True
# Please note that this is a specific case for a Business Process # Please note that this is a specific case for a Business Process
# built using asUnionBusinessProcess. In such business process # built using asUnionBusinessProcess. In such business process
# a business path may be completed even if its predecessor state # a business link may be completed even if its predecessor state
# is not # is not
return True return True
# Build the closure business process which only includes those business # Build the closure business process which only includes those business
# path wich are directly related to the current business path but DO NOT # links wich are directly related to the current business link but DO NOT
# narrow down the explanation else we might narrow down so much that # narrow down the explanation else we might narrow down so much that
# it becomes an empty set # it becomes an empty set
closure_process = _getBusinessLinkClosure(self, explanation, business_link) closure_process = _getBusinessLinkClosure(self, explanation, business_link)
return closure_process.isTradeStateCompleted(explanation, predecessor_state) return closure_process.isTradeStateCompleted(explanation, predecessor_state)
security.declareProtected(Permissions.AccessContentsInformation, 'isBusinessLinkPartiallyCompleted')
def isBusinessLinkPartiallyCompleted(self, explanation, business_link): def isBusinessLinkPartiallyCompleted(self, explanation, business_link):
"""Returns True if given Business Link document """Returns True if given Business Link document
is partially completed in the context of provided explanation. is partially completed in the context of provided explanation.
...@@ -214,7 +303,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -214,7 +303,7 @@ class BusinessProcess(Path, XMLObject):
return False return False
predecessor_state = business_link.getPredecessor() predecessor_state = business_link.getPredecessor()
if not predecessor_state: if not predecessor_state:
# This is a root business path, no predecessor # This is a root business link, no predecessor
# so no need to do any recursion # so no need to do any recursion
return True return True
if self.isTradeStatePartiallyCompleted(explanation, predecessor_state): if self.isTradeStatePartiallyCompleted(explanation, predecessor_state):
...@@ -222,94 +311,19 @@ class BusinessProcess(Path, XMLObject): ...@@ -222,94 +311,19 @@ class BusinessProcess(Path, XMLObject):
# given explanation, return True # given explanation, return True
# Please note that this is a specific case for a Business Process # Please note that this is a specific case for a Business Process
# built using asUnionBusinessProcess. In such business process # built using asUnionBusinessProcess. In such business process
# a business path may be partially completed even if its predecessor # a business link may be partially completed even if its predecessor
# state is not # state is not
return True return True
# Build the closure business process which only includes those business # Build the closure business process which only includes those business
# path wich are directly related to the current business path but DO NOT # links wich are directly related to the current business link but DO NOT
# narrow down the explanation else we might narrow down so much that # narrow down the explanation else we might narrow down so much that
# it becomes an empty set # it becomes an empty set
closure_process = _getBusinessLinkClosure(explanation, business_link) closure_process = _getBusinessLinkClosure(explanation, business_link)
return closure_process.isTradeStatePartiallyCompleted(explanation, return closure_process.isTradeStatePartiallyCompleted(explanation,
predecessor_state) predecessor_state)
def getExpectedBusinessLinkCompletionDate(self, explanation, business_link,
delay_mode=None):
"""Returns the expected completion date of given Business Link document
in the context of provided explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
business_link -- a Business Link document
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
closure_process = _getBusinessLinkClosure(self, explanation, business_link)
# XXX use explanation cache to optimize
predecessor = business_link.getPredecessor()
if closure_process.isInitialTradeState(predecessor):
return business_link.getCompletionDate(explanation)
# Recursively find reference_date
reference_date = closure_process.getExpectedTradeStateCompletionDate(explanation, predecessor)
start_date = reference_date + business_link.getPaymentTerm() # XXX-JPS Until better naming
if delay_mode == 'min':
delay = business_link.getMinDelay()
elif delay_mode == 'max':
delay = business_link.getMaxDelay()
else:
delay = (business_link.getMaxDelay() + business_link.getMinDelay()) / 2.0
stop_date = start_date + delay
completion_date_method_id = business_link.getCompletionDateMethodId()
if completion_date_method_id == 'getStartDate':
return start_date
elif completion_date_method_id == 'getStopDate':
return stop_date
raise ValueError("Business Link does not support %s complete date method" % completion_date_method_id)
def getExpectedTradeModelPathStartAndStopDate(self, explanation, trade_model_path,
delay_mode=None):
"""Returns the expected start and stop dates of given Trade Model Path
document in the context of provided explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_model_path -- a Trade Model Path document
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
if explanation.getPortalType() != 'Applied Rule':
raise TypeError('explanation must be an Applied Rule as part of expand process')
closure_process = _getBusinessLinkClosure(self, explanation, trade_model_path) # XXX-JPS ???
# XXX use explanation cache to optimize
trade_date = trade_model_path.getTradeDate()
if trade_date is not None:
trade_phase = trade_date[len('trade_date/'):] # XXX-JPS quite dirty way
reference_date = closure_process.getExpectedTradePhaseCompletionDate(explanation, trade_phase)
else:
predecessor = business_link.getPredecessor() # XXX-JPS all states are supposed to have a predecessor
reference_date = closure_process.getExpectedTradeStateCompletionDate(explanation, predecessor)
# Recursively find reference_date
reference_date = closure_process.getExpectedTradeStateCompletionDate(explanation, predecessor)
start_date = reference_date + trade_model_path.getPaymentTerm() # XXX-JPS Until better naming
if delay_mode == 'min':
delay = trade_model_path.getMinDelay()
elif delay_mode == 'max':
delay = trade_model_path.getMaxDelay()
else:
delay = (business_link.getMaxDelay() + trade_model_path.getMinDelay()) / 2.0
stop_date = start_date + delay
return start_date, stop_date
# IBuildableBusinessLinkProcess implementation # IBuildableBusinessLinkProcess implementation
security.declareProtected(Permissions.AccessContentsInformation, 'getBuildableBusinessLinkValueList')
def getBuildableBusinessLinkValueList(self, explanation): def getBuildableBusinessLinkValueList(self, explanation):
"""Returns the list of Business Link which are buildable """Returns the list of Business Link which are buildable
by taking into account trade state dependencies between by taking into account trade state dependencies between
...@@ -324,6 +338,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -324,6 +338,7 @@ class BusinessProcess(Path, XMLObject):
result.append(business_link) result.append(business_link)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'getPartiallyBuildableBusinessLinkValueList')
def getPartiallyBuildableBusinessLinkValueList(self, explanation): def getPartiallyBuildableBusinessLinkValueList(self, explanation):
"""Returns the list of Business Link which are partially buildable """Returns the list of Business Link which are partially buildable
by taking into account trade state dependencies between by taking into account trade state dependencies between
...@@ -338,6 +353,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -338,6 +353,7 @@ class BusinessProcess(Path, XMLObject):
result.append(business_link) result.append(business_link)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'isBusinessLinkBuildable')
def isBusinessLinkBuildable(self, explanation, business_link): def isBusinessLinkBuildable(self, explanation, business_link):
"""Returns True if any of the related Simulation Movement """Returns True if any of the related Simulation Movement
is buildable and if the predecessor trade state is completed. is buildable and if the predecessor trade state is completed.
...@@ -355,6 +371,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -355,6 +371,7 @@ class BusinessProcess(Path, XMLObject):
predecessor = business_link.getPredecessor() predecessor = business_link.getPredecessor()
return closure_process.isTradeStateCompleted(predecessor) return closure_process.isTradeStateCompleted(predecessor)
security.declareProtected(Permissions.AccessContentsInformation, 'isBusinessLinkPartiallyBuildable')
def isBusinessLinkPartiallyBuildable(self, explanation, business_link): def isBusinessLinkPartiallyBuildable(self, explanation, business_link):
"""Returns True if any of the related Simulation Movement """Returns True if any of the related Simulation Movement
is buildable and if the predecessor trade state is partially completed. is buildable and if the predecessor trade state is partially completed.
...@@ -373,6 +390,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -373,6 +390,7 @@ class BusinessProcess(Path, XMLObject):
return closure_process.isTradeStatePartiallyCompleted(predecessor) return closure_process.isTradeStatePartiallyCompleted(predecessor)
# ITradeStateProcess implementation # ITradeStateProcess implementation
security.declareProtected(Permissions.AccessContentsInformation, 'getTradeStateList')
def getTradeStateList(self): def getTradeStateList(self):
"""Returns list of all trade_state of this Business Process """Returns list of all trade_state of this Business Process
by looking at successor and predecessor values of contained by looking at successor and predecessor values of contained
...@@ -387,6 +405,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -387,6 +405,7 @@ class BusinessProcess(Path, XMLObject):
result.add(business_link.getSuccessor()) result.add(business_link.getSuccessor())
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'isInitialTradeState')
def isInitialTradeState(self, trade_state): def isInitialTradeState(self, trade_state):
"""Returns True if given 'trade_state' has no successor related """Returns True if given 'trade_state' has no successor related
Business Link. Business Link.
...@@ -395,6 +414,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -395,6 +414,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return len(self.getBusinessLinkValueList(successor=trade_state)) == 0 return len(self.getBusinessLinkValueList(successor=trade_state)) == 0
security.declareProtected(Permissions.AccessContentsInformation, 'isFinalTradeState')
def isFinalTradeState(self, trade_state): def isFinalTradeState(self, trade_state):
"""Returns True if given 'trade_state' has no predecessor related """Returns True if given 'trade_state' has no predecessor related
Business Link. Business Link.
...@@ -403,10 +423,11 @@ class BusinessProcess(Path, XMLObject): ...@@ -403,10 +423,11 @@ class BusinessProcess(Path, XMLObject):
""" """
return len(self.getBusinessLinkValueList(predecessor=trade_state)) == 0 return len(self.getBusinessLinkValueList(predecessor=trade_state)) == 0
security.declareProtected(Permissions.AccessContentsInformation, 'getSuccessorTradeStateList')
def getSuccessorTradeStateList(self, explanation, trade_state): def getSuccessorTradeStateList(self, explanation, trade_state):
"""Returns the list of successor states in the """Returns the list of successor states in the
context of given explanation. This list is built by looking context of given explanation. This list is built by looking
at all successor of business path involved in given explanation at all successor of business link involved in given explanation
and which predecessor is the given trade_phase. and which predecessor is the given trade_phase.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -420,10 +441,11 @@ class BusinessProcess(Path, XMLObject): ...@@ -420,10 +441,11 @@ class BusinessProcess(Path, XMLObject):
result.add(business_link.getSuccessor()) result.add(business_link.getSuccessor())
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'getPredecessorTradeStateList')
def getPredecessorTradeStateList(self, explanation, trade_state): def getPredecessorTradeStateList(self, explanation, trade_state):
"""Returns the list of predecessor states in the """Returns the list of predecessor states in the
context of given explanation. This list is built by looking context of given explanation. This list is built by looking
at all predecessor of business path involved in given explanation at all predecessor of business link involved in given explanation
and which sucessor is the given trade_phase. and which sucessor is the given trade_phase.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -437,6 +459,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -437,6 +459,7 @@ class BusinessProcess(Path, XMLObject):
result.add(business_link.getPredecessor()) result.add(business_link.getPredecessor())
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'getCompletedTradeStateList')
def getCompletedTradeStateList(self, explanation): def getCompletedTradeStateList(self, explanation):
"""Returns the list of Trade States which are completed """Returns the list of Trade States which are completed
in the context of given explanation. in the context of given explanation.
...@@ -446,6 +469,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -446,6 +469,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return filter(lambda x:self.isTradeStateCompleted(explanation, x), self.getTradeStateList()) return filter(lambda x:self.isTradeStateCompleted(explanation, x), self.getTradeStateList())
security.declareProtected(Permissions.AccessContentsInformation, 'getPartiallyCompletedTradeStateList')
def getPartiallyCompletedTradeStateList(self, explanation): def getPartiallyCompletedTradeStateList(self, explanation):
"""Returns the list of Trade States which are partially """Returns the list of Trade States which are partially
completed in the context of given explanation. completed in the context of given explanation.
...@@ -455,6 +479,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -455,6 +479,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return filter(lambda x:self.isTradeStatePartiallyCompleted(explanation, x), self.getTradeStateList()) return filter(lambda x:self.isTradeStatePartiallyCompleted(explanation, x), self.getTradeStateList())
security.declareProtected(Permissions.AccessContentsInformation, 'getLatestCompletedTradeStateList')
def getLatestCompletedTradeStateList(self, explanation): def getLatestCompletedTradeStateList(self, explanation):
"""Returns the list of completed trade states which predecessor """Returns the list of completed trade states which predecessor
states are completed and for which no successor state states are completed and for which no successor state
...@@ -470,6 +495,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -470,6 +495,7 @@ class BusinessProcess(Path, XMLObject):
result.add(state) result.add(state)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'getLatestPartiallyCompletedTradeStateList')
def getLatestPartiallyCompletedTradeStateList(self, explanation): def getLatestPartiallyCompletedTradeStateList(self, explanation):
"""Returns the list of completed trade states which predecessor """Returns the list of completed trade states which predecessor
states are completed and for which no successor state states are completed and for which no successor state
...@@ -485,6 +511,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -485,6 +511,7 @@ class BusinessProcess(Path, XMLObject):
result.add(state) result.add(state)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'isTradeStateCompleted')
def isTradeStateCompleted(self, explanation, trade_state): def isTradeStateCompleted(self, explanation, trade_state):
"""Returns True if all predecessor trade states are """Returns True if all predecessor trade states are
completed and if no successor trade state is completed completed and if no successor trade state is completed
...@@ -500,6 +527,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -500,6 +527,7 @@ class BusinessProcess(Path, XMLObject):
return False return False
return True return True
security.declareProtected(Permissions.AccessContentsInformation, 'isTradeStatePartiallyCompleted')
def isTradeStatePartiallyCompleted(self, explanation, trade_state): def isTradeStatePartiallyCompleted(self, explanation, trade_state):
"""Returns True if all predecessor trade states are """Returns True if all predecessor trade states are
completed and if no successor trade state is partially completed completed and if no successor trade state is partially completed
...@@ -515,25 +543,8 @@ class BusinessProcess(Path, XMLObject): ...@@ -515,25 +543,8 @@ class BusinessProcess(Path, XMLObject):
return False return False
return True return True
def getExpectedTradeStateCompletionDate(self, explanation, trade_state,
delay_mode=None):
"""Returns the date at which the give trade state is expected
to be completed in the context of given explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_state -- a Trade State category
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
date_list = []
for business_link in self.getBusinessLinkValueList(successor=trade_state):
date_list.append(self.getExpectedBusinessLinkCompletionDate(explanation, business_link))
return max(date_list) # XXX-JPS it would be good that date support infinite values
# ITradePhaseProcess implementation # ITradePhaseProcess implementation
security.declareProtected(Permissions.AccessContentsInformation, 'getTradePhaseList')
def getTradePhaseList(self): def getTradePhaseList(self):
"""Returns list of all trade_phase of this Business Process """Returns list of all trade_phase of this Business Process
by looking at trade_phase values of contained Business Link. by looking at trade_phase values of contained Business Link.
...@@ -543,6 +554,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -543,6 +554,7 @@ class BusinessProcess(Path, XMLObject):
result = result.union(business_link.getTradePhaseList()) result = result.union(business_link.getTradePhaseList())
return result return result
security.declareProtected(Permissions.AccessContentsInformation, 'getCompletedTradePhaseList')
def getCompletedTradePhaseList(self, explanation): def getCompletedTradePhaseList(self, explanation):
"""Returns the list of Trade Phases which are completed """Returns the list of Trade Phases which are completed
in the context of given explanation. in the context of given explanation.
...@@ -552,6 +564,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -552,6 +564,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return filter(lambda x:self.isTradePhaseCompleted(explanation, x), self.getTradePhaseList()) return filter(lambda x:self.isTradePhaseCompleted(explanation, x), self.getTradePhaseList())
security.declareProtected(Permissions.AccessContentsInformation, 'getPartiallyCompletedTradePhaseList')
def getPartiallyCompletedTradePhaseList(self, explanation): def getPartiallyCompletedTradePhaseList(self, explanation):
"""Returns the list of Trade Phases which are partially completed """Returns the list of Trade Phases which are partially completed
in the context of given explanation. in the context of given explanation.
...@@ -561,8 +574,9 @@ class BusinessProcess(Path, XMLObject): ...@@ -561,8 +574,9 @@ class BusinessProcess(Path, XMLObject):
""" """
return filter(lambda x:self.isTradePhasePartiallyCompleted(explanation, x), self.getTradePhaseList()) return filter(lambda x:self.isTradePhasePartiallyCompleted(explanation, x), self.getTradePhaseList())
security.declareProtected(Permissions.AccessContentsInformation, 'isTradePhaseCompleted')
def isTradePhaseCompleted(self, explanation, trade_phase): def isTradePhaseCompleted(self, explanation, trade_phase):
"""Returns True all business path with given trade_phase """Returns True all business link with given trade_phase
applicable to given explanation are completed. applicable to given explanation are completed.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -575,8 +589,9 @@ class BusinessProcess(Path, XMLObject): ...@@ -575,8 +589,9 @@ class BusinessProcess(Path, XMLObject):
return False return False
return True return True
security.declareProtected(Permissions.AccessContentsInformation, 'isTradePhasePartiallyCompleted')
def isTradePhasePartiallyCompleted(self, explanation, trade_phase): def isTradePhasePartiallyCompleted(self, explanation, trade_phase):
"""Returns True at least one business path with given trade_phase """Returns True at least one business link with given trade_phase
applicable to given explanation is partially completed applicable to given explanation is partially completed
or completed. or completed.
...@@ -590,31 +605,12 @@ class BusinessProcess(Path, XMLObject): ...@@ -590,31 +605,12 @@ class BusinessProcess(Path, XMLObject):
return False return False
return True return True
def getExpectedTradePhaseCompletionDate(self, explanation, trade_phase, security.declareProtected(Permissions.AccessContentsInformation, 'getRemainingTradePhaseList')
delay_mode=None):
"""Returns the date at which the give trade phase is expected
to be completed in the context of given explanation, taking
into account the graph of date constraints defined by business path
and business states.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_phase -- a Trade Phase category
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
date_list = []
for business_link in self.getBusinessLinkValueList(trade_phase=trade_phase):
date_list.append(self.getExpectedBusinessLinkCompletionDate(explanation, business_link, delay_mode=delay_mode))
return max(date_list)
def getRemainingTradePhaseList(self, business_link, trade_phase_list=None): def getRemainingTradePhaseList(self, business_link, trade_phase_list=None):
"""Returns the list of remaining trade phases which to be achieved """Returns the list of remaining trade phases which to be achieved
as part of a business process. This list is calculated by analysing as part of a business process. This list is calculated by analysing
the graph of business path and trade states, starting from a given the graph of business link and trade states, starting from a given
business path. The result if filtered by a list of trade phases. This business link. The result if filtered by a list of trade phases. This
method is useful mostly for production and MRP to manage a distributed method is useful mostly for production and MRP to manage a distributed
supply and production chain. supply and production chain.
...@@ -633,16 +629,16 @@ class BusinessProcess(Path, XMLObject): ...@@ -633,16 +629,16 @@ class BusinessProcess(Path, XMLObject):
API less uniform. API less uniform.
""" """
remaining_trade_phase_list = [] remaining_trade_phase_list = []
for path in [x for x in self.objectValues(portal_type="Business Link") \ for link in [x for x in self.objectValues(portal_type="Business Link") \
if x.getPredecessorValue() == trade_state]: if x.getPredecessorValue() == trade_state]:
# XXX When no simulations related to path, what should path.isCompleted return? # XXX When no simulations related to link, what should link.isCompleted return?
# if True we don't have way to add remaining trade phases to new movement # if True we don't have way to add remaining trade phases to new movement
if not (path.getRelatedSimulationMovementValueList(explanation) and if not (link.getRelatedSimulationMovementValueList(explanation) and
path.isCompleted(explanation)): link.isCompleted(explanation)):
remaining_trade_phase_list += path.getTradePhaseValueList() remaining_trade_phase_list += link.getTradePhaseValueList()
# collect to successor direction recursively # collect to successor direction recursively
state = path.getSuccessorValue() state = link.getSuccessorValue()
if state is not None: if state is not None:
remaining_trade_phase_list.extend( remaining_trade_phase_list.extend(
self.getRemainingTradePhaseList(explanation, state, None)) self.getRemainingTradePhaseList(explanation, state, None))
...@@ -655,6 +651,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -655,6 +651,7 @@ class BusinessProcess(Path, XMLObject):
return remaining_trade_phase_list return remaining_trade_phase_list
security.declareProtected(Permissions.AccessContentsInformation, 'getTradePhaseMovementList')
def getTradePhaseMovementList(self, explanation, amount, trade_phase=None, delay_mode=None): def getTradePhaseMovementList(self, explanation, amount, trade_phase=None, delay_mode=None):
"""Returns a list of movement with appropriate arrow and dates, """Returns a list of movement with appropriate arrow and dates,
based on the Business Link definitions, provided 'amount' and optional based on the Business Link definitions, provided 'amount' and optional
...@@ -681,7 +678,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -681,7 +678,7 @@ class BusinessProcess(Path, XMLObject):
result = [] result = []
id_index = 0 id_index = 0
base_id = amount.getId() base_id = amount.getId()
for business_link in self.getTradePathValueList(context=amount, trade_phase=trade_phase): for business_link in self.getTradeModelPathValueList(context=amount, trade_phase=trade_phase):
id_index += 1 id_index += 1
movement = newTempMovement(business_link, '%s_%s' % (base_id, id_index)) movement = newTempMovement(business_link, '%s_%s' % (base_id, id_index))
kw = self._getPropertyAndCategoryDict(explanation, amount, business_link, delay_mode=delay_mode) kw = self._getPropertyAndCategoryDict(explanation, amount, business_link, delay_mode=delay_mode)
...@@ -765,19 +762,20 @@ class BusinessProcess(Path, XMLObject): ...@@ -765,19 +762,20 @@ class BusinessProcess(Path, XMLObject):
# applied rules which are not root applied rules. # applied rules which are not root applied rules.
# XXX-JPS could be extended with a rule property instead # XXX-JPS could be extended with a rule property instead
# of supports only in root applied rule case # of supports only in root applied rule case
start_date, stop_date = self.getExpectedTradePathStartAndStopDate( start_date, stop_date = self.getExpectedTradeModelPathStartAndStopDate(
explanation, business_link, delay_mode=delay_mode) explanation, business_link, delay_mode=delay_mode)
property_dict['start_date'] = start_date property_dict['start_date'] = start_date
property_dict['stop_date'] = stop_date property_dict['stop_date'] = stop_date
else: else:
raise TypeError("Explanation must be an Applied Rule in expand process") # Nothing to do raise TypeError("Explanation must be an Applied Rule in expand process") # Nothing to do
# Set causality to business path # Set causality to business link
property_dict['causality'] = business_link.getRelativeUrl() # XXX-JPS Will not work if we do not use real object property_dict['causality'] = business_link.getRelativeUrl() # XXX-JPS Will not work if we do not use real object
return property_dict return property_dict
# IBusinessProcess global API # IBusinessProcess global API
security.declareProtected(Permissions.AccessContentsInformation, 'isCompleted')
def isCompleted(self, explanation): def isCompleted(self, explanation):
"""Returns True is all applicable Trade States and Trade Phases """Returns True is all applicable Trade States and Trade Phases
are completed in the context of given explanation. are completed in the context of given explanation.
...@@ -789,24 +787,8 @@ class BusinessProcess(Path, XMLObject): ...@@ -789,24 +787,8 @@ class BusinessProcess(Path, XMLObject):
if not state.isTradeStateCompleted(explanation): if not state.isTradeStateCompleted(explanation):
return False return False
return True return True
def getExpectedCompletionDate(self, explanation, delay_mode=None):
"""Returns the expected date at which all applicable Trade States and
Trade Phases are completed in the context of given explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
"""
date_list = []
# This implementation looks completely silly in the sense that it does
# not try to find a "final" state. However, it has the advantage to support
# negative delays in business path and propper optimization of ExplanationCache
# and completion methods should actually prevent calculating the same
# thing multiple times
for trade_state in self.getTradeStateList():
date_list.append(self.getExpectedTradeStateCompletionDate(explanation, delay_mode=delay_mode))
return max(date_list)
security.declareProtected(Permissions.AccessContentsInformation, 'isBuildable')
def isBuildable(self, explanation): def isBuildable(self, explanation):
"""Returns True is one Business Link of this Business Process """Returns True is one Business Link of this Business Process
is buildable in the context of given explanation. is buildable in the context of given explanation.
...@@ -816,6 +798,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -816,6 +798,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return len(self.getBuildableBusinessLinkValueList(explanation)) != 0 return len(self.getBuildableBusinessLinkValueList(explanation)) != 0
security.declareProtected(Permissions.AccessContentsInformation, 'isPartiallyBuildable')
def isPartiallyBuildable(self, explanation): def isPartiallyBuildable(self, explanation):
"""Returns True is one Business Link of this Business Process """Returns True is one Business Link of this Business Process
is partially buildable in the context of given explanation. is partially buildable in the context of given explanation.
...@@ -825,6 +808,7 @@ class BusinessProcess(Path, XMLObject): ...@@ -825,6 +808,7 @@ class BusinessProcess(Path, XMLObject):
""" """
return len(self.getPartiallyBuildableBusinessLinkValueList(explanation)) != 0 return len(self.getPartiallyBuildableBusinessLinkValueList(explanation)) != 0
security.declareProtected(Permissions.AccessContentsInformation, 'build')
def build(self, explanation): def build(self, explanation):
""" """
Build whatever is buildable Build whatever is buildable
......
...@@ -203,8 +203,8 @@ class ExplanationCache: ...@@ -203,8 +203,8 @@ class ExplanationCache:
NOTE: Business Link Closure must be at least as "big" as composed NOTE: Business Link Closure must be at least as "big" as composed
business path. The appropriate calculation is still not clear. business path. The appropriate calculation is still not clear.
Options are: Options are:
- take all path of composed business path (even not yet expanded) - take all link of composed business link (even not yet expanded)
- take all path of composed business path which phase is not yet expanded - take all link of composed business link which phase is not yet expanded
""" """
# Try to return cached value first # Try to return cached value first
new_business_process = self.closure_cache.get(business_link, None) new_business_process = self.closure_cache.get(business_link, None)
...@@ -271,6 +271,43 @@ class ExplanationCache: ...@@ -271,6 +271,43 @@ class ExplanationCache:
self.union_cache = new_business_process self.union_cache = new_business_process
return new_business_process return new_business_process
def getReferenceDate(self, business_process, trade_phase, reference_date_method_id, delay_mode=None):
"""Browse parent similation movements until a movement with
appropriate trade_phase is found.
"""
# Find simulation movements with appropriate trade_phase
movement_list = self.getSimulationMovementValueList(trade_phase=trade_phase)
# Case 1: some (parent) simulation movement with appropriate trade phase exists
if len(movement_list):
# XXX-JPS - for now take arbitrary one
# but we should in reality some way to configure this
movement = movement_list[0]
method = getattr(movement, reference_date_method_id)
return method()
# Case 2: we must recursively find another trade phase
# to find the value recursively
# XXX-JPS this is only useful for production (MRP) in reality
# whenever trade model path define time constraints within the same
# movement generator (ie. transformation with multiple phases)
path_list = business_process.getTradeModelPathValueList(trade_phase=trade_phase)
if not len(path_list):
raise ValueError('No Trade Model Path defines a reference data.')
path = path_list[0]
# XXX-JPS - for now take arbitrary one
# but we should in reality some way to configure this
start_date, stop_date = business_process.getExpectedTradeModelPathStartAndStopDate(
self.explanation, path, delay_mode=delay_mode)
# Create a fake simulation movement and lookup property
movement = self.explanation.newContent(portal_type="Simulation Movement",
temp_object=True,
start_date=start_date, stop_date=stop_date,
trade_phase=trade_phase, causality=path)
method = getattr(movement, reference_date_method_id)
return method()
def _getExplanationCache(explanation): def _getExplanationCache(explanation):
# Return cached value if any # Return cached value if any
......
...@@ -33,11 +33,6 @@ class BusinessLink: ...@@ -33,11 +33,6 @@ class BusinessLink:
Business Link properties Business Link properties
""" """
_properties = ( _properties = (
{ 'id' : 'completion_date_method_id',
'description' : 'ID of method to get source list of categories',
'type' : 'string',
'default' : 'getStartDate',
'mode' : 'w' },
{ 'id' : 'completed_state', { 'id' : 'completed_state',
'description' : 'List of states for which related Simulation ' 'description' : 'List of states for which related Simulation '
'Movement is considered as completed', 'Movement is considered as completed',
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Lukasz Nowak <luke@nexedi.com>
# Yusuke Muraoka <yusuke@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability 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
# garantees and support are strongly adviced 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
class TradeModelPath:
"""
Trade Model Path properties
"""
_properties = (
{ 'id' : 'source_method_id',
'description' : 'ID of method to get source list of categories',
'type' : 'string',
'mode' : 'w' },
{ 'id' : 'destination_method_id',
'description' : 'ID of method to get destination list of categories',
'type' : 'string',
'mode' : 'w' },
{ 'id' : 'reference_date_method_id',
'description' : 'ID of method to get the reference date at the trade_phase defined by trade_date',
'type' : 'string',
'default' : 'getStopDate',
'mode' : 'w' },
)
_categories = ('end_of', # XXX-JPS What is end_of ????
'trade_phase' , 'incoterm') # XXX-JPS why incoterm ?
...@@ -33,29 +33,39 @@ Products.ERP5.interfaces.business_process ...@@ -33,29 +33,39 @@ Products.ERP5.interfaces.business_process
from zope.interface import Interface from zope.interface import Interface
class ITradeModelPathProcess(Interface): class ITradeModelPathProcess(Interface):
""" """Trade Model Path Process interface specification
ITradeModelPathProcess defines Business Process APIs related
to Trade Model Path access and to the evaluation of start_date,
stop date, quantity shares and arrows of an Amount.
""" """
def getTradeModelPathValueList(): def getTradeModelPathValueList(trade_phase=None, context=None, **kw):
""" """Returns all Trade Model Path of the current Business Process which
are matching the given trade_phase and the optional context.
trade_phase -- filter by trade phase
context -- a context to test each Business Link on
and filter out Business Link which do not match
**kw -- same arguments as those passed to searchValues / contentValues
""" """
def getExpectedTradeModelPathStartAndStopDate(explanation, business_link, def getExpectedTradeModelPathStartAndStopDate(explanation, trade_model_path,
delay_mode=None): delay_mode=None):
"""Returns the expected start and stop dates of given Business Link """Returns the expected start and stop dates of given Trade Model Path
document in the context of provided explanation. document in the context of provided explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree Applied Rule which implicitely defines a simulation subtree
business_link -- a Business Link document trade_model_path -- a Trade Model Path document
delay_mode -- optional value to specify calculation mode ('min', 'max') delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay if no value specified use average delay
""" """
class IBusinessLinkProcess(Interface): class IBusinessLinkProcess(Interface):
"""Business Link Process interface specification """Business Link Process interface specification
...@@ -81,7 +91,8 @@ class IBusinessLinkProcess(Interface): ...@@ -81,7 +91,8 @@ class IBusinessLinkProcess(Interface):
def getBusinessLinkValueList(trade_phase=None, context=None, def getBusinessLinkValueList(trade_phase=None, context=None,
predecessor=None, successor=None, **kw): predecessor=None, successor=None, **kw):
"""Returns the list of contained Business Link documents """Returns all Business Links of the current BusinessProcess which
are matching the given trade_phase and the optional context.
trade_phase -- filter by trade phase trade_phase -- filter by trade phase
...@@ -115,26 +126,11 @@ class IBusinessLinkProcess(Interface): ...@@ -115,26 +126,11 @@ class IBusinessLinkProcess(Interface):
business_link -- a Business Link document business_link -- a Business Link document
""" """
def getExpectedBusinessLinkCompletionDate(explanation, business_link,
delay_mode=None):
"""Returns the expected completion date of given Business Link document
in the context of provided explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
business_link -- a Business Link document
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
class IBuildableBusinessLinkProcess(Interface): class IBuildableBusinessLinkProcess(Interface):
"""Buildable Business Link Process interface specification """Buildable Business Link Process interface specification
IBuildableBusinessLinkProcess defines an API to build IBuildableBusinessLinkProcess defines an API to build
simulation movements related to business pathj in the context simulation movements related to business link in the context
of a given explanation. of a given explanation.
""" """
...@@ -192,11 +188,11 @@ class ITradeStateProcess(Interface): ...@@ -192,11 +188,11 @@ class ITradeStateProcess(Interface):
ITradeStateProcess defines Business Process APIs related ITradeStateProcess defines Business Process APIs related
to Trade State completion status and expected completion dates. to Trade State completion status and expected completion dates.
ITradeStateProcess APIs recursively browse trade states and business ITradeStateProcess APIs recursively browse trade states and business
path to provide completion status and expected completion dates. links to provide completion status and expected completion dates.
For example, a complete trade state is a trade state for For example, a complete trade state is a trade state for
which all predecessor trade states are completed and for which all predecessor trade states are completed and for
which all business path applicable to the given explanation which all business links applicable to the given explanation
are also completed. are also completed.
""" """
...@@ -226,7 +222,7 @@ class ITradeStateProcess(Interface): ...@@ -226,7 +222,7 @@ class ITradeStateProcess(Interface):
def getSuccessorTradeStateList(explanation, trade_state): def getSuccessorTradeStateList(explanation, trade_state):
"""Returns the list of successor states in the """Returns the list of successor states in the
context of given explanation. This list is built by looking context of given explanation. This list is built by looking
at all successor of business path involved in given explanation at all successor of business link involved in given explanation
and which predecessor is the given trade_phase. and which predecessor is the given trade_phase.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -238,7 +234,7 @@ class ITradeStateProcess(Interface): ...@@ -238,7 +234,7 @@ class ITradeStateProcess(Interface):
def getPredecessorTradeStateList(explanation, trade_state): def getPredecessorTradeStateList(explanation, trade_state):
"""Returns the list of predecessor states in the """Returns the list of predecessor states in the
context of given explanation. This list is built by looking context of given explanation. This list is built by looking
at all predecessor of business path involved in given explanation at all predecessor of business link involved in given explanation
and which sucessor is the given trade_phase. and which sucessor is the given trade_phase.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -303,20 +299,6 @@ class ITradeStateProcess(Interface): ...@@ -303,20 +299,6 @@ class ITradeStateProcess(Interface):
trade_state -- a Trade State category trade_state -- a Trade State category
""" """
def getExpectedTradeStateCompletionDate(explanation, trade_state,
delay_mode=None):
"""Returns the date at which the give trade state is expected
to be completed in the context of given explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_state -- a Trade State category
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
class ITradePhaseProcess(Interface): class ITradePhaseProcess(Interface):
"""Trade Phase Process interface specification """Trade Phase Process interface specification
...@@ -324,12 +306,12 @@ class ITradePhaseProcess(Interface): ...@@ -324,12 +306,12 @@ class ITradePhaseProcess(Interface):
to Trade Phase completion status and expected completion dates. to Trade Phase completion status and expected completion dates.
Unlike ITradeStateProcess, ITradePhaseProcess APIs related to completion Unlike ITradeStateProcess, ITradePhaseProcess APIs related to completion
do not take into account relations between trade states and do not take into account relations between trade states and
business path. business link.
For example, a completed trade phase is a trade phase for which all For example, a completed trade phase is a trade phase for which all
business path applicable to the given explanation are completed. business link applicable to the given explanation are completed.
It does not matter whether the predecessor trade state of related It does not matter whether the predecessor trade state of related
business path is completed or not. business link is completed or not.
""" """
def getTradePhaseList(): def getTradePhaseList():
...@@ -354,7 +336,7 @@ class ITradePhaseProcess(Interface): ...@@ -354,7 +336,7 @@ class ITradePhaseProcess(Interface):
""" """
def isTradePhaseCompleted(explanation, trade_phase): def isTradePhaseCompleted(explanation, trade_phase):
"""Returns True all business path with given trade_phase """Returns True all business link with given trade_phase
applicable to given explanation are completed. applicable to given explanation are completed.
explanation -- an Order, Order Line, Delivery or Delivery Line or explanation -- an Order, Order Line, Delivery or Delivery Line or
...@@ -364,7 +346,7 @@ class ITradePhaseProcess(Interface): ...@@ -364,7 +346,7 @@ class ITradePhaseProcess(Interface):
""" """
def isTradePhasePartiallyCompleted(explanation, trade_phase): def isTradePhasePartiallyCompleted(explanation, trade_phase):
"""Returns True at least one business path with given trade_phase """Returns True at least one business link with given trade_phase
applicable to given explanation is partially completed applicable to given explanation is partially completed
or completed. or completed.
...@@ -374,27 +356,11 @@ class ITradePhaseProcess(Interface): ...@@ -374,27 +356,11 @@ class ITradePhaseProcess(Interface):
trade_phase -- a Trade Phase category trade_phase -- a Trade Phase category
""" """
def getExpectedTradePhaseCompletionDate(explanation, trade_phase,
delay_mode=None):
"""Returns the date at which the give trade phase is expected
to be completed in the context of given explanation, taking
into account the graph of date constraints defined by business path
and business states.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_phase -- a Trade Phase category
delay_mode -- optional value to specify calculation mode ('min', 'max')
if no value specified use average delay
"""
def getRemainingTradePhaseList(business_link, trade_phase_list=None): def getRemainingTradePhaseList(business_link, trade_phase_list=None):
"""Returns the list of remaining trade phases which to be achieved """Returns the list of remaining trade phases which to be achieved
as part of a business process. This list is calculated by analysing as part of a business process. This list is calculated by analysing
the graph of business path and trade states, starting from a given the graph of business link and trade states, starting from a given
business path. The result if filtered by a list of trade phases. This business link. The result if filtered by a list of trade phases. This
method is useful mostly for production and MRP to manage a distributed method is useful mostly for production and MRP to manage a distributed
supply and production chain. supply and production chain.
...@@ -428,8 +394,63 @@ class ITradePhaseProcess(Interface): ...@@ -428,8 +394,63 @@ class ITradePhaseProcess(Interface):
if no value specified use average delay if no value specified use average delay
""" """
class ISimulationMovementProcess(Interface):
"""Simulation Movemnt Process interface specification
ISimulationMovementProcess provides help methods to
access simulation movements of an explanation and
gather statistics about them. It is useful to find
out min dates or max dates related to a business link,
to a trade phase, to a trade model path, to a
trade_model_line, etc.
"""
def getSimulationMovementList(explanation, trade_phase=None,
business_link=None, trade_model_path=None, trade_model_line=None, **kw):
"""Returns a list of movement part of the simulation subtrees
defined by explanation and which match provided parameters. This
method can be useful for example to list all simulation movements
related to a phase such as payment, and inspect them.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_phase -- optional Trade Phase category
business_link -- optional Business Link document
trade_model_path -- optional Trade Model Path document
trade_model_line --optional Trade Model Line document
**kw -- other optional parameters which are passed to Catalog API
"""
def getSimulationMovementStat(explanation, trade_phase=None,
business_link=None, trade_model_path=None, trade_model_line=None, **kw):
"""Returns statistics for movements part of the simulation subtrees
defined by explanation and which match provided parameters. This
method can be useful for example to find the max date of simulation movements
related to a phase such as payment.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
trade_phase -- optional Trade Phase category
business_link -- optional Business Link document
trade_model_path -- optional Trade Model Path document
trade_model_line --optional Trade Model Line document
**kw -- other optional parameters which are passed to Catalog API
"""
class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableBusinessLinkProcess, class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableBusinessLinkProcess,
ITradeStateProcess, ITradePhaseProcess, ): ITradeStateProcess, ITradePhaseProcess, ISimulationMovementProcess):
"""Business Process interface specification. """Business Process interface specification.
Business Process APIs are used to manage the completion status, Business Process APIs are used to manage the completion status,
...@@ -461,14 +482,6 @@ class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableB ...@@ -461,14 +482,6 @@ class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableB
Applied Rule which implicitely defines a simulation subtree Applied Rule which implicitely defines a simulation subtree
""" """
def getExpectedCompletionDate(explanation, delay_mode=None):
"""Returns the expected date at which all applicable Trade States and
Trade Phases are completed in the context of given explanation.
explanation -- an Order, Order Line, Delivery or Delivery Line or
Applied Rule which implicitely defines a simulation subtree
"""
def build(explanation, include_partially_buildable=False): def build(explanation, include_partially_buildable=False):
"""Build whatever is buildable in the context of given explanation. """Build whatever is buildable in the context of given explanation.
...@@ -476,8 +489,6 @@ class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableB ...@@ -476,8 +489,6 @@ class IBusinessProcess(ITradeModelPathProcess, IBusinessLinkProcess, IBuildableB
Applied Rule which implicitely defines a simulation subtree Applied Rule which implicitely defines a simulation subtree
include_partially_buildable -- if set to True, also build partially include_partially_buildable -- if set to True, also build partially
buildable business path. Else buildable business link. Else
only build strictly buildable path. only build strictly buildable link.
""" """
\ No newline at end of file
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