diff --git a/product/ERP5/GeneratedAmountList.py b/product/ERP5/GeneratedAmountList.py index c28adb33952a1cf83bed9156f676475fd89a9bb2..e67cc073e2951bcd5d9c31a4eba41266c076b105 100644 --- a/product/ERP5/GeneratedAmountList.py +++ b/product/ERP5/GeneratedAmountList.py @@ -27,6 +27,7 @@ # ############################################################################## +from collections import defaultdict import zope.interface from AccessControl import allow_class from Products.ERP5Type import interfaces @@ -35,6 +36,13 @@ class GeneratedAmountList(list): """ Temporary object needed to aggregate Amount value And to calculate some report or total value + + For example, delivery.getGeneratedAmountList() returns an object of this + type, with amounts for each movement and/or for the delivery. This result + can be used to get: + 1. totals for the delivery, by first using aggregate() + 2. detailed information on each movement with split(), which would be + equivalent to call getGeneratedAmountList() on each movement """ zope.interface.implements(interfaces.IAmountList) @@ -61,6 +69,12 @@ class GeneratedAmountList(list): return result def aggregate(self): + """Return a list of aggregated amounts + + Groups amounts with same price, efficiency, reference and categories, merge + them by summing their quantities, and return the new amounts in a new list. + """ + from Products.ERP5Type.Document import newTempAmount # XXX: Do we handle rounding correctly ? # What to do if only total price is rounded ?? aggregate_dict = {} @@ -71,14 +85,33 @@ class GeneratedAmountList(list): aggregate = aggregate_dict.get(key) if aggregate is None: aggregate_dict[key] = [amount, amount.getQuantity()] - result_list.append(amount) else: aggregate[1] += amount.getQuantity() for amount, quantity in aggregate_dict.itervalues(): # Before we ignore 'quantity==0' amount here for better performance, # but it is not a good idea, especially when the first expand causes # non-zero quantity and then quantity becomes zero. - amount._setQuantity(quantity) + aggregate = newTempAmount(amount.aq_parent, '', notify_workflow=False) + result_list.append(aggregate) + aggregate.__dict__.update(amount.__dict__) + aggregate._setQuantity(quantity) + del aggregate._base return result_list + def split(self): + """Return a dictionary with all amounts grouped by base amount + + Return {amount: amount_list} where + - amount is the Amount instance (e.g. movement, delivery) + that generated amounts + - amount_list is an instance of this class + + This is the opposite of aggregate(), which merges amounts that only differ + by their base amounts. + """ + result = defaultdict(self.__class__) + for amount in self: + result[amount._base].append(amount) + return result + allow_class(GeneratedAmountList) diff --git a/product/ERP5/mixin/amount_generator.py b/product/ERP5/mixin/amount_generator.py index 6605bc55ea1a3649f2c4e29f4cbb8fb4f1111db3..c258cd509d2f129a5abb7360db3aaf675f085c16 100644 --- a/product/ERP5/mixin/amount_generator.py +++ b/product/ERP5/mixin/amount_generator.py @@ -474,6 +474,7 @@ class AmountGeneratorMixin: property_dict['causality_value_list'][-1] .getRelativeUrl().replace('/', '_'), notify_workflow=False) + amount._base = delivery_amount amount._setCategoryList(property_dict.pop('category_list', ())) if amount.getQuantityUnit(): del property_dict['quantity_unit']