Skip to content

Commit

Permalink
Merge pull request #210 from mkurek/feature/venture-exclude-usagetype
Browse files Browse the repository at this point in the history
Feature/venture exclude usagetype
  • Loading branch information
kula1922 committed Jul 23, 2014
2 parents 59804f5 + a633acb commit 748fbf7
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/ralph_pricing/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ class VentureAdmin(ModelAdmin):
inlines = [ExtraCostInline]


class UsageTypeForm(forms.ModelForm):
class Meta:
model = models.UsageType
widgets = {
'excluded_ventures': FilteredSelectMultiple('Ventures', False)
}


class UsagePriceInline(admin.TabularInline):
model = models.UsagePrice

Expand All @@ -62,6 +70,7 @@ class UsageTypeAdmin(ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
inlines = [UsagePriceInline]
form = UsageTypeForm


@register(models.ExtraCostType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def backwards(self, orm):
'cache_version': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['account.Profile']", 'blank': 'True', 'null': 'True'}),
'excluded_ventures': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "u'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['ralph_pricing.Venture']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['account.Profile']", 'blank': 'True', 'null': 'True'}),
Expand Down Expand Up @@ -286,4 +287,4 @@ def backwards(self, orm):
}
}

complete_apps = ['ralph_pricing']
complete_apps = ['ralph_pricing']
294 changes: 294 additions & 0 deletions src/ralph_pricing/migrations/0038_auto.py

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/ralph_pricing/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,13 @@ class UsageType(db.Model):
verbose_name=_("Use universal plugin"),
default=True,
)
excluded_ventures = db.ManyToManyField(
'Venture',
verbose_name=_("Excluded ventures"),
related_name='excluded_usage_types',
blank=True,
null=True,
)

class Meta:
verbose_name = _("usage type")
Expand Down
18 changes: 17 additions & 1 deletion src/ralph_pricing/plugins/reports/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def _get_price_from_cost(
forecast,
warehouse=None,
ventures=None,
excluded_ventures=None,
):
"""
Calculate price for single unit of usage type in period of time defined
Expand All @@ -125,6 +126,7 @@ def _get_price_from_cost(
usage_price.type,
warehouse,
ventures,
excluded_ventures,
)
cost = usage_price.forecast_cost if forecast else usage_price.cost
price = 0
Expand All @@ -140,6 +142,7 @@ def _get_total_usage_in_period(
usage_type,
warehouse=None,
ventures=None,
excluded_ventures=None,
):
"""
Calculates total usage of usage type in period of time (between start
Expand All @@ -157,7 +160,10 @@ def _get_total_usage_in_period(
daily_usages = daily_usages.filter(warehouse=warehouse)
if ventures:
daily_usages = daily_usages.filter(pricing_venture__in=ventures)

if excluded_ventures:
daily_usages = daily_usages.exclude(
pricing_venture__in=excluded_ventures
)
return daily_usages.aggregate(
total=Sum('value')
).get('total') or 0
Expand All @@ -170,6 +176,7 @@ def _get_usages_in_period_per_venture(
usage_type,
warehouse=None,
ventures=None,
excluded_ventures=None,
):
"""
Method similar to `_get_total_usage_in_period`, but instead of
Expand All @@ -188,6 +195,10 @@ def _get_usages_in_period_per_venture(
daily_usages = daily_usages.filter(warehouse=warehouse)
if ventures:
daily_usages = daily_usages.filter(pricing_venture__in=ventures)
if excluded_ventures:
daily_usages = daily_usages.exclude(
pricing_venture__in=excluded_ventures
)
return list(daily_usages.values('pricing_venture').annotate(
usage=Sum('value'),
).order_by('pricing_venture'))
Expand All @@ -200,6 +211,7 @@ def _get_usages_in_period_per_device(
usage_type,
ventures=None,
warehouse=None,
excluded_ventures=None,
):
"""
Works almost exactly as `_get_usages_in_period_per_venture`, but
Expand All @@ -215,6 +227,10 @@ def _get_usages_in_period_per_device(
)
if ventures:
daily_usages = daily_usages.filter(pricing_venture__in=ventures)
if excluded_ventures:
daily_usages = daily_usages.exclude(
pricing_venture__in=excluded_ventures
)
if warehouse:
daily_usages = daily_usages.filter(warehouse=warehouse)
return list(daily_usages.values('pricing_device').annotate(
Expand Down
18 changes: 16 additions & 2 deletions src/ralph_pricing/plugins/reports/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ def _get_total_cost_by_warehouses(
warehouses = [None]
result = []
total_cost = D(0)

# remove from ventures ones that should not be taken into consideration
# when calculating costs for this usage type (and should not be counted
# to total usages count)
if usage_type.excluded_ventures.count():
ventures = list(
set(ventures) - set(usage_type.excluded_ventures.all())
)

for warehouse in warehouses:
usage_in_warehouse = 0
cost_in_warehouse = 0
Expand Down Expand Up @@ -109,7 +118,8 @@ def _get_total_cost_by_warehouses(
price = self._get_price_from_cost(
usage_price,
forecast,
warehouse
warehouse,
excluded_ventures=usage_type.excluded_ventures.all(),
)
else:
if forecast:
Expand Down Expand Up @@ -158,6 +168,7 @@ def _get_usages_per_warehouse(
price for period of time in undefined of partially defined (incomplete)
cost will be message what's wrong with price (i.e. 'Incomplete price').
"""
excluded_ventures = usage_type.excluded_ventures.all()
total_days = (end - start).days + 1 # total report days
if usage_type.by_warehouse:
warehouses = self.get_warehouses()
Expand All @@ -178,6 +189,7 @@ def add_usages_per_venture(
usage_type=usage_type,
warehouse=warehouse,
ventures=ventures,
excluded_ventures=excluded_ventures,
)
for v in usages_per_venture:
venture = v['pricing_venture']
Expand All @@ -203,6 +215,7 @@ def add_usages_per_device(
usage_type=usage_type,
ventures=ventures,
warehouse=warehouse,
excluded_ventures=excluded_ventures,
)
for v in usages_per_device:
device = v['pricing_device']
Expand Down Expand Up @@ -270,7 +283,8 @@ def add_usages_per_device(
price = self._get_price_from_cost(
usage_price,
forecast,
warehouse
warehouse,
excluded_ventures=excluded_ventures,
)

up_start = max(start, usage_price.start)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def test_get_price_from_cost(self, get_total_usage_in_period_mock):
self.usage_type_cost_wh,
None,
None,
None,
)

@mock.patch('ralph_pricing.plugins.reports.base.BaseReportPlugin._get_total_usage_in_period') # noqa
Expand Down Expand Up @@ -197,6 +198,7 @@ def test_get_price_from_cost_with_warehouse(
self.usage_type_cost_wh,
self.warehouse1,
None,
None,
)

@mock.patch('ralph_pricing.plugins.reports.base.BaseReportPlugin._get_total_usage_in_period') # noqa
Expand All @@ -220,6 +222,7 @@ def test_get_price_from_cost_with_forecast(
self.usage_type_cost_wh,
None,
None,
None
)

@mock.patch('ralph_pricing.plugins.reports.base.BaseReportPlugin._get_total_usage_in_period') # noqa
Expand All @@ -238,6 +241,34 @@ def test_get_price_from_cost_total_usage_0(

self.assertEquals(result, D(0))

@mock.patch('ralph_pricing.plugins.reports.base.BaseReportPlugin._get_total_usage_in_period') # noqa
def test_get_price_from_cost_total_excluded_ventures(
self,
get_total_usage_in_period_mock
):
get_total_usage_in_period_mock.return_value = 10.0
usage_price = models.UsagePrice(
start=datetime.date(2013, 10, 10),
end=datetime.date(2013, 10, 10),
cost=3000,
type=self.usage_type_cost_wh,
)
result = self.plugin._get_price_from_cost(
usage_price,
False,
excluded_ventures=[self.venture1],
)

self.assertEquals(result, D(300))
get_total_usage_in_period_mock.assert_called_with(
datetime.date(2013, 10, 10),
datetime.date(2013, 10, 10),
self.usage_type_cost_wh,
None,
None,
[self.venture1]
)

# =========================================================================
# _get_total_usage_in_period
# =========================================================================
Expand Down Expand Up @@ -287,6 +318,20 @@ def test_get_total_usage_in_period_with_ventures_and_warehouse(self):
# +--- every odd day between 10 and 20 (inclusive)
self.assertEquals(result, 200.0)

def test_get_total_usage_in_period_with_excluded_ventures(self):
result = self.plugin._get_total_usage_in_period(
start=datetime.date(2013, 10, 10),
end=datetime.date(2013, 10, 20),
usage_type=self.usage_type_cost_wh,
warehouse=self.warehouse1,
excluded_ventures=[self.venture2],
)
# 5 * (20 + 60 + 80)= 800
# /^\
# |
# +--- every odd day between 10 and 20 (inclusive)
self.assertEquals(result, 800.0)

# =========================================================================
# _get_usages_in_period_per_venture
# =========================================================================
Expand Down Expand Up @@ -340,8 +385,22 @@ def test_get_usages_in_period_per_venture_with_warehouse_and_venture(self):
{'usage': 240.0, 'pricing_venture': 2} # 6 * 40 = 240
])

def test_get_usages_in_period_per_venture_with_excluded_ventures(self):
result = self.plugin._get_usages_in_period_per_venture(
start=datetime.date(2013, 10, 10),
end=datetime.date(2013, 10, 20),
usage_type=self.usage_type_cost_wh,
warehouse=self.warehouse1,
excluded_ventures=[self.venture3],
)
self.assertEquals(result, [
{'usage': 100.0, 'pricing_venture': 1}, # 5 * 20 = 100
{'usage': 200.0, 'pricing_venture': 2}, # 5 * 40 = 200
{'usage': 400.0, 'pricing_venture': 4}, # 5 * 80 = 400
])

# =========================================================================
# _get_usages_in_period_per_venture
# _get_usages_in_period_per_device
# =========================================================================
def _devices_sample(self):
self.device1 = utils.get_or_create_device()
Expand Down Expand Up @@ -410,6 +469,30 @@ def test_get_usages_in_period_per_device_with_warehouse(self):
}
])

def test_get_usages_in_period_per_device_excluded_ventures(self):
self._devices_sample()
result = self.plugin._get_usages_in_period_per_device(
start=datetime.date(2013, 10, 10),
end=datetime.date(2013, 10, 25),
usage_type=self.usage_type,
excluded_ventures=[
self.venture1,
self.venture2,
self.venture3,
self.venture4
],
)
self.assertEquals(result, [
{
'pricing_device': self.device1.id,
'usage': 110.0, # 11 (days) * 10 (daily usage)
},
{
'pricing_device': self.device2.id,
'usage': 220.0, # 11 (days) * 20 (daily usage)
}
])

# =========================================================================
# _distribute_costs
# =========================================================================
Expand Down

0 comments on commit 748fbf7

Please sign in to comment.