Commit 4c8bc987 authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: erp5_pdm: Migrate Documents and Unit Test from filesystem.

parent d32a1124
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.Variation import Variation from erp5.component.document.Variation import Variation
from erp5.component.document.Image import Image from erp5.component.document.Image import Image
class VariationImage(Image, Variation): class VariationImage(Image, Variation):
......
...@@ -28,19 +28,16 @@ ...@@ -28,19 +28,16 @@
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, Constraint from Products.ERP5Type import Permissions, Constraint
from Products.ERP5Type.XMLMatrix import XMLMatrix from Products.ERP5Type.XMLMatrix import XMLMatrix
from Products.ERP5Type.Utils import cartesianProduct from Products.ERP5Type.Utils import cartesianProduct
from Products.ERP5.Document.AmountGeneratorLine import AmountGeneratorLine from Products.ERP5.Document.AmountGeneratorLine import AmountGeneratorLine
from Products.ERP5.Document.TransformedResource import TransformedResource from erp5.component.document.TransformedResource import TransformedResource
from Products.ERP5Type.Base import TempBase
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
import operator
class AssortedResource(TransformedResource): class AssortedResource(TransformedResource):
""" """
This code was copied from TransformedResource very stupidly. This code was copied from TransformedResource very stupidly.
Therefore it is necessaery to review all the code. -yo Therefore it is necessaery to review all the code. -yo
...@@ -105,18 +102,18 @@ class AssortedResource(TransformedResource): ...@@ -105,18 +102,18 @@ class AssortedResource(TransformedResource):
setValueUids could be overriden to provide quick and dirty setValueUids could be overriden to provide quick and dirty
behaviour of range update behaviour of range update
""" """
meta_type = 'ERP5 Assorted Resource' meta_type = 'ERP5 Assorted Resource'
portal_type = 'Assorted Resource' portal_type = 'Assorted Resource'
add_permission = Permissions.AddPortalContent add_permission = Permissions.AddPortalContent
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
# Local property sheet # Local property sheet
_properties = ( _properties = (
{ 'id' : 'variation_base_category', { 'id' : 'variation_base_category',
'storage_id' : 'variation_base_category_list', # Coramy Compatibility 'storage_id' : 'variation_base_category_list', # Coramy Compatibility
'description' : "", 'description' : "",
...@@ -128,373 +125,375 @@ class AssortedResource(TransformedResource): ...@@ -128,373 +125,375 @@ class AssortedResource(TransformedResource):
'acquisition_accessor_id' : 'getVariationBaseCategoryList', ### XXX BUG 'acquisition_accessor_id' : 'getVariationBaseCategoryList', ### XXX BUG
'acquisition_depends' : None, 'acquisition_depends' : None,
'mode' : 'w' }, 'mode' : 'w' },
) )
getCellAggregateKey = AmountGeneratorLine.getCellAggregateKey getCellAggregateKey = AmountGeneratorLine.getCellAggregateKey
security.declareProtected(Permissions.AccessContentsInformation, 'getAssortedVariationCategoryList')
security.declareProtected(Permissions.AccessContentsInformation, 'getAssortedVariationCategoryList') def getAssortedVariationCategoryList(self, cell_index):
def getAssortedVariationCategoryList(self, cell_index): """
"""
Nice for A Nice for A
""" """
transformation = self.getParentValue() transformation = self.getParentValue()
transformation_category_list = transformation.getVariationCategoryList() transformation_category_list = transformation.getVariationCategoryList()
variation_category_list = [] variation_category_list = []
for p in cell_index: for p in cell_index:
if p is not None and p not in transformation_category_list: if p is not None and p not in transformation_category_list:
variation_category_list.append(p) variation_category_list.append(p)
#LOG('getAssortedVariationCategoryList', 0, repr(cell_index)) #LOG('getAssortedVariationCategoryList', 0, repr(cell_index))
#LOG('getAssortedVariationCategoryList', 0, repr(variation_category_list)) #LOG('getAssortedVariationCategoryList', 0, repr(variation_category_list))
return variation_category_list return variation_category_list
security.declareProtected(Permissions.AccessContentsInformation, 'getAssortedVariationBaseCategoryList') security.declareProtected(Permissions.AccessContentsInformation, 'getAssortedVariationBaseCategoryList')
def getAssortedVariationBaseCategoryList(self): def getAssortedVariationBaseCategoryList(self):
""" """
Nice for A Nice for A
""" """
return self.getQVariationBaseCategoryList() return self.getQVariationBaseCategoryList()
security.declareProtected(Permissions.AccessContentsInformation, 'getAssortmentVariationCategoryList') security.declareProtected(Permissions.AccessContentsInformation, 'getAssortmentVariationCategoryList')
def getAssortmentVariationCategoryList(self, cell_index): def getAssortmentVariationCategoryList(self, cell_index):
""" """
Nice for C Nice for C
""" """
transformation = self.getParentValue() transformation = self.getParentValue()
transformation_category_list = transformation.getVariationCategoryList() transformation_category_list = transformation.getVariationCategoryList()
variation_category_list = [] variation_category_list = []
for p in cell_index: for p in cell_index:
if p is not None and p in transformation_category_list: if p is not None and p in transformation_category_list:
variation_category_list.append(p) variation_category_list.append(p)
#LOG('getAssortmentVariationCategoryList', 0, repr(cell_index)) #LOG('getAssortmentVariationCategoryList', 0, repr(cell_index))
#LOG('getAssortmentVariationCategoryList', 0, repr(variation_category_list)) #LOG('getAssortmentVariationCategoryList', 0, repr(variation_category_list))
return variation_category_list return variation_category_list
security.declareProtected(Permissions.AccessContentsInformation, 'getAssortmentVariationBaseCategoryList') security.declareProtected(Permissions.AccessContentsInformation, 'getAssortmentVariationBaseCategoryList')
def getAssortmentVariationBaseCategoryList(self): def getAssortmentVariationBaseCategoryList(self):
""" """
Nice for C Nice for C
""" """
#LOG('getAssortmentVariationBaseCategoryList', 0, repr(self)) #LOG('getAssortmentVariationBaseCategoryList', 0, repr(self))
transformation = self.getParentValue() transformation = self.getParentValue()
return transformation.getVariationBaseCategoryList() return transformation.getVariationBaseCategoryList()
# XXX Should be moved to somewhere more global, as this is general. # XXX Should be moved to somewhere more global, as this is general.
# This does not depend even on self. # This does not depend even on self.
# #
# Return a sorted list of base categories. This makes views of matrices consistent. # Return a sorted list of base categories. This makes views of matrices consistent.
# Use the global variables, 'column_base_category_list' and 'line_base_category_list' # Use the global variables, 'column_base_category_list' and 'line_base_category_list'
# (but not 'tab_base_category_list' at the moment). # (but not 'tab_base_category_list' at the moment).
def _getSortedBaseCategoryList(self, base_category_list): def _getSortedBaseCategoryList(self, base_category_list):
base_category_list = base_category_list[:] # Work on a copy. base_category_list = base_category_list[:] # Work on a copy.
base_category_list.sort() base_category_list.sort()
column = None column = None
line = None line = None
sorted_list = [None, None] sorted_list = [None, None]
for category in self.getPortalColumnBaseCategoryList(): for category in self.getPortalColumnBaseCategoryList():
if category in base_category_list: if category in base_category_list:
if column is None: if column is None:
column = category column = category
else:
sorted_list.append(category)
base_category_list.remove(category)
for category in self.getPortalLineBaseCategoryList():
if category in base_category_list:
if line is None:
line = category
else:
sorted_list.append(category)
base_category_list.remove(category)
sorted_list.extend(base_category_list)
sorted_list[0] = line
sorted_list[1] = column
return sorted_list
# Update the range of cells according to the currently selected base categories.
def _updateCellRange(self, base=1, current_category=None):
transformation = self.getParentValue()
kwd = {'base_id': 'quantity'}
kw = []
base_category_list = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())
for base_category in base_category_list:
# FIXME: Actually, getVariationRangeCategoryList should be used here.
# But getVariationRangeCategoryList is inconsistent with
# getVariationRangeCategoryItemList, because getVariationRangeCategoryItemList
# is overrided in Amount and Resource. -yo
if base_category is None:
category_item_list = [(None,'')]
else: else:
category_item_list = self.getVariationRangeCategoryItemList(base_category_list = [base_category], sorted_list.append(category)
base=1) base_category_list.remove(category)
category_list = [] for category in self.getPortalLineBaseCategoryList():
for item in category_item_list: if category in base_category_list:
category_list.append(item[0]) if line is None:
kw.append(category_list) line = category
kw.append(transformation.getVariationCategoryList()) else:
#LOG('_updateCellRange', 20, str(kw)) sorted_list.append(category)
self.setCellRange(*kw, **kwd) base_category_list.remove(category)
sorted_list.extend(base_category_list)
### Variation matrix definition sorted_list[0] = line
# sorted_list[1] = column
security.declareProtected(Permissions.ModifyPortalContent, '_setQVariationBaseCategoryList') return sorted_list
def _setQVariationBaseCategoryList(self, value):
""" # Update the range of cells according to the currently selected base categories.
def _updateCellRange(self, base=1, current_category=None, *args, **kw):
transformation = self.getParentValue()
kwd = {'base_id': 'quantity'}
kw = []
base_category_list = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())
for base_category in base_category_list:
# FIXME: Actually, getVariationRangeCategoryList should be used here.
# But getVariationRangeCategoryList is inconsistent with
# getVariationRangeCategoryItemList, because getVariationRangeCategoryItemList
# is overrided in Amount and Resource. -yo
if base_category is None:
category_item_list = [(None,'')]
else:
category_item_list = self.getVariationRangeCategoryItemList(base_category_list = [base_category],
base=1)
category_list = []
for item in category_item_list:
category_list.append(item[0])
kw.append(category_list)
kw.append(transformation.getVariationCategoryList())
#LOG('_updateCellRange', 20, str(kw))
self.setCellRange(*kw, **kwd)
### Variation matrix definition
#
security.declareProtected(Permissions.ModifyPortalContent, '_setQVariationBaseCategoryList')
def _setQVariationBaseCategoryList(self, value):
"""
Defines the possible base categories which Quantity value (Q) Defines the possible base categories which Quantity value (Q)
variate on variate on
""" """
self._baseSetQVariationBaseCategoryList(value) self._baseSetQVariationBaseCategoryList(value)
self._updateCellRange() self._updateCellRange()
# And fix it in case the cells are not renamed (XXX this will be removed in the future) # And fix it in case the cells are not renamed (XXX this will be removed in the future)
self._checkConsistency(fixit=1) self._checkConsistency(fixit=1)
security.declareProtected(Permissions.ModifyPortalContent, 'setQVariationBaseCategoryList') security.declareProtected(Permissions.ModifyPortalContent, 'setQVariationBaseCategoryList')
def setQVariationBaseCategoryList(self, value): def setQVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Quantity value (Q) Defines the possible base categories which Quantity value (Q)
variate on and reindex the object variate on and reindex the object
""" """
self._setQVariationBaseCategoryList(value) self._setQVariationBaseCategoryList(value)
self.reindexObject() self.reindexObject()
security.declareProtected(Permissions.ModifyPortalContent, '_setVVariationBaseCategoryList') security.declareProtected(Permissions.ModifyPortalContent, '_setVVariationBaseCategoryList')
def _setVVariationBaseCategoryList(self, value): def _setVVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Variation value (V) Defines the possible base categories which Variation value (V)
variate on variate on
""" """
self._baseSetVVariationBaseCategoryList(value) self._baseSetVVariationBaseCategoryList(value)
kwd = {} kwd = {}
kwd['base_id'] = 'variation' kwd['base_id'] = 'variation'
kw = [] kw = []
transformation = self.getParentValue() transformation = self.getParentValue()
line_id = transformation.getVariationBaseCategoryLine() line_id = transformation.getVariationBaseCategoryLine()
column_id = transformation.getVariationBaseCategoryColumn() column_id = transformation.getVariationBaseCategoryColumn()
line = [[None]] line = [[None]]
column = [[None]] column = [[None]]
for v in value: for v in value:
if v == line_id: if v == line_id:
line = [transformation.getCategoryMembershipList(v,base=1)] line = [transformation.getCategoryMembershipList(v,base=1)]
elif v == column_id: elif v == column_id:
column = [transformation.getCategoryMembershipList(v,base=1)] column = [transformation.getCategoryMembershipList(v,base=1)]
else: else:
kw += [transformation.getCategoryMembershipList(v,base=1)] kw += [transformation.getCategoryMembershipList(v,base=1)]
kw = line + column + kw kw = line + column + kw
self.setCellRange(*kw, **kwd) self.setCellRange(*kw, **kwd)
# Empty cells if no variation # Empty cells if no variation
if line == [[None]] and column == [[None]]: if line == [[None]] and column == [[None]]:
self.delCells(base_id='variation') self.delCells(base_id='variation')
# And fix it in case the cells are not renamed (XXX this will be removed in the future) # And fix it in case the cells are not renamed (XXX this will be removed in the future)
self._checkConsistency(fixit=1) self._checkConsistency(fixit=1)
security.declareProtected(Permissions.ModifyPortalContent, 'setVVariationBaseCategoryList') security.declareProtected(Permissions.ModifyPortalContent, 'setVVariationBaseCategoryList')
def setVVariationBaseCategoryList(self, value): def setVVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Variation value (V) Defines the possible base categories which Variation value (V)
variate on and reindex the object variate on and reindex the object
""" """
self._setVVariationBaseCategoryList(value) self._setVVariationBaseCategoryList(value)
self.reindexObject() self.reindexObject()
# Methods for matrix UI widgets
security.declareProtected(Permissions.AccessContentsInformation, 'getQLineItemList')
def getQLineItemList(self, display_id='getTitle', base=1, current_category=None):
"""
"""
line_category = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[0]
#LOG('getQLineItemList', 0, "%s" % str(line_category))
if line_category is None:
result = [(None,'')]
else:
result = self.getVariationRangeCategoryItemList(base_category_list = [line_category],
display_id=display_id,
base=base,
current_category=current_category)
#LOG('getQLineItemList', 10, "%s" % str(result))
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getQColumnItemList')
def getQColumnItemList(self, display_id='getTitle', base=1, current_category=None):
"""
"""
column_category = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[1]
#LOG('getQColumnItemList', 0, "%s" % str(column_category))
if column_category is None:
result = [(None,'')]
else:
result = self.getVariationRangeCategoryItemList(base_category_list = [column_category],
display_id=display_id,
base=base,
current_category=current_category)
#LOG('getQColumnItemList', 0, "%s" % str(result))
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getQTabItemList') # Methods for matrix UI widgets
def getQTabItemList(self, display_id='getTitle', base=1, current_category=None): security.declareProtected(Permissions.AccessContentsInformation, 'getQLineItemList')
""" def getQLineItemList(self, display_id='getTitle', base=1, current_category=None):
"""
"""
line_category = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[0]
#LOG('getQLineItemList', 0, "%s" % str(line_category))
if line_category is None:
result = [(None,'')]
else:
result = self.getVariationRangeCategoryItemList(base_category_list = [line_category],
display_id=display_id,
base=base,
current_category=current_category)
#LOG('getQLineItemList', 10, "%s" % str(result))
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getQColumnItemList')
def getQColumnItemList(self, display_id='getTitle', base=1, current_category=None):
"""
"""
column_category = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[1]
#LOG('getQColumnItemList', 0, "%s" % str(column_category))
if column_category is None:
result = [(None,'')]
else:
result = self.getVariationRangeCategoryItemList(base_category_list = [column_category],
display_id=display_id,
base=base,
current_category=current_category)
#LOG('getQColumnItemList', 0, "%s" % str(result))
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getQTabItemList')
def getQTabItemList(self, display_id='getTitle', base=1, current_category=None):
"""
Returns a list of items which can be used as index for Returns a list of items which can be used as index for
each tab of a matrix or to define a cell range. each tab of a matrix or to define a cell range.
""" """
tab_category_list = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[2:] tab_category_list = self._getSortedBaseCategoryList(self.getQVariationBaseCategoryList())[2:]
tab_category_item_list_list = [] tab_category_item_list_list = []
for tab_category in tab_category_list: for tab_category in tab_category_list:
tab_category_item_list = self.getVariationRangeCategoryItemList(base_category_list = [tab_category], tab_category_item_list = self.getVariationRangeCategoryItemList(base_category_list = [tab_category],
display_id=display_id, display_id=display_id,
base=base, base=base,
current_category=current_category) current_category=current_category)
tab_category_item_list_list.append(tab_category_item_list) tab_category_item_list_list.append(tab_category_item_list)
transformation = self.getParentValue() transformation = self.getParentValue()
transformation_category_item_list = transformation.getVariationCategoryItemList( transformation_category_item_list = transformation.getVariationCategoryItemList(
display_id=display_id, display_id=display_id,
base=base, base=base,
current_category=current_category) current_category=current_category)
tab_category_item_list_list.append(transformation_category_item_list) tab_category_item_list_list.append(transformation_category_item_list)
if len(tab_category_item_list_list) > 0: if len(tab_category_item_list_list) > 0:
product_list = cartesianProduct(tab_category_item_list_list) product_list = cartesianProduct(tab_category_item_list_list)
result = []
for item_list in product_list:
value_list = []
label_list = []
for item in item_list:
value_list.append(item[0])
label_list.append(item[1])
result.append((value_list, label_list))
else:
result = [(None,'')]
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getVLineItemList')
def getVLineItemList(self):
base_category = self.getParentValue().getVariationBaseCategoryLine()
if base_category in self.getVVariationBaseCategoryList():
clist = self.getParentValue().getCategoryMembershipList(base_category, base=1)
else:
clist = [None]
result = [] result = []
for c in clist: for item_list in product_list:
result += [(c,c)] value_list = []
return result label_list = []
for item in item_list:
security.declareProtected(Permissions.AccessContentsInformation, 'getVColumnItemList') value_list.append(item[0])
def getVColumnItemList(self): label_list.append(item[1])
base_category = self.getParentValue().getVariationBaseCategoryColumn() result.append((value_list, label_list))
if base_category in self.getVVariationBaseCategoryList(): else:
clist = self.getParentValue().getCategoryMembershipList(base_category, base=1) result = [(None,'')]
else: return result
clist = [None]
security.declareProtected(Permissions.AccessContentsInformation, 'getVLineItemList')
def getVLineItemList(self):
base_category = self.getParentValue().getVariationBaseCategoryLine()
if base_category in self.getVVariationBaseCategoryList():
clist = self.getParentValue().getCategoryMembershipList(base_category, base=1)
else:
clist = [None]
result = []
for c in clist:
result += [(c,c)]
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getVColumnItemList')
def getVColumnItemList(self):
base_category = self.getParentValue().getVariationBaseCategoryColumn()
if base_category in self.getVVariationBaseCategoryList():
clist = self.getParentValue().getCategoryMembershipList(base_category, base=1)
else:
clist = [None]
result = []
for c in clist:
result += [(c,c)]
result.sort() # XXX Temp until set / list issue solved
return result
security.declareProtected(Permissions.AccessContentsInformation, 'getVTabItemList')
def getVTabItemList(self):
transformation = self.getParentValue()
line_id = transformation.getVariationBaseCategoryLine()
column_id = transformation.getVariationBaseCategoryColumn()
base_category_list = transformation.getVariationBaseCategoryList()
base_category = []
for c in base_category_list:
if not c in (line_id, column_id):
if c in self.getVVariationBaseCategoryList():
base_category += [transformation.getCategoryMembershipList(c, base=1)]
if len(base_category) > 0:
clist = cartesianProduct(base_category)
result = [] result = []
for c in clist: for c in clist:
result += [(c,c)] result += [(c,c)]
else:
result = [(None,'')]
result.sort() # XXX Temp until set / list issue solved
return result
security.declareProtected( Permissions.ModifyPortalContent, 'newCell' )
def newCell(self, *kw, **kwd):
result = XMLMatrix.newCell(self, *kw, **kwd)
result._setPredicateOperator("SUPERSET_OF")
membership_list = []
for c in kw:
if c is not None:
membership_list += [c]
result._setPredicateValueList(membership_list)
base_id = kwd.get('base_id', 'cell')
if base_id == 'quantity':
result._setDomainBaseCategoryList(self.getQVariationBaseCategoryList())
elif base_id == 'variation':
result._setDomainBaseCategoryList(self.getVVariationBaseCategoryList())
return result
security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
def newCellContent(self, id, portal_type='Set Mapped Value', **kw): # pylint: disable=redefined-builtin
"""Overriden to specify default portal type
"""
return self.newContent(id=id, portal_type=portal_type, **kw)
result.sort() # XXX Temp until set / list issue solved security.declarePrivate('_checkConsistency')
def _checkConsistency(self, fixit=0):
return result """
security.declareProtected(Permissions.AccessContentsInformation, 'getVTabItemList')
def getVTabItemList(self):
transformation = self.getParentValue()
line_id = transformation.getVariationBaseCategoryLine()
column_id = transformation.getVariationBaseCategoryColumn()
base_category_list = transformation.getVariationBaseCategoryList()
base_category = []
for c in base_category_list:
if not c in (line_id, column_id):
if c in self.getVVariationBaseCategoryList():
base_category += [transformation.getCategoryMembershipList(c, base=1)]
if len(base_category) > 0:
clist = cartesianProduct(base_category)
result = []
for c in clist:
result += [(c,c)]
else:
result = [(None,'')]
result.sort() # XXX Temp until set / list issue solved
return result
security.declareProtected( Permissions.ModifyPortalContent, 'newCell' )
def newCell(self, *kw, **kwd):
result = XMLMatrix.newCell(self, *kw, **kwd)
result._setPredicateOperator("SUPERSET_OF")
membership_list = []
for c in kw:
if c is not None:
membership_list += [c]
result._setPredicateValueList(membership_list)
base_id = kwd.get('base_id', 'cell')
if base_id == 'quantity':
result._setDomainBaseCategoryList(self.getQVariationBaseCategoryList())
elif base_id == 'variation':
result._setDomainBaseCategoryList(self.getVVariationBaseCategoryList())
return result
security.declareProtected( Permissions.ModifyPortalContent, 'newCellContent' )
def newCellContent(self, id, portal_type='Set Mapped Value', **kw):
"""Overriden to specify default portal type
"""
return self.newContent(id=id, portal_type=portal_type, **kw)
security.declarePrivate('_checkConsistency')
def _checkConsistency(self, fixit=0):
"""
Check the constitency of transformation elements Check the constitency of transformation elements
""" """
transformation = self.getParentValue() transformation = self.getParentValue()
transformation_category_list = transformation.getVariationCategoryList() transformation_category_list = transformation.getVariationCategoryList()
error_list = XMLMatrix._checkConsistency(self, fixit=fixit) error_list = XMLMatrix._checkConsistency(self, fixit=fixit)
# Quantity should be empty if no variation # Quantity should be empty if no variation
q_range = self.getCellRange(base_id = 'quantity') q_range = self.getCellRange(base_id = 'quantity')
if q_range is not None: if q_range is not None:
range_is_empty = 1 range_is_empty = 1
for q_list in q_range: for q_list in q_range:
if q_list is not None: if q_list is not None:
range_is_empty = 0 range_is_empty = 0
break break
if range_is_empty: if range_is_empty:
matrix_is_not_empty = 0 matrix_is_not_empty = 0
for k in self.getCellIds(base_id = 'quantity'): for k in self.getCellIds(base_id = 'quantity'):
if hasattr(self, k):matrix_is_not_empty = 1 if hasattr(self, k):matrix_is_not_empty = 1
if matrix_is_not_empty: if matrix_is_not_empty:
if fixit:
self.delCells(base_id = 'quantity')
error_message = "Variation cells for quantity should be empty (fixed)"
else:
error_message = "Variation cells for quantity should be empty"
error_list += [(self.getRelativeUrl(),
'TransformedResource inconsistency', 100, error_message)]
# First quantity
# We build an attribute equality and look at all cells
q_constraint = Constraint.AttributeEquality(
domain_base_category_list = self.getQVariationBaseCategoryList(),
predicate_operator = 'SUPERSET_OF',
mapped_value_property_list = ['quantity'] )
for kw in self.getCellKeys(base_id = 'quantity'):
kwd={'base_id': 'quantity'}
c = self.getCell(*kw, **kwd)
if c is not None:
predicate_value_list = []
categories_list = []
for p in kw:
if p is not None:
if p in transformation_category_list:
if p not in predicate_value_list:
predicate_value_list.append(p)
else:
if p not in categories_list:
categories_list.append(p)
q_constraint.edit(predicate_value_list = predicate_value_list,
categories_list = categories_list)
if fixit: if fixit:
error_list += q_constraint.fixConsistency(c) self.delCells(base_id = 'quantity')
error_message = "Variation cells for quantity should be empty (fixed)"
else: else:
error_list += q_constraint.checkConsistency(c) error_message = "Variation cells for quantity should be empty"
error_list += [(self.getRelativeUrl(),
return error_list 'TransformedResource inconsistency', 100, error_message)]
# First quantity
# We build an attribute equality and look at all cells
q_constraint = Constraint.AttributeEquality(
domain_base_category_list = self.getQVariationBaseCategoryList(),
predicate_operator = 'SUPERSET_OF',
mapped_value_property_list = ['quantity'] )
for kw in self.getCellKeys(base_id = 'quantity'):
kwd={'base_id': 'quantity'}
c = self.getCell(*kw, **kwd)
if c is not None:
predicate_value_list = []
categories_list = []
for p in kw:
if p is not None:
if p in transformation_category_list:
if p not in predicate_value_list:
predicate_value_list.append(p)
else:
if p not in categories_list:
categories_list.append(p)
q_constraint.edit(predicate_value_list = predicate_value_list,
categories_list = categories_list)
if fixit:
error_list += q_constraint.fixConsistency(c)
else:
error_list += q_constraint.checkConsistency(c)
return error_list
"""
if 0: # obsolete if 0: # obsolete
from Products.ERP5Type.Base import TempBase
import operator
def getAggregatedAmountList(self, REQUEST): def getAggregatedAmountList(self, REQUEST):
# First, we set initial values for quantity and variation # First, we set initial values for quantity and variation
# Currently, we only consider discrete variations # Currently, we only consider discrete variations
...@@ -726,3 +725,4 @@ class AssortedResource(TransformedResource): ...@@ -726,3 +725,4 @@ class AssortedResource(TransformedResource):
reduce(operator.add, total_variated_base_price_list, 0), \ reduce(operator.add, total_variated_base_price_list, 0), \
reduce(operator.add, total_variated_source_base_price_list, 0), \ reduce(operator.add, total_variated_source_base_price_list, 0), \
duration duration
"""
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>AssortedResource</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.AssortedResource</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.AssortedResource</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Measure</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.Measure</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.Measure</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>QuantityUnitConversionDefinition</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.QuantityUnitConversionDefinition</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.QuantityUnitConversionDefinition</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>QuantityUnitConversionGroup</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.QuantityUnitConversionGroup</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.QuantityUnitConversionGroup</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ResourceMeasuresConsistencyConstraint</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.ResourceMeasuresConsistencyConstraint</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.ResourceMeasuresConsistencyConstraint</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -30,7 +30,6 @@ ...@@ -30,7 +30,6 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# #
############################################################################## ##############################################################################
from zLOG import LOG, WARNING
from warnings import warn from warnings import warn
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -41,206 +40,203 @@ from Products.ERP5.Document.MappedValue import MappedValue ...@@ -41,206 +40,203 @@ from Products.ERP5.Document.MappedValue import MappedValue
from Products.ERP5.mixin.amount_generator import AmountGeneratorMixin from Products.ERP5.mixin.amount_generator import AmountGeneratorMixin
from Products.ERP5.mixin.variated import VariatedMixin from Products.ERP5.mixin.variated import VariatedMixin
from Products.ERP5.mixin.composition import CompositionMixin
from Products.ERP5Type.XMLObject import XMLObject
# XXX Give priority to VariatedMixin (over Amount) due to conflicting # XXX Give priority to VariatedMixin (over Amount) due to conflicting
# implementations of getVariationBaseCategoryList # implementations of getVariationBaseCategoryList
class Transformation(MappedValue, VariatedMixin, Amount, AmountGeneratorMixin): class Transformation(MappedValue, VariatedMixin, Amount, AmountGeneratorMixin):
"""
Build of material - contains a list of transformed resources
Use of default_resource... (to define the variation range,
to ...)
XXX Transformation works only for a maximum of 3 variation base category...
Matrixbox must be rewritten for a clean implementation of n base category
"""
meta_type = 'ERP5 Transformation'
portal_type = 'Transformation'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Comment
, PropertySheet.Version
#, PropertySheet.Resource
, PropertySheet.TransformedResource
, PropertySheet.Transformation
, PropertySheet.Order
, PropertySheet.Task
)
def getAggregatedAmountList(self, *args, **kw):
""" """
Build of material - contains a list of transformed resources
Use of default_resource... (to define the variation range,
to ...)
XXX Transformation works only for a maximum of 3 variation base category...
Matrixbox must be rewritten for a clean implementation of n base category
""" """
meta_type = 'ERP5 Transformation' getAggregatedAmountList = \
portal_type = 'Transformation' super(Transformation, self).getAggregatedAmountList
# Detect old use of getAggregatedAmountList
# Declarative security if 'context' in kw:
security = ClassSecurityInfo() context = kw.pop('context')
security.declareObjectProtected(Permissions.AccessContentsInformation) else:
if not args or isinstance(args[0], (list, tuple)):
# Declarative properties return getAggregatedAmountList(*args, **kw)
property_sheets = ( PropertySheet.Comment context, args = args[0], args[1:]
, PropertySheet.Version warn("The API of getAggregatedAmountList has changed:"
#, PropertySheet.Resource " it must be called on the context instead of passing"
, PropertySheet.TransformedResource " the context as first parameter", DeprecationWarning)
, PropertySheet.Transformation # XXX add a 'transformation_amount_generator' group type
, PropertySheet.Order kw['amount_generator_type_list'] = ('Transformation',
, PropertySheet.Task 'Transformed Resource',
) 'Transformation Operation',
'Assorted Resource')
def getAggregatedAmountList(self, *args, **kw): if context is not None:
""" context = (context,)
""" return getAggregatedAmountList(context, *args, **kw)
getAggregatedAmountList = \
super(Transformation, self).getAggregatedAmountList def getQuantity(self, default=None):
# Detect old use of getAggregatedAmountList # Used for amount generation
if 'context' in kw: # (Transformation is defined for 1 unit of target resource)
context = kw.pop('context') return 1.
else:
if not args or isinstance(args[0], (list, tuple)): # Predicate Value implementation
return getAggregatedAmountList(*args, **kw) # asPredicate takes into account the resource
context, args = args[0], args[1:] # XXX-JPS not Impl.
warn("The API of getAggregatedAmountList has changed:"
" it must be called on the context instead of passing" # Mapped Value implementation
" the context as first parameter", DeprecationWarning) # Transformation itself provides no properties or categories
# XXX add a 'transformation_amount_generator' group type def getMappedValuePropertyList(self):
kw['amount_generator_type_list'] = ('Transformation', return ()
'Transformed Resource',
'Transformation Operation', def getMappedValueBaseCategoryList(self, *args, **kw):
'Assorted Resource') return ()
if context is not None:
context = (context,) # IVariationRange and IVariated Implementation
return getAggregatedAmountList(context, *args, **kw) security.declareProtected(Permissions.AccessContentsInformation,
'updateVariationCategoryList')
def getQuantity(self, default=None): def updateVariationCategoryList(self):
# Used for amount generation """
# (Transformation is defined for 1 unit of target resource) Check if variation category list of the resource has changed and update
return 1. transformation and transformation line
"""
# Predicate Value implementation self.setVariationBaseCategoryList(self.getVariationBaseCategoryList())
# asPredicate takes into account the resource transformation_line_list = self.contentValues()
# XXX-JPS not Impl. for transformation_line in transformation_line_list:
transformation_line.updateVariationCategoryList()
# Mapped Value implementation
# Transformation itself provides no properties or categories security.declareProtected(Permissions.AccessContentsInformation,
def getMappedValuePropertyList(self): 'getVariationRangeBaseCategoryList')
return () def getVariationRangeBaseCategoryList(self):
"""
def getMappedValueBaseCategoryList(self): Returns possible variation base_category ids of the
return () default resource which can be used as variation axis
in the transformation.
# IVariationRange and IVariated Implementation """
security.declareProtected(Permissions.AccessContentsInformation, resource = self.getResourceValue()
'updateVariationCategoryList') if resource is not None:
def updateVariationCategoryList(self): result = resource.getVariationBaseCategoryList()
""" else:
Check if variation category list of the resource has changed and update # XXX result = self.getBaseCategoryIds()
transformation and transformation line # Why calling this method ?
""" # Get a global variable which define a list of variation base category
self.setVariationBaseCategoryList(self.getVariationBaseCategoryList()) result = self.getPortalVariationBaseCategoryList()
transformation_line_list = self.contentValues() return result
for transformation_line in transformation_line_list:
transformation_line.updateVariationCategoryList() security.declareProtected(Permissions.AccessContentsInformation,
'getVariationRangeBaseCategoryItemList')
security.declareProtected(Permissions.AccessContentsInformation, def getVariationRangeBaseCategoryItemList(self, display_id='getTitleOrId', **kw):
'getVariationRangeBaseCategoryList') """
def getVariationRangeBaseCategoryList(self): Returns possible variations of the transformation
""" as a list of tuples (id, title). This is mostly
Returns possible variation base_category ids of the useful in ERP5Form instances to generate selection
default resource which can be used as variation axis menus.
in the transformation. """
""" return self.portal_categories.getItemList(
resource = self.getResourceValue()
if resource is not None:
result = resource.getVariationBaseCategoryList()
else:
# XXX result = self.getBaseCategoryIds()
# Why calling this method ?
# Get a global variable which define a list of variation base category
result = self.getPortalVariationBaseCategoryList()
return result
security.declareProtected(Permissions.AccessContentsInformation,
'getVariationRangeBaseCategoryItemList')
def getVariationRangeBaseCategoryItemList(self, display_id='getTitleOrId', **kw):
"""
Returns possible variations of the transformation
as a list of tuples (id, title). This is mostly
useful in ERP5Form instances to generate selection
menus.
"""
return self.portal_categories.getItemList(
self.getVariationRangeBaseCategoryList(), self.getVariationRangeBaseCategoryList(),
display_id=display_id, **kw) display_id=display_id, **kw)
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getVariationRangeCategoryItemList') 'getVariationRangeCategoryItemList')
def getVariationRangeCategoryItemList(self, base_category_list=(), def getVariationRangeCategoryItemList(self, base_category_list=(),
omit_individual_variation=0, omit_individual_variation=0,
display_base_category=1, **kw): display_base_category=1, **kw):
""" """
Returns possible variation category values for the Returns possible variation category values for the
transformation according to the default resource. transformation according to the default resource.
Possible category values are provided as a list of Possible category values are provided as a list of
tuples (id, title). This is mostly tuples (id, title). This is mostly
useful in ERP5Form instances to generate selection useful in ERP5Form instances to generate selection
menus. menus.
User may want to define generic transformation without User may want to define generic transformation without
any defined resource. any defined resource.
""" """
if base_category_list is (): if base_category_list is ():
base_category_list = self.getVariationBaseCategoryList() base_category_list = self.getVariationBaseCategoryList()
resource = self.getResourceValue() resource = self.getResourceValue()
if resource is not None: if resource is not None:
result = resource.getVariationCategoryItemList( result = resource.getVariationCategoryItemList(
base_category_list=base_category_list, base_category_list=base_category_list,
omit_individual_variation=omit_individual_variation, omit_individual_variation=omit_individual_variation,
display_base_category=display_base_category,**kw) display_base_category=display_base_category,**kw)
else: else:
# No resource is define on transformation. # No resource is define on transformation.
# We want to display content of base categories # We want to display content of base categories
result = self.portal_categories.getCategoryChildTitleItemList( result = self.portal_categories.getCategoryChildTitleItemList(
base_category_list, base=1, display_none_category=0) base_category_list, base=1, display_none_category=0)
return result return result
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'setVariationBaseCategoryList') 'setVariationBaseCategoryList')
def setVariationBaseCategoryList(self, value): def setVariationBaseCategoryList(self, value):
""" """
Define the possible base categories and reindex object Define the possible base categories and reindex object
""" """
self._setVariationBaseCategoryList(value) self._setVariationBaseCategoryList(value)
self.reindexObject() self.reindexObject()
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'getVariationCategoryItemList') 'getVariationCategoryItemList')
def getVariationCategoryItemList(self, base_category_list=(), base=1, def getVariationCategoryItemList(self, base_category_list=(), base=1,
display_id='title', display_id='title',
current_category=None, current_category=None,
**kw): **kw):
""" """
Returns the list of possible variations Returns the list of possible variations
XXX Copied and modified from VariatedMixin XXX Copied and modified from VariatedMixin
Result is left display. Result is left display.
""" """
variation_category_item_list = [] variation_category_item_list = []
if base_category_list == (): if base_category_list == ():
base_category_list = self.getVariationBaseCategoryList() base_category_list = self.getVariationBaseCategoryList()
category_renderer = Renderer( category_renderer = Renderer(
is_right_display=0, is_right_display=0,
display_none_category=0, base=base, display_none_category=0, base=base,
current_category=current_category, current_category=current_category,
display_id='logical_path', **kw) display_id='logical_path', **kw)
for base_category in base_category_list: for base_category in base_category_list:
variation_category_list = self.getVariationCategoryList( variation_category_list = self.getVariationCategoryList(
base_category_list=[base_category]) base_category_list=[base_category])
category_list = [] category_list = []
object_list = [] object_list = []
for variation_category in variation_category_list: for variation_category in variation_category_list:
resource = self.portal_categories.resolveCategory(variation_category) resource = self.portal_categories.resolveCategory(variation_category)
if resource.getPortalType() == 'Category': if resource.getPortalType() == 'Category':
category_list.append(resource) category_list.append(resource)
else: else:
object_list.append(resource) object_list.append(resource)
variation_category_item_list.extend(category_renderer.\ variation_category_item_list.extend(category_renderer.\
render(category_list)) render(category_list))
variation_category_item_list.extend(Renderer( variation_category_item_list.extend(Renderer(
is_right_display=0, is_right_display=0,
base_category=base_category, base_category=base_category,
display_none_category=0, base=base, display_none_category=0, base=base,
current_category=current_category, current_category=current_category,
display_id=display_id,**kw).\ display_id=display_id,**kw).\
render(object_list)) render(object_list))
return variation_category_item_list return variation_category_item_list
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Transformation</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.Transformation</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.Transformation</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -31,12 +31,12 @@ ...@@ -31,12 +31,12 @@
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet, interfaces from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.AmountGeneratorLine import AmountGeneratorLine from Products.ERP5.Document.AmountGeneratorLine import AmountGeneratorLine
class TransformedResource(AmountGeneratorLine): class TransformedResource(AmountGeneratorLine):
""" """
TransformedResource defines which resource is being transformed TransformedResource defines which resource is being transformed
in order to produce a product define in the parent Transformation in order to produce a product define in the parent Transformation
document. document.
...@@ -47,119 +47,119 @@ class TransformedResource(AmountGeneratorLine): ...@@ -47,119 +47,119 @@ class TransformedResource(AmountGeneratorLine):
is no longer usable. It is time to reimplement it. This is is no longer usable. It is time to reimplement it. This is
completely unrelated to MatrixBox reimplementation unlike completely unrelated to MatrixBox reimplementation unlike
what is stated in some comments. what is stated in some comments.
"""
meta_type = 'ERP5 Transformed Resource'
portal_type = 'Transformed Resource'
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = (PropertySheet.TransformedResource, )
### Mapped Value Definition
# Provide default mapped value properties and categories if
# not defined
def getMappedValuePropertyList(self):
result = self._baseGetMappedValuePropertyList()
if not result:
# Since MappedValue does not inherit Amount, and type class of
# Transformation {Operation,Transformed Resource} Cell
# was changed to TransformedResource as a workaround,
# we also need to check if 'self' has a quantity.
# Otherwise, generated amounts could have 0 quantity
# (overridden by cells that only define variation).
result = ['quantity']
# Take into account variation_property_list for each variation
# for which hasProperty is true...
# FIXME: Why the resource and not the model line itself ? Or both ??
resource = self.getDefaultResourceValue()
if resource is not None:
result += resource.getVariationPropertyList()
result = filter(self.hasProperty, result)
return result
def getMappedValueBaseCategoryList(self, *args, **kw):
result = list(self._baseGetMappedValueBaseCategoryList())
if not result:
if not self.hasCellContent(base_id='variation'):
result = list(self.getVariationRangeBaseCategoryList()) # The current resource variation
if 'trade_phase' not in result:
result.append('trade_phase')
if 'industrial_phase' not in result:
result.append('industrial_phase')
return result
def getCellAggregateKey(self):
"""Define a key in order to aggregate amounts at cell level"""
return None
@classmethod
def getBaseAmountQuantity(cls, delivery_amount, base_application, rounding, *args, **kw):
value = delivery_amount.getGeneratedAmountQuantity(base_application)
if base_application == 'base_amount/produced_quantity':
value += delivery_amount.getConvertedQuantity()
return value
security.declareProtected(Permissions.AccessContentsInformation,
'getBaseApplication')
def getBaseApplication(self):
"""
""" """
return self.getBaseApplicationList()[0]
meta_type = 'ERP5 Transformed Resource' security.declareProtected(Permissions.AccessContentsInformation,
portal_type = 'Transformed Resource' 'getBaseApplicationList')
def getBaseApplicationList(self):
# Declarative security """
security = ClassSecurityInfo() """
security.declareObjectProtected(Permissions.AccessContentsInformation) # It is OK to try to acquire
return self._categoryGetBaseApplicationList() \
# Declarative properties or ['base_amount/produced_quantity']
property_sheets = (PropertySheet.TransformedResource, )
### Variation matrix definition
### Mapped Value Definition # XXX-JPS Some explanation needed
# Provide default mapped value properties and categories if security.declareProtected(Permissions.AccessContentsInformation,
# not defined 'updateVariationCategoryList')
def getMappedValuePropertyList(self): def updateVariationCategoryList(self):
result = self._baseGetMappedValuePropertyList() """
if not result:
# Since MappedValue does not inherit Amount, and type class of
# Transformation {Operation,Transformed Resource} Cell
# was changed to TransformedResource as a workaround,
# we also need to check if 'self' has a quantity.
# Otherwise, generated amounts could have 0 quantity
# (overridden by cells that only define variation).
result = ['quantity']
# Take into account variation_property_list for each variation
# for which hasProperty is true...
# FIXME: Why the resource and not the model line itself ? Or both ??
resource = self.getDefaultResourceValue()
if resource is not None:
result += resource.getVariationPropertyList()
result = filter(self.hasProperty, result)
return result
def getMappedValueBaseCategoryList(self):
result = list(self._baseGetMappedValueBaseCategoryList())
if not result:
if not self.hasCellContent(base_id='variation'):
result = list(self.getVariationRangeBaseCategoryList()) # The current resource variation
if 'trade_phase' not in result:
result.append('trade_phase')
if 'industrial_phase' not in result:
result.append('industrial_phase')
return result
def getCellAggregateKey(self):
"""Define a key in order to aggregate amounts at cell level"""
return None
@classmethod
def getBaseAmountQuantity(cls, delivery_amount, base_application, rounding):
value = delivery_amount.getGeneratedAmountQuantity(base_application)
if base_application == 'base_amount/produced_quantity':
value += delivery_amount.getConvertedQuantity()
return value
security.declareProtected(Permissions.AccessContentsInformation,
'getBaseApplication')
def getBaseApplication(self):
"""
"""
return self.getBaseApplicationList()[0]
security.declareProtected(Permissions.AccessContentsInformation,
'getBaseApplicationList')
def getBaseApplicationList(self):
"""
"""
# It is OK to try to acquire
return self._categoryGetBaseApplicationList() \
or ['base_amount/produced_quantity']
### Variation matrix definition
# XXX-JPS Some explanation needed
security.declareProtected(Permissions.AccessContentsInformation,
'updateVariationCategoryList')
def updateVariationCategoryList(self):
"""
Check if variation category list of the resource changed and Check if variation category list of the resource changed and
update transformed resource by doing a set cell range update transformed resource by doing a set cell range
""" """
self.setQVariationBaseCategoryList(self.getQVariationBaseCategoryList()) self.setQVariationBaseCategoryList(self.getQVariationBaseCategoryList())
self.setVVariationBaseCategoryList(self.getVVariationBaseCategoryList()) self.setVVariationBaseCategoryList(self.getVVariationBaseCategoryList())
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'_setQVariationBaseCategoryList') '_setQVariationBaseCategoryList')
def _setQVariationBaseCategoryList(self, value): def _setQVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Quantity value (Q) Defines the possible base categories which Quantity value (Q)
variate on variate on
""" """
self._baseSetQVariationBaseCategoryList(value) self._baseSetQVariationBaseCategoryList(value)
self._updateCellRange('quantity') self._updateCellRange('quantity')
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'_setVVariationBaseCategoryList') '_setVVariationBaseCategoryList')
def _setVVariationBaseCategoryList(self, value): def _setVVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Variation value (V) Defines the possible base categories which Variation value (V)
variate on variate on
""" """
self._baseSetVVariationBaseCategoryList(value) self._baseSetVVariationBaseCategoryList(value)
# XXX calling updatecellRange is better # XXX calling updatecellRange is better
self._updateCellRange('variation') self._updateCellRange('variation')
# XXX-JPS This should be handled by interaction workflow or interactor # XXX-JPS This should be handled by interaction workflow or interactor
# XXX-JPS SO many cases are not handled well... # XXX-JPS SO many cases are not handled well...
security.declareProtected(Permissions.ModifyPortalContent, security.declareProtected(Permissions.ModifyPortalContent,
'setVVariationBaseCategoryList') 'setVVariationBaseCategoryList')
def setVVariationBaseCategoryList(self, value): def setVVariationBaseCategoryList(self, value):
""" """
Defines the possible base categories which Variation value (V) Defines the possible base categories which Variation value (V)
variate on and reindex the object variate on and reindex the object
""" """
self._setVVariationBaseCategoryList(value) self._setVVariationBaseCategoryList(value)
self.reindexObject() self.reindexObject()
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>TransformedResource</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.TransformedResource</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.TransformedResource</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -35,24 +35,23 @@ from Products.ERP5Type.XMLObject import XMLObject ...@@ -35,24 +35,23 @@ from Products.ERP5Type.XMLObject import XMLObject
#class Variation(XMLObject, Variation): #class Variation(XMLObject, Variation):
class Variation(XMLObject): class Variation(XMLObject):
""" """
A Variation A Variation
""" """
meta_type = 'ERP5 Variation'
portal_type = 'Variation'
add_permission = Permissions.AddPortalContent
meta_type = 'ERP5 Variation' # Declarative security
portal_type = 'Variation' security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative security # Declarative properties
security = ClassSecurityInfo() property_sheets = ( PropertySheet.Base
security.declareObjectProtected(Permissions.AccessContentsInformation) , PropertySheet.CategoryCore
, PropertySheet.DublinCore
# Declarative properties , PropertySheet.Price
property_sheets = ( PropertySheet.Base , PropertySheet.Variation
, PropertySheet.CategoryCore , PropertySheet.VariationRange
, PropertySheet.DublinCore , PropertySheet.Reference
, PropertySheet.Price )
, PropertySheet.Variation \ No newline at end of file
, PropertySheet.VariationRange
, PropertySheet.Reference
)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>Variation</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.Variation</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.Variation</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testPDM</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.tests.testPDM</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testPDM</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
document.erp5.Consumption document.erp5.AssortedResource
\ No newline at end of file document.erp5.Consumption
document.erp5.Measure
document.erp5.QuantityUnitConversionDefinition
document.erp5.QuantityUnitConversionGroup
document.erp5.ResourceMeasuresConsistencyConstraint
document.erp5.Transformation
document.erp5.TransformedResource
document.erp5.Variation
\ No newline at end of file
test.erp5.testPDM
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
...@@ -45,9 +45,9 @@ implements_tuple_list = [ ...@@ -45,9 +45,9 @@ implements_tuple_list = [
(('erp5.component.document.TradeModelLine','TradeModelLine'), 'IAmountGenerator'), (('erp5.component.document.TradeModelLine','TradeModelLine'), 'IAmountGenerator'),
(('erp5.component.document.TradeModelLine','TradeModelLine'), 'IVariated'), (('erp5.component.document.TradeModelLine','TradeModelLine'), 'IVariated'),
(('erp5.component.document.TradeModelPath','TradeModelPath'), 'IArrowBase'), (('erp5.component.document.TradeModelPath','TradeModelPath'), 'IArrowBase'),
(('Products.ERP5.Document.Transformation','Transformation'), 'IAmountGenerator'), (('erp5.component.document.Transformation','Transformation'), 'IAmountGenerator'),
(('Products.ERP5.Document.Transformation','Transformation'), 'IVariated'), (('erp5.component.document.Transformation','Transformation'), 'IVariated'),
(('Products.ERP5.Document.TransformedResource','TransformedResource'), 'IVariated'), (('erp5.component.document.TransformedResource','TransformedResource'), 'IVariated'),
#IDocument #IDocument
(('Products.ERP5.Document.Document', 'Document'), 'IDocument'), (('Products.ERP5.Document.Document', 'Document'), 'IDocument'),
(('erp5.component.document.Image', 'Image'), 'IDocument'), (('erp5.component.document.Image', 'Image'), 'IDocument'),
......
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