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
from Globals import InitializeClass, DTMLFile
from Products.ERP5Type.Document.Folder import Folder
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
......@@ -40,7 +40,6 @@ from zLOG import LOG
from Products.ERP5.Capacity.GLPK import solve
from Numeric import zeros, resize
import cPickle
# Solver Registration
is_initialized = 0
......@@ -210,18 +209,50 @@ class SimulationTool (Folder, UniqueObject):
#######################################################
# 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:
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.group_list = []
if movement is not None :
self.append(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):
self.movement_list.append(movement)
......@@ -258,9 +289,6 @@ class SimulationTool (Folder, UniqueObject):
order_relative_url = order_value.getRelativeUrl()
self.order = order_relative_url
def appendGroup(self, movement):
self.group_list.append(PathGroup(movement))
def test(self,movement):
if hasattr(movement, 'getRootAppliedRule'):
order_value = movement.getRootAppliedRule().getCausalityValue(
......@@ -294,9 +322,6 @@ class SimulationTool (Folder, UniqueObject):
self.source_section = movement.getSourceSection()
self.destination_section = movement.getDestinationSection()
def appendGroup(self, movement):
self.group_list.append(DateGroup(movement))
def test(self,movement):
if movement.getSource() == self.source and \
movement.getDestination() == self.destination and \
......@@ -315,9 +340,6 @@ class SimulationTool (Folder, UniqueObject):
self.start_date = movement.getStartDate()
self.stop_date = movement.getStopDate()
def appendGroup(self, movement):
self.group_list.append(ResourceGroup(movement))
def test(self,movement):
if movement.getStartDate() == self.start_date and \
movement.getStopDate() == self.stop_date :
......@@ -325,30 +347,58 @@ class SimulationTool (Folder, UniqueObject):
else :
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):
def __init__(self,movement):
RootGroup.__init__(self,movement)
self.resource = movement.getResource()
def appendGroup(self, movement):
self.group_list.append(VariantGroup(movement))
def test(self,movement):
if movement.getResource() == self.resource :
return 1
else :
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):
def __init__(self,movement):
RootGroup.__init__(self,movement)
self.category_list = movement.getVariationCategoryList()
def appendGroup(self, movement):
pass
def test(self,movement):
# we must have the same number of categories
categories_identity = 0
......@@ -1097,6 +1147,9 @@ class SimulationTool (Folder, UniqueObject):
return result
# Used for mergeDeliveryList.
class MergeDeliveryListError(Exception): pass
security.declareProtected( Permissions.ModifyPortalContent, 'mergeDeliveryList' )
def mergeDeliveryList(self, delivery_list):
"""
......@@ -1106,18 +1159,17 @@ class SimulationTool (Folder, UniqueObject):
The others are cancelled.
Return the main delivery.
"""
class MergeDeliveryListError(Exception): pass
# Sanity checks.
if len(delivery_list) == 0:
raise MergeDeliveryListError, "No delivery is passed"
raise self.MergeDeliveryListError, "No delivery is passed"
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]
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 attr in ('portal_type', 'simulation_state',
'source', 'destination',
......@@ -1128,118 +1180,181 @@ class SimulationTool (Folder, UniqueObject):
main_value = main_delivery.getProperty(attr)
value = delivery.getProperty(attr)
if main_value != value:
raise MergeDeliveryListError, \
"In %s of %s, %s is different from %s" % (attr, delivery.getId(), value, main_value)
raise self.MergeDeliveryListError, \
"%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.
for delivery in delivery_list:
for order in delivery.getCausalityValueList(portal_type = order_type_list):
for applied_rule in order.getCausalityRelatedValueList(portal_type = 'Applied Rule'):
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.
movement_list = []
# Get a list of simulated movements and invoice movements.
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:
movement_list.extend(delivery.getMovementList())
group_list = main_delivery.collectMovement(movement_list)
for group in group_list:
# First, try to find a delivery line which matches this group in the main delivery.
for delivery_line in main_delivery.contentValues():
if group.resource_id == delivery_line.getResourceId() and \
group.variation_base_category_list == delivery_line.getVariationBaseCategoryList():
# Found. Update the list of variation categories.
delivery_line.setVariationCategoryList(group.variation_category_list)
break
else:
# Create a new delivery line.
delivery_line_type = None
for delivery in delivery_list:
for delivery_line in delivery.contentValues():
delivery_line_type = delivery_line.getPortalType()
break
delivery_line = main_delivery.newContent(portal_type = delivery_line_type,
resource = group.resource)
delivery_line.setVariationBaseCategoryList(group.variation_base_category_list)
delivery_line.setVariationCategoryList(group.variation_category_list)
# Make variant groups. Since Python cannot use a list as an index of a hash,
# use a picked object as an index instead.
variant_map = {}
for movement in group.movement_list:
predicate_list = movement.getPredicateValueList()
predicate_list.sort()
pickled_predicate_list = cPickle.dumps(predicate_list)
if pickled_predicate_list in variant_map:
variant_map[pickled_predicate_list].append(movement)
else:
variant_map[pickled_predicate_list] = [movement]
simulated_movement_list.extend(delivery.getSimulatedMovementList())
invoice_movement_list.extend(delivery.getInvoiceMovementList())
LOG('mergeDeliveryList', 0, 'simulated_movement_list = %s, invoice_movement_list = %s' % (str(simulated_movement_list), str(invoice_movement_list)))
for main_movement_list, movement_list in \
((main_simulated_movement_list, simulated_movement_list),
(main_invoice_movement_list, invoice_movement_list)):
root_group = self.collectMovement(movement_list,
check_order = 0,
check_path = 0,
check_date = 0,
check_criterion = 1,
check_resource = 1,
check_base_variant = 1,
check_variant = 1)
for criterion_group in root_group.group_list:
for resource_group in criterion_group.group_list:
for base_variant_group in resource_group.group_list:
# Get a list of categories.
category_dict = {}
for variant_group in base_variant_group.group_list:
for category in variant_group.category_list:
category_dict[category] = 1
category_list = category_dict.keys()
# Try to find a delivery line.
delivery_line = None
for movement in base_variant_group.movement_list:
if movement in main_movement_list:
if movement.aq_parent.getPortalType() in simulated_movement_type_list \
or movement.aq_parent.getPortalType() in invoice_movement_type_list:
delivery_line = movement.aq_parent
else:
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():
predicate_list = cPickle.loads(pickled_predicate_list)
object_to_update = None
if len(predicate_list) == 0:
object_to_update = delivery_line
else:
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
if delivery_line is None:
# Not found. So create a new delivery line.
movement = base_variant_group.movement_list[0]
if movement.aq_parent.getPortalType() in simulated_movement_type_list \
or movement.aq_parent.getPortalType() in invoice_movement_type_list:
delivery_line_type = movement.aq_parent.getPortalType()
else:
identical = 1
break
delivery_line_type = movement.getPortalType()
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:
object_to_update = delivery_cell
LOG("mergeDeliveryList", 0, "predicate_list = %s, movement_list = %s, object_to_update = %s" % (repr(predicate_list), repr(movement_list), repr(object_to_update)))
if object_to_update is not None:
cell_quantity = object_to_update.getQuantity()
cell_target_quantity = object_to_update.getNetConvertedTargetQuantity()
cell_total_price = cell_target_quantity * object_to_update.getPrice()
cell_category_list = list(object_to_update.getCategoryList())
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)))
for movement in movement_list :
cell_quantity += movement.getQuantity()
cell_target_quantity += movement.getNetConvertedTargetQuantity()
try:
# XXX WARNING - ADD PRICED QUANTITY
cell_total_price += movement.getNetConvertedTargetQuantity() * movement.getPrice()
except:
cell_total_price = None
for category in movement.getCategoryList():
if category not in cell_category_list:
cell_category_list.append(category)
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)))
# Make sure that simulation movements point to an appropriate delivery line or
# delivery cell.
if hasattr(movement, 'getDeliveryRelatedValueList'):
for simulation_movement in \
movement.getDeliveryRelatedValueList(portal_type = 'Simulation Movement'):
simulation_movement.setDeliveryValue(object_to_update)
#simulation_movement.reindexObject()
if hasattr(movement, 'getOrderRelatedValueList'):
for simulation_movement in \
movement.getOrderRelatedValueList(portal_type = 'Simulation Movement'):
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
else:
average_price = 0
#LOG('object mis ?jour',0,str(object_to_update.getRelativeUrl()))
object_to_update.setCategoryList(cell_category_list)
object_to_update.edit(target_quantity = cell_target_quantity,
quantity = cell_quantity,
price = average_price,
)
#object_to_update.reindexObject()
else:
raise MergeDeliveryListError, "No object to update"
LOG('mergeDeliveryList', 0, 'object_to_update = %s' % repr(object_to_update))
if object_to_update is not None:
cell_quantity = object_to_update.getQuantity() or 0.0
cell_target_quantity = object_to_update.getNetConvertedTargetQuantity() or 0.0
cell_total_price = cell_target_quantity * (object_to_update.getPrice() or 0.0)
cell_category_list = list(object_to_update.getCategoryList())
for movement in variant_group.movement_list:
if movement in main_movement_list:
continue
LOG('mergeDeliveryList', 0, 'movement = %s' % repr(movement))
cell_quantity += movement.getQuantity()
cell_target_quantity += movement.getNetConvertedTargetQuantity()
try:
# XXX WARNING - ADD PRICED QUANTITY
cell_total_price += movement.getNetConvertedTargetQuantity() * movement.getPrice()
except:
cell_total_price = None
for category in movement.getCategoryList():
if category not in cell_category_list:
cell_category_list.append(category)
# Make sure that simulation movements point to an appropriate delivery line or
# delivery cell.
if hasattr(movement, 'getDeliveryRelatedValueList'):
for simulation_movement in \
movement.getDeliveryRelatedValueList(portal_type = 'Simulation Movement'):
simulation_movement.setDeliveryValue(object_to_update)
#simulation_movement.reindexObject()
if hasattr(movement, 'getOrderRelatedValueList'):
for simulation_movement in \
movement.getOrderRelatedValueList(portal_type = 'Simulation Movement'):
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
else:
average_price = 0
LOG('mergeDeliveryList', 0, 'cell_category_list = %s' % repr(cell_category_list))
object_to_update.setCategoryList(cell_category_list)
object_to_update.edit(target_quantity = cell_target_quantity,
quantity = cell_quantity,
price = average_price,
)
#object_to_update.reindexObject()
else:
raise self.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.
causality_list = main_delivery.getCausalityValueList()
......@@ -1261,4 +1376,4 @@ class SimulationTool (Folder, UniqueObject):
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