Commit 58429e1a authored by Łukasz Nowak's avatar Łukasz Nowak

- Business Process Modelling documents


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@26653 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 7426a824
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@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.
#
##############################################################################
from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo
from Products.CMFCore.PortalFolder import ContentFilter
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5.Document.Path import Path
import zope.interface
class BusinessPath(Path):
"""
The BusinessPath class embeds all information related to
lead times and parties involved at a give phase of a business
process.
BusinessPath are also used as helper to build buildable movements.
Here is the typical code of an alarm:
Approach 1: explanation per explanation
builder = portal_deliveries.default_order_builder
for path in builder.getSpecialiseRelatedValueList() # or wharever category
for explanation in portal_catalog(buildable=1, portal_type='Order'):
path.build(explanation)
Pros: easy explanation based approach
Cons: buildable column added in delivery table
reexpand of a finished order might generate remaining buildable
Approach 2: isBuildable is indexed for SimulationMovements
isBuildable() method is added to SimulationMovement
Pros: global select is possible
Cons: reindex of simulation is required
slow indexing
Approach 3: isBuildable is invoked during build "after select" process
builder = portal_deliveries.default_order_builder
for path in builder.getSpecialiseRelatedValueList() # or wharever category
builder.build(causality_uid=path.getUid(),) # Select movemenents
Pros: global select is possible
Cons: global select retrieves long lists
slow build
Method 3 is best
"""
meta_type = 'ERP5 Business Path'
portal_type = 'Business Path'
isPredicate = 1
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Folder
, PropertySheet.Comment
, PropertySheet.Arrow
, PropertySheet.Chain
, PropertySheet.BusinessPath
)
# Declarative interfaces
zope.interface.implements(Interface.ICategoryAccessProvider,
Interface.IArrow)
# IBusinessPath Interface
security.declareProtected(Permissions.AccessContentsInformation, 'getSourceBaseCategoryList')
def getSourceBaseCategoryList(self):
"""
Returns all categories which are used to define the source
of this Arrow
"""
# Naive implementation - we must use category groups instead
return ('source', 'source_section', 'source_payment', 'source_project', )
security.declareProtected(Permissions.AccessContentsInformation, 'getDestinationBaseCategoryList')
def getDestinationBaseCategoryList(self):
"""
Returns all categories which are used to define the destination
of this Arrow
"""
# Naive implementation - we must use category groups instead
return ('destination', 'destination_section', 'destination_payment', 'destination_project', )
# ICategoryAccessProvider overriden methods
def _getCategoryMembershipList(self, category, **kw):
"""
Overriden in order to take into account dynamic arrow
categories
"""
context = kw.get('context')
result = Path._getCategoryMembershipList(self, category, **kw)
if context is not None:
dynamic_category_list = self._getDynamicCategoryList(context)
dynamic_category_list= self._filterCategoryList(dynamic_category_list, category, **kw)
# TODO: static categories should have priority over dynamic categories
result = dynamic_category_list + result
return result
def _getAcquiredCategoryMembershipList(self, category, **kw):
"""
Overriden in order to take into account dynamic arrow
categories
"""
context = kw.get('context')
result = Path._getAcquiredCategoryMembershipList(self, category, **kw)
if context is not None:
dynamic_category_list = self._getDynamicCategoryList(context)
dynamic_category_list= self._filterCategoryList(dynamic_category_list, category, **kw)
# TODO: static categories should have priority over dynamic categories
result = dynamic_category_list + result
return result
def _filterCategoryList(self, category_list, category, spec=(), filter=None, portal_type=(), base=0,
keep_default=1, checked_permission=None):
"""
XXX - implementation missing
TBD - look at CategoryTool._buildFilter for inspiration
"""
return category_list
# Dynamic context based categories
def _getDynamicCategoryList(self, context):
return self._getDynamicSourceCategoryList(context) \
+ self._getDynamicDestinationCategoryList(context)
def _getDynamicSourceCategoryList(self, context):
method_id = self.getSourceMethodId()
if method_id:
method = getattr(self, method_id)
return method(context)
return ()
def _getDynamicDestinationCategoryList(self, context):
method_id = self.getDestinationMethodId()
if method_id:
method = getattr(self, method_id)
return method(context)
return ()
# Core API
def isBuildable(self, explanation):
"""
"""
if self.isCompleted(explanation):
return False # No need to build what was already built
if self.isFrozen(explanation):
return False # No need to build what is frozen
predecessor = self.getPredecessorValue()
if predecessor is None:
return True # No predecessor, let's build
if predecessor.isCompleted(explanation):
return True
return False
def isPartiallyBuildable(self, explanation):
"""
Not sure if this will exist some day XXX
"""
def _getRelatedSimulationMovementList(explanation):
"""
"""
return self.getCausalityRelatedValueList(portal_type='Simulation Movement',
explanation_uid=explanation.getUid())
def isCompleted(self, explanation):
"""
Looks at all simulation related movements
and checks the simulation_state of the delivery
"""
acceptable_state_list = self.getCompletedStateIdList() # XXX Missing method / name
for movement in self._getRelatedSimulationMovementList(explanation):
if movement.getSimulationState() not in acceptable_state_list:
return False
return True
def isPartiallyCompleted(self, explanation):
"""
Looks at all simulation related movements
and checks the simulation_state of the delivery
"""
acceptable_state_list = self.getCompletedStateList() # XXX Missing method / name
for movement in self._getRelatedSimulationMovementList(explanation):
if movement.getSimulationState() in acceptable_state_list:
return True
return False
def isFrozen(self, explanation):
"""
Looks at all simulation related movements
and checks if frozen
"""
movement_list = self._getRelatedSimulationMovementList(explanation)
if len(movement_list) == 0:
return False # Nothing to be considered as Frozen
for movement in movement_list:
if not movement.isFrozen():
return False
return True
def build(self, explanation):
"""
Build
"""
builder_list = self.getBuilderList() # Missing method
for builder in builder_list:
builder.build(causality_uid=self.getUid()) # This is one way of doing
builder.build(movement_relative_url_list=
self._getRelatedSimulationMovementList(explanation)) # Another way
# Date calculation
def getExpectedStartDate(self, explanation, predecessor_date=None):
"""
Returns the expected start date for this
path based on the explanation.
predecessor_date -- if provided, computes the date base on the
date value provided
"""
if predecessor_date is None:
predecessor_date = self.getPredecessorValue().getExpectedCompletionDate()
return predecessor_date + wait_time
def getExpectedStopDate(self, explanation, predecessor_date=None):
"""
Returns the expected stop date for this
path based on the explanation.
predecessor_date -- if provided, computes the date base on the
date value provided
"""
return context.getExpectedStartDate(explanation, predecessor_date=predecessor_date) + lead_time
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@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.
#
##############################################################################
from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5.Document.Path import Path
class BusinessProcess(Path, XMLObject):
"""
The BusinessProcess class is a container class which is used
to describe business processes in the area of trade, payroll
and production.
"""
meta_type = 'ERP5 Business Process'
portal_type = 'Business Process'
isPredicate = 1
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Folder
, PropertySheet.Comment
, PropertySheet.Arrow
)
# Access to path and states of the business process
security.declareProtected(Permissions.AccessContentsInformation, 'getPathValueList')
def getPathValueList(self, trade_phase, context=None, **kw):
"""
Returns all Path of the current BusinessProcess which
are matching the given trade_phase and the optional context.
trade_phase -- a single trade phase category or a list of
trade phases
context -- the context to search matching predicates for
**kw -- same parameters as for searchValues / contentValues
"""
# Naive implementation to redo XXX using contentValues
if type(trade_phase) is type(''): # XXX - bad, use isinstance
trade_phase = (trade_phase,)
result = []
for document in self.contentValues(portal_type="Business Path"):
for phase in trade_phase:
if document.isMemberOf('trade_phase/' + phase): # XXX - not so good, use filter if possible
result.append(document)
break
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getStateValueList')
def getStateValueList(self, *args, **kw):
"""
Returns all states of the business process. The method
**kw parameters follows the API of searchValues / contentValues
"""
# Naive implementation to redo XXX
kw['portal_type'] = "Business State"
return self.contentValues(*args, **kw)
# Access to path and states of the business process
def isCompleted(self, explanation):
"""
True if all states are completed
"""
for state in self.getStateValueList():
if not state.isCompleted(explanation):
return False
return True
def isBuildable(self, explanation):
"""
True if all any path is buildable
"""
return len(self.getBuildablePathValueList(explanation)) != 0
def getBuildablePathValueList(self, explanation):
"""
Returns the list of Business Path which are ready to
be built
"""
return filter(lambda x:x.isBuildable(explanation), self.getPathValueList())
def getCompletedStateValueList(self, explanation):
"""
Returns the list of Business States which are finished
"""
return filter(lambda x:x.isCompleted(explanation), self.getStateValueList())
def getPartiallyCompletedStateValueList(self, explanation):
"""
Returns the list of Business States which are finished
"""
return filter(lambda x:x.isPartiallyCompleted(explanation), self.getStateValueList())
def getLatestCompletedStateValue(self, explanation):
"""
Returns the most advanced completed state
"""
for state in self.getCompletedStateValueList(explanation):
for path in state.getPredecessorRelatedValueList():
if not path.isCompleted(explanation):
return state
return None
def getLatestPartiallyCompletedStateValue(self, explanation):
"""
Returns the most advanced completed state
"""
for state in self.getCompletedStateValueList(explanation):
for path in state.getPredecessorRelatedValueList():
if not path.isPartiallyCompleted(explanation):
return state
return None
def getLatestCompletedStateValueList(self, explanation):
"""
Returns the most advanced completed state
"""
result = []
for state in self.getCompletedStateValueList(explanation):
for path in state.getPredecessorRelatedValueList():
if not path.isCompleted(explanation):
result.append(state)
return result
def getLatestPartiallyCompletedStateValueList(self, explanation):
"""
Returns the most advanced completed state
"""
result = []
for state in self.getCompletedStateValueList(explanation):
for path in state.getPredecessorRelatedValueList():
if not path.isPartiallyCompleted(explanation):
result.append(state)
return result
def build(self, explanation):
"""
Build whatever is buildable
"""
for path in self.getBuildablePathValueList(explanation):
path.build(explanation)
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved.
# Jean-Paul Smets-Solanes <jp@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.
#
##############################################################################
from Globals import InitializeClass, PersistentMapping
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint, Interface
from Products.ERP5Type.XMLObject import XMLObject
class BusinessState(XMLObject):
"""
The BusinessProcess class is a container class which is used
to describe business processes in the area of trade, payroll
and production.
"""
meta_type = 'ERP5 Business State'
portal_type = 'Business State'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.CategoryCore
, PropertySheet.DublinCore
, PropertySheet.Folder
, PropertySheet.Comment
)
# Core API
def isCompleted(self, explanation):
"""
If all path which reach this state are completed
then this state is completed
"""
for path in self.getSuccessorRelatedValueList():
if not path.isCompleted(explanation):
return False
return True
def isPartiallyCompleted(self, explanation):
"""
If all path which reach this state are partially completed
then this state is completed
"""
for path in self.getSuccessorRelatedValueList():
if not path.isPartiallyCompleted(explanation):
return False
return True
# Duration calculation
def getExpectedCompletionDate(self, explanation):
"""
Returns the expected completion date for this
state based on the explanation.
"""
def getExpectedCompletionDuration(self, explanation):
"""
Returns the expected completion duration for this
state.
"""
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