Commit 7505e054 authored by Xiaowu Zhang's avatar Xiaowu Zhang

erp5_invoicing: enable "Merge Deliveries" action on accounting module

/reviewed-on nexedi/erp5!876
parents 17c0aa5a d2f4dd44
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>merge_delivery_list</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>Add portal content</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>5.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Merge Deliveries</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/DeliveryModule_mergeDeliveryList</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Accounting Transaction Module | merge_delivery_list
Invoice Line | price
Invoice Line | quantity
Invoice Line | view
......
......@@ -5,12 +5,15 @@ delivery_list = []
for o in object_list:
delivery_list.append(o)
Base_translateString = context.Base_translateString
if len(delivery_list) < 2:
ret_url = context.absolute_url() + '/' + form_id
qs = '?portal_status_message=Please+select+more+than+one+items.'
message = Base_translateString('Please select more than one items.')
else:
ret_url = context.absolute_url() + '/' + form_id
qs = '?portal_status_message=Merged.'
context.portal_simulation.mergeDeliveryList(delivery_list)
error_list = context.portal_simulation.mergeDeliveryList(delivery_list)
if not error_list:
message = Base_translateString('Merged.')
else:
message = ' '.join([str(x) for x in error_list])
return REQUEST.RESPONSE.redirect( ret_url + qs )
return context.Base_redirect('view',keep_items={'portal_status_message': message})
......@@ -2647,7 +2647,7 @@ class SimulationTool(BaseTool):
return result
def _findBuilderForDelivery(self, delivery):
def _findBuilderForDelivery(self, delivery, movement_portal_type):
"""
Find out the builder corresponding to a delivery by looking at the business process
"""
......@@ -2655,7 +2655,8 @@ class SimulationTool(BaseTool):
portal_type = delivery.getPortalType()
for business_link in delivery.asComposedDocument().objectValues(portal_type="Business Link"):
for business_link_builder in business_link.getDeliveryBuilderValueList():
if business_link_builder.getDeliveryPortalType() == portal_type:
if business_link_builder.getDeliveryPortalType() == portal_type \
and business_link_builder.getDeliveryLinePortalType() == movement_portal_type:
builder = business_link_builder
break
if builder is not None:
......@@ -2681,87 +2682,93 @@ class SimulationTool(BaseTool):
if len(portal_type_set) != 1:
error_list.append(translateString("Please select only deliveries of same type"))
else:
portal_type, = portal_type_set
allowed_state_set = set(portal.getPortalReservedInventoryStateList() + \
portal.getPortalFutureInventoryStateList())
found_state_set = set([x.getSimulationState() for x in delivery_list])
if found_state_set.difference(allowed_state_set):
error_list.append(translateString("Found delivery having unexpected status for merge"))
else:
# Allow to call a script to do custom checking conditions before merge
main_delivery = delivery_list[0]
check_merge_condition_method = main_delivery._getTypeBasedMethod("checkMergeConditionOnDeliveryList")
if check_merge_condition_method is not None:
error_list.extend(check_merge_condition_method(delivery_list=delivery_list))
if len(error_list) == 0:
# so far so good
# in delivery_list we have list of delivery to merge
simulation_movement_list = []
to_copy_delivery_line_list = [] # for lines not coming from upper simulation, thus
# created by hand should be manually added to main
# delivery since they are not coming from builder
for delivery in delivery_list:
line_id_to_delete_list = []
for movement in delivery.getMovementList():
related_simulation_movement_list = movement.getDeliveryRelatedValueList()
for simulation_movement in related_simulation_movement_list:
# if we are on a root applied rule directly, so in the case of
# a manually added line, we have to copy
# the simulation movement into to main delivery
if simulation_movement.getParentValue().getParentValue().getId() == "portal_simulation":
# For manually added lines, make sure we have only one simulation movement
assert len(related_simulation_movement_list) == 1
if not(delivery is main_delivery):
to_copy_delivery_line_list.append(movement)
else:
simulation_movement.setDeliveryValue(None)
simulation_movement_list.append(simulation_movement)
# Since we keep the main delivery, we remove existing lines already
# coming from builder to let builder recreate them in the same time
# as other ones (to possibly merge lines also)
movement_id = movement.getId()
if delivery is main_delivery and not(movement_id in line_id_to_delete_list):
line_id_to_delete_list.append(movement.getId())
if line_id_to_delete_list:
delivery.manage_delObjects(ids=line_id_to_delete_list)
# It is required to expand again simulation movement, because
# we unlinked them from delivery, so it is possible that some
# properties will change on simulation movement (mostly categories).
# By expanding again, we will avoid having many deliveries instead
# of one when doing "merge"
for simulation_movement in simulation_movement_list:
simulation_movement.expand(expand_policy='immediate')
# activate builder
merged_builder = self._findBuilderForDelivery(main_delivery)
if merged_builder is None:
error_list.append(translateString("Unable to find builder"))
else:
merged_builder.build(movement_relative_url_list=[q.getRelativeUrl() for q in \
simulation_movement_list], merge_delivery=True,
delivery_relative_url_list=[main_delivery.getRelativeUrl()])
# Finally, copy all lines that were created manually on all deliveries except
# the main one
@UnrestrictedMethod
def setMainDeliveryModifiable(delivery):
# set causality state in such way we can modify delivery
delivery.diverge()
setMainDeliveryModifiable(main_delivery)
delivery_type_list = portal.getPortalDeliveryTypeList()
for delivery_line in to_copy_delivery_line_list:
delivery = delivery_line.getParentValue()
if not(delivery.getPortalType() in delivery_type_list):
raise NotImplementedError("Merge of deliveries doe not yet handle case of cells")
copy_data = delivery.manage_copyObjects(ids=[delivery_line.getId()])
main_delivery.manage_pasteObjects(copy_data)
main_delivery.updateCausalityState()
# Finally do cleanup
for delivery in delivery_list[1:]:
# cancel, delete - to disallow any user related operations on those deliveries
after_merge_method = delivery._getTypeBasedMethod('cleanDeliveryAfterMerge')
if after_merge_method is not None:
after_merge_method()
movement_portal_type_set = set()
for delivery in delivery_list:
movement_portal_type_set.update([x.getPortalType() for x in delivery.getMovementList()])
if len(movement_portal_type_set) != 1:
error_list.append(translateString("Please Select only movement of same type"))
else:
# Allow to call a script to do custom checking conditions before merge
main_delivery = delivery_list[0]
check_merge_condition_method = main_delivery._getTypeBasedMethod("checkMergeConditionOnDeliveryList")
if check_merge_condition_method is not None:
error_list.extend(check_merge_condition_method(delivery_list=delivery_list))
if len(error_list) == 0:
# so far so good
# in delivery_list we have list of delivery to merge
simulation_movement_list = []
to_copy_delivery_line_list = [] # for lines not coming from upper simulation, thus
# created by hand should be manually added to main
# delivery since they are not coming from builder
for delivery in delivery_list:
line_id_to_delete_list = []
for movement in delivery.getMovementList():
related_simulation_movement_list = movement.getDeliveryRelatedValueList()
for simulation_movement in related_simulation_movement_list:
# if we are on a root applied rule directly, so in the case of
# a manually added line, we have to copy
# the simulation movement into to main delivery
if simulation_movement.getParentValue().getParentValue().getId() == "portal_simulation":
# For manually added lines, make sure we have only one simulation movement
assert len(related_simulation_movement_list) == 1
if not(delivery is main_delivery):
to_copy_delivery_line_list.append(movement)
else:
simulation_movement.setDeliveryValue(None)
simulation_movement_list.append(simulation_movement)
# Since we keep the main delivery, we remove existing lines already
# coming from builder to let builder recreate them in the same time
# as other ones (to possibly merge lines also)
movement_id = movement.getId()
if delivery is main_delivery and not(movement_id in line_id_to_delete_list):
line_id_to_delete_list.append(movement.getId())
if line_id_to_delete_list:
delivery.manage_delObjects(ids=line_id_to_delete_list)
# It is required to expand again simulation movement, because
# we unlinked them from delivery, so it is possible that some
# properties will change on simulation movement (mostly categories).
# By expanding again, we will avoid having many deliveries instead
# of one when doing "merge"
for simulation_movement in simulation_movement_list:
simulation_movement.expand(expand_policy='immediate')
# activate builder
movement_portal_type, = movement_portal_type_set
merged_builder = self._findBuilderForDelivery(main_delivery, movement_portal_type)
if merged_builder is None:
error_list.append(translateString("Unable to find builder"))
else:
merged_builder.build(movement_relative_url_list=[q.getRelativeUrl() for q in \
simulation_movement_list], merge_delivery=True,
delivery_relative_url_list=[main_delivery.getRelativeUrl()])
# Finally, copy all lines that were created manually on all deliveries except
# the main one
@UnrestrictedMethod
def setMainDeliveryModifiable(delivery):
# set causality state in such way we can modify delivery
delivery.diverge()
setMainDeliveryModifiable(main_delivery)
delivery_type_list = portal.getPortalDeliveryTypeList()
for delivery_line in to_copy_delivery_line_list:
delivery = delivery_line.getParentValue()
if not(delivery.getPortalType() in delivery_type_list):
raise NotImplementedError("Merge of deliveries doe not yet handle case of cells")
copy_data = delivery.manage_copyObjects(ids=[delivery_line.getId()])
main_delivery.manage_pasteObjects(copy_data)
main_delivery.updateCausalityState()
# Finally do cleanup
for delivery in delivery_list[1:]:
# cancel, delete - to disallow any user related operations on those deliveries
after_merge_method = delivery._getTypeBasedMethod('cleanDeliveryAfterMerge')
if after_merge_method is not None:
after_merge_method()
else:
error_list.append(translateString("Please select at least two deliveries"))
return error_list
......
......@@ -2471,6 +2471,63 @@ self.portal.getDefaultModule(self.packing_list_portal_type).newContent(
self.assertEqual([], packing_list.getDivergenceList())
self.assertEqual('solved', packing_list.getCausalityState())
def test_merge_accounting_invoice(
self, quiet=quiet):
sequence_list = SequenceList()
sequence = sequence_list.addSequenceString(self.PACKING_LIST_DEFAULT_SEQUENCE)
sequence_list.play(self, quiet=quiet)
packing_list = sequence.get('packing_list')
packing_list_line = packing_list.getMovementList()[0]
quantity = packing_list_line.getQuantity()
resource = packing_list_line.getResource()
price = packing_list_line.getPrice()
packing_list.setReady()
packing_list.start()
packing_list.stop()
self.tic()
self.default_quantity = self.default_quantity + 10
self.default_price = self.default_price + 10
self.tic()
sequence_list.play(self, quiet=quiet)
packing_list_2 = sequence.get('packing_list')
packing_list_line = packing_list_2.getMovementList()[0]
quantity_2 = packing_list_line.getQuantity()
resource_2 = packing_list_line.getResource()
price_2 = packing_list_line.getPrice()
packing_list_2.setReady()
packing_list_2.start()
packing_list_2.stop()
self.tic()
self.stepInvoiceBuilderAlarm()
self.tic()
self.default_quantity = self.default_quantity - 10
self.default_price = self.default_price - 10
invoice = packing_list.getCausalityRelatedValue(
portal_type=self.invoice_portal_type)
invoice_2 = packing_list_2.getCausalityRelatedValue(
portal_type=self.invoice_portal_type)
self.assertNotEquals(invoice, None)
self.assertNotEquals(invoice_2, None)
self.tic()
error_list = self.portal.portal_simulation.mergeDeliveryList([invoice, invoice_2])
self.tic()
self.assertEqual(0, len(error_list))
self.assertEqual(invoice.getSimulationState(), 'confirmed')
# MergeDeliveryList change the first delivery to diverged
# Make sure it works as expected
self.assertEqual(invoice.getCausalityState(), 'diverged')
self.assertEqual(invoice_2.getSimulationState(), 'cancelled')
self.assertEqual(len(invoice.getMovementList()), 2)
expected_set = set([
(resource,quantity,price),
(resource_2, quantity_2,price_2)])
result_set = set(sorted(
[(x.getResource(), x.getQuantity(), x.getPrice()) for x in invoice.getMovementList()],
key=lambda x: x[1]))
self.assertEqual(expected_set, result_set)
def test_subcontent_reindexing(self):
"""Tests, that modification on Order are propagated to lines and cells
......
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