Commit 4a464178 authored by Yoshinori Okuji's avatar Yoshinori Okuji

Rewrite collectMovement to make it generic. Fix many bugs in mergeDeliveryList.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@910 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 5faf6e92
...@@ -32,7 +32,7 @@ from AccessControl import ClassSecurityInfo ...@@ -32,7 +32,7 @@ from AccessControl import ClassSecurityInfo
from Globals import InitializeClass, DTMLFile from Globals import InitializeClass, DTMLFile
from Products.ERP5Type.Document.Folder import Folder from Products.ERP5Type.Document.Folder import Folder
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5.ERP5Globals import default_section_category, order_type_list, delivery_type_list, current_inventory_state_list from Products.ERP5.ERP5Globals import default_section_category, order_type_list, delivery_type_list, current_inventory_state_list, discount_type_list, simulated_movement_type_list, container_type_list, payment_condition_type_list, invoice_movement_type_list
from Products.ERP5 import _dtmldir from Products.ERP5 import _dtmldir
...@@ -40,7 +40,6 @@ from zLOG import LOG ...@@ -40,7 +40,6 @@ from zLOG import LOG
from Products.ERP5.Capacity.GLPK import solve from Products.ERP5.Capacity.GLPK import solve
from Numeric import zeros, resize from Numeric import zeros, resize
import cPickle
# Solver Registration # Solver Registration
is_initialized = 0 is_initialized = 0
...@@ -210,18 +209,50 @@ class SimulationTool (Folder, UniqueObject): ...@@ -210,18 +209,50 @@ class SimulationTool (Folder, UniqueObject):
####################################################### #######################################################
# Movement Group Collection / Delivery Creation # Movement Group Collection / Delivery Creation
def collectMovement(self, movement_list): def collectMovement(self, movement_list,
check_order = 1, check_path = 1, check_date = 1, check_criterion = 0,
check_resource = 1, check_base_variant = 0, check_variant = 1):
class RootGroup: class RootGroup:
def __init__(self,movement=None): def getNestedClass(self, class_list):
for a in class_list:
if a[0]:
return a[1]
return None
def setNestedClass(self):
"""
This sets an appropriate nested class.
"""
class_list = ((1, RootGroup),
(check_order, OrderGroup),
(check_path, PathGroup),
(check_date, DateGroup),
(check_criterion, CriterionGroup),
(check_resource, ResourceGroup),
(check_base_variant, BaseVariantGroup),
(check_variant, VariantGroup),
)
for i in range(len(class_list)):
if class_list[i][1] == self.__class__:
break
else:
raise RuntimeError, "no appropriate nested class is found for %s" % str(self)
self.nested_class = self.getNestedClass(class_list[i+1:])
def __init__(self, movement=None):
self.nested_class = None
self.setNestedClass()
self.movement_list = [] self.movement_list = []
self.group_list = [] self.group_list = []
if movement is not None : if movement is not None :
self.append(movement) self.append(movement)
def appendGroup(self, movement): def appendGroup(self, movement):
self.group_list.append(OrderGroup(movement)) if self.nested_class is not None:
self.group_list.append(self.nested_class(movement))
def append(self,movement): def append(self,movement):
self.movement_list.append(movement) self.movement_list.append(movement)
...@@ -258,9 +289,6 @@ class SimulationTool (Folder, UniqueObject): ...@@ -258,9 +289,6 @@ class SimulationTool (Folder, UniqueObject):
order_relative_url = order_value.getRelativeUrl() order_relative_url = order_value.getRelativeUrl()
self.order = order_relative_url self.order = order_relative_url
def appendGroup(self, movement):
self.group_list.append(PathGroup(movement))
def test(self,movement): def test(self,movement):
if hasattr(movement, 'getRootAppliedRule'): if hasattr(movement, 'getRootAppliedRule'):
order_value = movement.getRootAppliedRule().getCausalityValue( order_value = movement.getRootAppliedRule().getCausalityValue(
...@@ -294,9 +322,6 @@ class SimulationTool (Folder, UniqueObject): ...@@ -294,9 +322,6 @@ class SimulationTool (Folder, UniqueObject):
self.source_section = movement.getSourceSection() self.source_section = movement.getSourceSection()
self.destination_section = movement.getDestinationSection() self.destination_section = movement.getDestinationSection()
def appendGroup(self, movement):
self.group_list.append(DateGroup(movement))
def test(self,movement): def test(self,movement):
if movement.getSource() == self.source and \ if movement.getSource() == self.source and \
movement.getDestination() == self.destination and \ movement.getDestination() == self.destination and \
...@@ -315,9 +340,6 @@ class SimulationTool (Folder, UniqueObject): ...@@ -315,9 +340,6 @@ class SimulationTool (Folder, UniqueObject):
self.start_date = movement.getStartDate() self.start_date = movement.getStartDate()
self.stop_date = movement.getStopDate() self.stop_date = movement.getStopDate()
def appendGroup(self, movement):
self.group_list.append(ResourceGroup(movement))
def test(self,movement): def test(self,movement):
if movement.getStartDate() == self.start_date and \ if movement.getStartDate() == self.start_date and \
movement.getStopDate() == self.stop_date : movement.getStopDate() == self.stop_date :
...@@ -325,30 +347,58 @@ class SimulationTool (Folder, UniqueObject): ...@@ -325,30 +347,58 @@ class SimulationTool (Folder, UniqueObject):
else : else :
return 0 return 0
class CriterionGroup(RootGroup):
def __init__(self,movement):
RootGroup.__init__(self,movement)
if hasattr(movement, 'getGroupCriterion'):
self.criterion = movement.getGroupCriterion()
else:
self.criterion = None
def test(self,movement):
# we must have the same criterion
if hasattr(movement, 'getGroupCriterion'):
criterion = movement.getGroupCriterion()
else:
criterion = None
return self.criterion == criterion
class ResourceGroup(RootGroup): class ResourceGroup(RootGroup):
def __init__(self,movement): def __init__(self,movement):
RootGroup.__init__(self,movement) RootGroup.__init__(self,movement)
self.resource = movement.getResource() self.resource = movement.getResource()
def appendGroup(self, movement):
self.group_list.append(VariantGroup(movement))
def test(self,movement): def test(self,movement):
if movement.getResource() == self.resource : if movement.getResource() == self.resource :
return 1 return 1
else : else :
return 0 return 0
class BaseVariantGroup(RootGroup):
def __init__(self,movement):
RootGroup.__init__(self,movement)
self.base_category_list = movement.getVariationBaseCategoryList()
def test(self,movement):
# we must have the same number of categories
categories_identity = 0
if len(self.base_category_list) == len(movement.getVariationBaseCategoryList()) :
for category in movement.getVariationBaseCategoryList() :
if not category in self.base_category_list :
break
else :
categories_identity = 1
return categories_identity
class VariantGroup(RootGroup): class VariantGroup(RootGroup):
def __init__(self,movement): def __init__(self,movement):
RootGroup.__init__(self,movement) RootGroup.__init__(self,movement)
self.category_list = movement.getVariationCategoryList() self.category_list = movement.getVariationCategoryList()
def appendGroup(self, movement):
pass
def test(self,movement): def test(self,movement):
# we must have the same number of categories # we must have the same number of categories
categories_identity = 0 categories_identity = 0
...@@ -1097,6 +1147,9 @@ class SimulationTool (Folder, UniqueObject): ...@@ -1097,6 +1147,9 @@ class SimulationTool (Folder, UniqueObject):
return result return result
# Used for mergeDeliveryList.
class MergeDeliveryListError(Exception): pass
security.declareProtected( Permissions.ModifyPortalContent, 'mergeDeliveryList' ) security.declareProtected( Permissions.ModifyPortalContent, 'mergeDeliveryList' )
def mergeDeliveryList(self, delivery_list): def mergeDeliveryList(self, delivery_list):
""" """
...@@ -1106,18 +1159,17 @@ class SimulationTool (Folder, UniqueObject): ...@@ -1106,18 +1159,17 @@ class SimulationTool (Folder, UniqueObject):
The others are cancelled. The others are cancelled.
Return the main delivery. Return the main delivery.
""" """
class MergeDeliveryListError(Exception): pass
# Sanity checks. # Sanity checks.
if len(delivery_list) == 0: if len(delivery_list) == 0:
raise MergeDeliveryListError, "No delivery is passed" raise self.MergeDeliveryListError, "No delivery is passed"
elif len(delivery_list) == 1: elif len(delivery_list) == 1:
raise MergeDeliveryListError, "Only one delivery is passed" raise self.MergeDeliveryListError, "Only one delivery is passed"
main_delivery = delivery_list[0] main_delivery = delivery_list[0]
delivery_list = delivery_list[1:] delivery_list = delivery_list[1:]
# One more sanity check. # Another sanity check. It is necessary for them to be identical in some attributes.
for delivery in delivery_list: for delivery in delivery_list:
for attr in ('portal_type', 'simulation_state', for attr in ('portal_type', 'simulation_state',
'source', 'destination', 'source', 'destination',
...@@ -1128,118 +1180,181 @@ class SimulationTool (Folder, UniqueObject): ...@@ -1128,118 +1180,181 @@ class SimulationTool (Folder, UniqueObject):
main_value = main_delivery.getProperty(attr) main_value = main_delivery.getProperty(attr)
value = delivery.getProperty(attr) value = delivery.getProperty(attr)
if main_value != value: if main_value != value:
raise MergeDeliveryListError, \ raise self.MergeDeliveryListError, \
"In %s of %s, %s is different from %s" % (attr, delivery.getId(), value, main_value) "%s is not the same between %s and %s (%s and %s)" % (attr, delivery.getId(), main_delivery.getId(), value, main_value)
# One more sanity check. Check if discounts are the same, if any.
main_discount_list = main_delivery.contentValues(filter = {'portal_type': discount_type_list})
for delivery in delivery_list:
discount_list = delivery.contentValues(filter = {'portal_type': discount_type_list})
if len(main_discount_list) != len(discount_list):
raise self.MergeDeliveryListError, "Discount is not the same between %s and %s" % (delivery.getId(), main_delivery.getId())
for discount in discount_list:
for main_discount in main_discount_list:
if discount.getDiscount() == main_discount.getDiscount() \
and discount.getDiscountRatio() == main_discount.getDiscountRatio() \
and discount.getDiscountType() == main_discount.getDiscountType() \
and discount.getImmediateDiscount() == main_discount.getImmediateDiscount():
break
else:
raise self.MergeDeliveryListError, "Discount is not the same between %s and %s" % (delivery.getId(), main_delivery.getId())
# One more sanity check. Check if payment conditions are the same, if any.
main_payment_condition_list = main_delivery.contentValues(filter = {'portal_type': payment_condition_type_list})
for delivery in delivery_list:
payment_condition_list = delivery.contentValues(filter = {'portal_type': payment_condition_type_list})
if len(main_payment_condition_list) != len(payment_condition_list):
raise self.MergeDeliveryListError, "Payment Condition is not the same between %s and %s" % (delivery.getId(), main_delivery.getId())
for condition in payment_condition_list:
for main_condition in main_payment_condition_list:
if condition.getPaymentMode() == main_condition.getPaymentMode() \
and condition.getPaymentAdditionalTerm() == main_condition.getPaymentAdditionalTerm() \
and condition.getPaymentAmount() == main_condition.getPaymentAmount() \
and condition.getPaymentEndOfMonth() == main_condition.getPaymentEndOfMonth() \
and condition.getPaymentRatio() == main_condition.getPaymentRatio() \
and condition.getPaymentTerm() == main_condition.getPaymentTerm():
break
else:
raise self.MergeDeliveryListError, "Payment Condition is not the same between %s and %s" % (delivery.getId(), main_delivery.getId())
# Make sure that all activities are flushed, to get simulation movements from delivery cells. # Make sure that all activities are flushed, to get simulation movements from delivery cells.
for delivery in delivery_list: for delivery in delivery_list:
for order in delivery.getCausalityValueList(portal_type = order_type_list): for order in delivery.getCausalityValueList(portal_type = order_type_list):
for applied_rule in order.getCausalityRelatedValueList(portal_type = 'Applied Rule'): for applied_rule in order.getCausalityRelatedValueList(portal_type = 'Applied Rule'):
applied_rule.flushActivity(invoke = 1) applied_rule.flushActivity(invoke = 1)
for causality_related_delivery in delivery.getCausalityValueList(portal_type = delivery_type_list):
for applied_rule in causality_related_delivery.getCausalityRelatedValueList(portal_type = 'Applied Rule'):
applied_rule.flushActivity(invoke = 1)
# Get a list of movements. # Get a list of simulated movements and invoice movements.
movement_list = [] main_simulated_movement_list = main_delivery.getSimulatedMovementList()
main_invoice_movement_list = main_delivery.getInvoiceMovementList()
simulated_movement_list = main_simulated_movement_list[:]
invoice_movement_list = main_invoice_movement_list[:]
for delivery in delivery_list: for delivery in delivery_list:
movement_list.extend(delivery.getMovementList()) simulated_movement_list.extend(delivery.getSimulatedMovementList())
invoice_movement_list.extend(delivery.getInvoiceMovementList())
group_list = main_delivery.collectMovement(movement_list)
for group in group_list: LOG('mergeDeliveryList', 0, 'simulated_movement_list = %s, invoice_movement_list = %s' % (str(simulated_movement_list), str(invoice_movement_list)))
# First, try to find a delivery line which matches this group in the main delivery. for main_movement_list, movement_list in \
for delivery_line in main_delivery.contentValues(): ((main_simulated_movement_list, simulated_movement_list),
if group.resource_id == delivery_line.getResourceId() and \ (main_invoice_movement_list, invoice_movement_list)):
group.variation_base_category_list == delivery_line.getVariationBaseCategoryList(): root_group = self.collectMovement(movement_list,
# Found. Update the list of variation categories. check_order = 0,
delivery_line.setVariationCategoryList(group.variation_category_list) check_path = 0,
break check_date = 0,
else: check_criterion = 1,
# Create a new delivery line. check_resource = 1,
delivery_line_type = None check_base_variant = 1,
for delivery in delivery_list: check_variant = 1)
for delivery_line in delivery.contentValues(): for criterion_group in root_group.group_list:
delivery_line_type = delivery_line.getPortalType() for resource_group in criterion_group.group_list:
break for base_variant_group in resource_group.group_list:
delivery_line = main_delivery.newContent(portal_type = delivery_line_type, # Get a list of categories.
resource = group.resource) category_dict = {}
delivery_line.setVariationBaseCategoryList(group.variation_base_category_list) for variant_group in base_variant_group.group_list:
delivery_line.setVariationCategoryList(group.variation_category_list) for category in variant_group.category_list:
category_dict[category] = 1
# Make variant groups. Since Python cannot use a list as an index of a hash, category_list = category_dict.keys()
# use a picked object as an index instead.
variant_map = {} # Try to find a delivery line.
for movement in group.movement_list: delivery_line = None
predicate_list = movement.getPredicateValueList() for movement in base_variant_group.movement_list:
predicate_list.sort() if movement in main_movement_list:
pickled_predicate_list = cPickle.dumps(predicate_list) if movement.aq_parent.getPortalType() in simulated_movement_type_list \
if pickled_predicate_list in variant_map: or movement.aq_parent.getPortalType() in invoice_movement_type_list:
variant_map[pickled_predicate_list].append(movement) delivery_line = movement.aq_parent
else: else:
variant_map[pickled_predicate_list] = [movement] delivery_line = movement
LOG('mergeDeliveryList', 0, 'delivery_line %s is found: criterion = %s, resource = %s, base_category_list = %s' % (repr(delivery_line), repr(criterion_group.criterion), repr(resource_group.resource), repr(base_variant_group.base_category_list)))
break
for pickled_predicate_list,movement_list in variant_map.items(): if delivery_line is None:
predicate_list = cPickle.loads(pickled_predicate_list) # Not found. So create a new delivery line.
object_to_update = None movement = base_variant_group.movement_list[0]
if len(predicate_list) == 0: if movement.aq_parent.getPortalType() in simulated_movement_type_list \
object_to_update = delivery_line or movement.aq_parent.getPortalType() in invoice_movement_type_list:
else: delivery_line_type = movement.aq_parent.getPortalType()
identical = 0
for delivery_cell in delivery_line.contentValues():
if len(delivery_cell.getPredicateValueList()) == len(predicate_list):
for category in delivery_cell.getPredicateValueList():
if category not in predicate_list:
break
else: else:
identical = 1 delivery_line_type = movement.getPortalType()
break delivery_line = main_delivery.newContent(portal_type = delivery_line_type,
resource = resource_group.resource)
LOG('mergeDeliveryList', 0, 'New delivery_line %s is created: criterion = %s, resource = %s, base_category_list = %s' % (repr(delivery_line), repr(criterion_group.criterion), repr(resource_group.resource), repr(base_variant_group.base_category_list)))
# Update the base categories and categories.
#LOG('mergeDeliveryList', 0, 'base_category_list = %s, category_list = %s' % (repr(base_category_list), repr(category_list)))
delivery_line.setVariationBaseCategoryList(base_variant_group.base_category_list)
delivery_line.setVariationCategoryList(category_list)
for variant_group in base_variant_group.group_list:
if len(variant_group.category_list) == 0:
object_to_update = delivery_line
else:
for delivery_cell in delivery_line.contentValues():
predicate_value_list = delivery_cell.getPredicateValueList()
if len(predicate_value_list) == len(variant_group.category_list):
for category in variant_group.category_list:
if category not in predicate_value_list:
break
else:
object_to_update = delivery_cell
break
if identical: LOG('mergeDeliveryList', 0, 'object_to_update = %s' % repr(object_to_update))
object_to_update = delivery_cell if object_to_update is not None:
cell_quantity = object_to_update.getQuantity() or 0.0
LOG("mergeDeliveryList", 0, "predicate_list = %s, movement_list = %s, object_to_update = %s" % (repr(predicate_list), repr(movement_list), repr(object_to_update))) cell_target_quantity = object_to_update.getNetConvertedTargetQuantity() or 0.0
if object_to_update is not None: cell_total_price = cell_target_quantity * (object_to_update.getPrice() or 0.0)
cell_quantity = object_to_update.getQuantity() cell_category_list = list(object_to_update.getCategoryList())
cell_target_quantity = object_to_update.getNetConvertedTargetQuantity()
cell_total_price = cell_target_quantity * object_to_update.getPrice() for movement in variant_group.movement_list:
cell_category_list = list(object_to_update.getCategoryList()) if movement in main_movement_list:
LOG("mergeDeliveryList", 0, "cell_target_quantity = %s, cell_quantity = %s, cell_total_price = %s, cell_category_list = %s" % (repr(cell_target_quantity), repr(cell_quantity), repr(cell_total_price), repr(cell_category_list))) continue
LOG('mergeDeliveryList', 0, 'movement = %s' % repr(movement))
for movement in movement_list : cell_quantity += movement.getQuantity()
cell_quantity += movement.getQuantity() cell_target_quantity += movement.getNetConvertedTargetQuantity()
cell_target_quantity += movement.getNetConvertedTargetQuantity() try:
try: # XXX WARNING - ADD PRICED QUANTITY
# XXX WARNING - ADD PRICED QUANTITY cell_total_price += movement.getNetConvertedTargetQuantity() * movement.getPrice()
cell_total_price += movement.getNetConvertedTargetQuantity() * movement.getPrice() except:
except: cell_total_price = None
cell_total_price = None for category in movement.getCategoryList():
for category in movement.getCategoryList(): if category not in cell_category_list:
if category not in cell_category_list: cell_category_list.append(category)
cell_category_list.append(category) # Make sure that simulation movements point to an appropriate delivery line or
LOG("mergeDeliveryList", 0, "movement = %s, cell_target_quantity = %s, cell_quantity = %s, cell_total_price = %s, cell_category_list = %s" % (repr(movement), repr(cell_target_quantity), repr(cell_quantity), repr(cell_total_price), repr(cell_category_list))) # delivery cell.
# Make sure that simulation movements point to an appropriate delivery line or if hasattr(movement, 'getDeliveryRelatedValueList'):
# delivery cell. for simulation_movement in \
if hasattr(movement, 'getDeliveryRelatedValueList'): movement.getDeliveryRelatedValueList(portal_type = 'Simulation Movement'):
for simulation_movement in \ simulation_movement.setDeliveryValue(object_to_update)
movement.getDeliveryRelatedValueList(portal_type = 'Simulation Movement'): #simulation_movement.reindexObject()
simulation_movement.setDeliveryValue(object_to_update) if hasattr(movement, 'getOrderRelatedValueList'):
#simulation_movement.reindexObject() for simulation_movement in \
if hasattr(movement, 'getOrderRelatedValueList'): movement.getOrderRelatedValueList(portal_type = 'Simulation Movement'):
for simulation_movement in \ simulation_movement.setOrderValue(object_to_update)
movement.getOrderRelatedValueList(portal_type = 'Simulation Movement'): #simulation_movement.reindexObject()
simulation_movement.setOrderValue(object_to_update)
#simulation_movement.reindexObject() if cell_target_quantity != 0 and cell_total_price is not None:
average_price = cell_total_price / cell_target_quantity
if cell_target_quantity != 0 and cell_total_price is not None: else:
average_price = cell_total_price / cell_target_quantity average_price = 0
else:
average_price = 0 LOG('mergeDeliveryList', 0, 'cell_category_list = %s' % repr(cell_category_list))
#LOG('object mis ?jour',0,str(object_to_update.getRelativeUrl())) object_to_update.setCategoryList(cell_category_list)
object_to_update.setCategoryList(cell_category_list) object_to_update.edit(target_quantity = cell_target_quantity,
object_to_update.edit(target_quantity = cell_target_quantity, quantity = cell_quantity,
quantity = cell_quantity, price = average_price,
price = average_price, )
) #object_to_update.reindexObject()
#object_to_update.reindexObject() else:
else: raise self.MergeDeliveryListError, "No object to update"
raise MergeDeliveryListError, "No object to update"
# Merge containers. Just copy them from other deliveries into the main.
for delivery in delivery_list:
container_id_list = delivery.contentIds(filter = {'portal_type': container_type_list})
if len(container_id_list) > 0:
copy_data = delivery.manage_copyObjects(ids = container_id_list)
new_id_list = main_delivery.manage_pasteObjects(copy_data)
# Unify the list of causality. # Unify the list of causality.
causality_list = main_delivery.getCausalityValueList() causality_list = main_delivery.getCausalityValueList()
...@@ -1261,4 +1376,4 @@ class SimulationTool (Folder, UniqueObject): ...@@ -1261,4 +1376,4 @@ class SimulationTool (Folder, UniqueObject):
return main_delivery return main_delivery
InitializeClass(SimulationTool) InitializeClass(SimulationTool)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment