diff --git a/src/ralph_scrooge/plugins/cost/pricing_service.py b/src/ralph_scrooge/plugins/cost/pricing_service.py index 0dd151f2..f5fbad0c 100644 --- a/src/ralph_scrooge/plugins/cost/pricing_service.py +++ b/src/ralph_scrooge/plugins/cost/pricing_service.py @@ -237,17 +237,16 @@ def _distribute_costs( ... } """ - usages = defaultdict(list) - total_usages = [] - percentage = [] + usages = defaultdict(dict) + total_usages = {} + percentage = {} result = defaultdict(list) self.pricing_service = pricing_service - for service_usage_type in service_usage_types: service_excluded = excluded_services.union( service_usage_type.usage_type.excluded_services.all() ) - usages_per_po = self._get_usages_per_pricing_object( + usages_per_pricing_object = self._get_usages_per_pricing_object( usage_type=service_usage_type.usage_type, date=date, excluded_services=service_excluded, @@ -255,20 +254,33 @@ def _distribute_costs( 'daily_pricing_object__pricing_object', 'service_environment', ).annotate(usage=Sum('value')) - for pricing_object, se, usage in usages_per_po: - usages[(pricing_object, se)].append(usage) - - total_usages.append(self._get_total_usage( + usage_type_id = service_usage_type.usage_type_id + for ( + pricing_object, service_environment, usage + ) in usages_per_pricing_object: + usages[( + pricing_object, service_environment + )][usage_type_id] = usage + + total_usages[usage_type_id] = self._get_total_usage( usage_type=service_usage_type.usage_type, date=date, excluded_services=service_excluded, - )) - percentage.append(service_usage_type.percent) + ) + percentage[usage_type_id] = service_usage_type.percent # create hierarchy basing on usages - for (po, se), po_usages in usages.items(): - po_usages_info = zip(po_usages, total_usages, percentage) - result[se].extend( - self._add_hierarchy_costs(po, po_usages_info, costs_hierarchy) + for ( + (pricing_object, service_environment), + pricing_object_usages + ) in usages.items(): + pricing_object_usages_info = [ + (value, total_usages[usage_type_id], percentage[usage_type_id]) + for usage_type_id, value in pricing_object_usages.items() # noqa: F812,E501 + ] + result[service_environment].extend( + self._add_hierarchy_costs( + pricing_object, pricing_object_usages_info, costs_hierarchy + ) ) return result diff --git a/src/ralph_scrooge/tests/plugins/cost/test_pricing_service.py b/src/ralph_scrooge/tests/plugins/cost/test_pricing_service.py index ff8e202e..40f56b95 100644 --- a/src/ralph_scrooge/tests/plugins/cost/test_pricing_service.py +++ b/src/ralph_scrooge/tests/plugins/cost/test_pricing_service.py @@ -859,6 +859,72 @@ def test_distribute_costs_with_excluded_services(self): ]), ) + def test_distribute_costs_with_incomplete_multiple_usages(self): + self._init_one() + + # delete some daily usages + models.DailyUsage.objects.get( + daily_pricing_object=self.dpo1, + date=self.today, + type=self.service_usage_types[0] + ).delete() + models.DailyUsage.objects.get( + daily_pricing_object=self.dpo2, + date=self.today, + type=self.service_usage_types[1] + ).delete() + + # usages right now: + # | percent | dpo1 | dpo2 | dpo3 | total + # SU1 | 70% | - | 10 | 10 | 20 + # SU2 | 30% | 20 | - | 20 | 40 + + distributed_costs = PricingServicePlugin._distribute_costs( + date=self.today, + pricing_service=self.ps1, + costs_hierarchy={ + self.ps1.id: ( + D(1000), + { + self.service_usage_types[0].id: (D(300), {}), + self.service_usage_types[1].id: (D(700), {}), + } + ) + }, + service_usage_types=self.ps1.serviceusagetypes_set.all(), + excluded_services=set() + ) + + expected = { + self.dpo1.service_environment_id: [ + { + 'pricing_object_id': self.dpo1.pricing_object.id, + # 70% * 1000 * 0 / 20 + 30% * 1000 * 20 / 40 + 'cost': D('150.00'), + 'type_id': self.ps1.id, + 'value': 20, + } + ], + self.dpo2.service_environment_id: [ + { + 'pricing_object_id': self.dpo2.pricing_object.id, + # 70% * 1000 * 10 / 20 + 30% * 1000 * 0 / 40 + 'cost': D('350.00'), + 'value': 10, + 'type_id': self.ps1.id, + } + ], + self.dpo3.service_environment_id: [ + { + 'pricing_object_id': self.dpo3.pricing_object.id, + # 70% * 1000 * 10 / 20 + 30% * 1000 * 20 / 40 + 'cost': D('500.00'), + 'type_id': self.ps1.id, + } + ], + } + self.assertEqual(dict(distributed_costs), expected) + def test_pricing_dependent_services(self): """ Call costs for PS1, which dependent on PS2, which dependent of PS3.