Added preliminar implementation of admin billing
This commit is contained in:
parent
06db4cd346
commit
b88689864f
7
TODO.md
7
TODO.md
|
@ -64,6 +64,11 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
dependency collector with max_recursion that matches the number of dots on service.match and service.metric
|
dependency collector with max_recursion that matches the number of dots on service.match and service.metric
|
||||||
|
|
||||||
|
|
||||||
* Be consistent with dates: name_on, created ?
|
* Be consistent with dates:
|
||||||
|
* created_on date
|
||||||
|
* created_at datetime
|
||||||
|
|
||||||
|
at + clock time, midnight, noon- At 3:30 p.m., At 4:01, At noon
|
||||||
|
|
||||||
|
|
||||||
* backend logs with hal logo
|
* backend logs with hal logo
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
|
||||||
def admin_field(method):
|
def admin_field(method):
|
||||||
|
""" Wraps a function to be used as a ModelAdmin method field """
|
||||||
def admin_field_wrapper(*args, **kwargs):
|
def admin_field_wrapper(*args, **kwargs):
|
||||||
""" utility function for creating admin links """
|
""" utility function for creating admin links """
|
||||||
kwargs['field'] = args[0] if args else ''
|
kwargs['field'] = args[0] if args else ''
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
from django.template import Template, Context
|
||||||
|
from django.contrib.admin.helpers import AdminForm
|
||||||
|
|
||||||
|
|
||||||
|
class AdminFormMixin(object):
|
||||||
|
""" Provides a method for rendering a form just like in Django Admin """
|
||||||
|
def as_admin(self):
|
||||||
|
prepopulated_fields = {}
|
||||||
|
fieldsets = [
|
||||||
|
(None, {'fields': self.fields.keys()})
|
||||||
|
]
|
||||||
|
adminform = AdminForm(self, fieldsets, prepopulated_fields)
|
||||||
|
template = Template(
|
||||||
|
'{% for fieldset in adminform %}'
|
||||||
|
'{% include "admin/includes/fieldset.html" %}'
|
||||||
|
'{% endfor %}'
|
||||||
|
)
|
||||||
|
return template.render(Context({'adminform': adminform}))
|
|
@ -0,0 +1,90 @@
|
||||||
|
from django.contrib import admin, messages
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from .forms import (BillSelectedOptionsForm, BillSelectConfirmationForm,
|
||||||
|
BillSelectRelatedForm)
|
||||||
|
|
||||||
|
|
||||||
|
class BillSelectedOrders(object):
|
||||||
|
""" Form wizard for billing orders admin action """
|
||||||
|
short_description = _("Bill selected orders")
|
||||||
|
template = 'admin/orders/order/bill_selected_options.html'
|
||||||
|
__name__ = 'bill_selected_orders'
|
||||||
|
|
||||||
|
def __call__(self, modeladmin, request, queryset):
|
||||||
|
""" make this monster behave like a function """
|
||||||
|
self.modeladmin = modeladmin
|
||||||
|
self.queryset = queryset
|
||||||
|
opts = modeladmin.model._meta
|
||||||
|
app_label = opts.app_label
|
||||||
|
self.context = {
|
||||||
|
'opts': opts,
|
||||||
|
'app_label': app_label,
|
||||||
|
'queryset': queryset,
|
||||||
|
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
|
||||||
|
}
|
||||||
|
return self.set_options(request)
|
||||||
|
|
||||||
|
def set_options(self, request):
|
||||||
|
form = BillSelectedOptionsForm()
|
||||||
|
if request.POST.get('step'):
|
||||||
|
form = BillSelectedOptionsForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
self.options = dict(
|
||||||
|
billing_point=form.cleaned_data['billing_point'],
|
||||||
|
fixed_point=form.cleaned_data['fixed_point'],
|
||||||
|
create_new_open=form.cleaned_data['create_new_open'],
|
||||||
|
)
|
||||||
|
return self.select_related(request)
|
||||||
|
self.context.update({
|
||||||
|
'title': _("Options for billing selected orders, step 1 / 3"),
|
||||||
|
'step': 'one',
|
||||||
|
'form': form,
|
||||||
|
})
|
||||||
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
|
def select_related(self, request):
|
||||||
|
self.options['related_queryset'] = self.queryset.all() #get_related(**options)
|
||||||
|
form = BillSelectRelatedForm(initial=self.options)
|
||||||
|
if request.POST.get('step') == 'two':
|
||||||
|
form = BillSelectRelatedForm(request.POST, initial=self.options)
|
||||||
|
if form.is_valid():
|
||||||
|
select_related = form.cleaned_data['selected_related']
|
||||||
|
self.options['selected_related'] = select_related
|
||||||
|
return self.confirmation(request)
|
||||||
|
self.context.update({
|
||||||
|
'title': _("Select related order for billing, step 2 / 3"),
|
||||||
|
'step': 'two',
|
||||||
|
'form': form,
|
||||||
|
})
|
||||||
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
|
def confirmation(self, request):
|
||||||
|
form = BillSelectConfirmationForm(initial=self.options)
|
||||||
|
if request.POST:
|
||||||
|
bills = Order.bill(queryset, commit=True, **self.options)
|
||||||
|
if not bills:
|
||||||
|
msg = _("Selected orders do not have pending billing")
|
||||||
|
self.modeladmin.message_user(request, msg, messages.WARNING)
|
||||||
|
else:
|
||||||
|
ids = ','.join([bill.id for bill in bills])
|
||||||
|
url = reverse('admin:bills_bill_changelist')
|
||||||
|
context = {
|
||||||
|
'url': url + '?id=%s' % ids,
|
||||||
|
'num': len(bills),
|
||||||
|
'bills': _("bills"),
|
||||||
|
'msg': _("have been generated"),
|
||||||
|
}
|
||||||
|
msg = '<a href="%(url)s">%(num)s %(bills)s</a> %(msg)s' % context
|
||||||
|
msg = mark_safe(msg)
|
||||||
|
self.modeladmin.message_user(request, msg, messages.INFO)
|
||||||
|
return
|
||||||
|
self.context.update({
|
||||||
|
'title': _("Confirmation for billing selected orders"),
|
||||||
|
'step': 'three',
|
||||||
|
'form': form,
|
||||||
|
})
|
||||||
|
return render(request, self.template, self.context)
|
|
@ -11,6 +11,7 @@ from orchestra.admin.utils import admin_link, admin_date
|
||||||
from orchestra.apps.accounts.admin import AccountAdminMixin
|
from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from orchestra.core import services
|
from orchestra.core import services
|
||||||
|
|
||||||
|
from .actions import BillSelectedOrders
|
||||||
from .filters import ActiveOrderListFilter
|
from .filters import ActiveOrderListFilter
|
||||||
from .models import Service, Order, MetricStorage
|
from .models import Service, Order, MetricStorage
|
||||||
|
|
||||||
|
@ -81,6 +82,7 @@ class OrderAdmin(AccountAdminMixin, ChangeListDefaultFilter, admin.ModelAdmin):
|
||||||
)
|
)
|
||||||
list_display_link = ('id', 'service')
|
list_display_link = ('id', 'service')
|
||||||
list_filter = (ActiveOrderListFilter, 'service',)
|
list_filter = (ActiveOrderListFilter, 'service',)
|
||||||
|
actions = (BillSelectedOrders(),)
|
||||||
date_hierarchy = 'registered_on'
|
date_hierarchy = 'registered_on'
|
||||||
default_changelist_filters = (
|
default_changelist_filters = (
|
||||||
('is_active', 'True'),
|
('is_active', 'True'),
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
from django import forms
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orchestra.admin.forms import AdminFormMixin
|
||||||
|
|
||||||
|
from .models import Order
|
||||||
|
|
||||||
|
|
||||||
|
class BillSelectedOptionsForm(AdminFormMixin, forms.Form):
|
||||||
|
billing_point = forms.DateField(initial=timezone.now,
|
||||||
|
label=_("Billing point"),
|
||||||
|
help_text=_("Date you want to bill selected orders"))
|
||||||
|
fixed_point = forms.BooleanField(initial=False, required=False,
|
||||||
|
label=_("fixed point"),
|
||||||
|
help_text=_("Deisgnates whether you want the billing point to be an "
|
||||||
|
"exact date, or adapt it to the billing period."))
|
||||||
|
create_new_open = forms.BooleanField(initial=False, required=False,
|
||||||
|
label=_("Create a new open bill"),
|
||||||
|
help_text=_("Deisgnates whether you want to put this orders on a new "
|
||||||
|
"open bill, or allow to reuse an existing one."))
|
||||||
|
|
||||||
|
|
||||||
|
class BillSelectRelatedForm(AdminFormMixin, forms.Form):
|
||||||
|
selected_related = forms.ModelMultipleChoiceField(queryset=Order.objects.none(),
|
||||||
|
required=False)
|
||||||
|
billing_point = forms.DateField(widget=forms.HiddenInput())
|
||||||
|
fixed_point = forms.BooleanField(widget=forms.HiddenInput(), required=False)
|
||||||
|
create_new_open = forms.BooleanField(widget=forms.HiddenInput(), required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(BillSelectRelatedForm, self).__init__(*args, **kwargs)
|
||||||
|
queryset = kwargs['initial'].get('related_queryset', None)
|
||||||
|
if queryset:
|
||||||
|
self.fields['selected_related'].queryset = queryset
|
||||||
|
|
||||||
|
|
||||||
|
class BillSelectConfirmationForm(forms.Form):
|
||||||
|
selected_related = forms.ModelMultipleChoiceField(queryset=Order.objects.none(),
|
||||||
|
widget=forms.HiddenInput(), required=False)
|
||||||
|
billing_point = forms.DateField(widget=forms.HiddenInput())
|
||||||
|
fixed_point = forms.BooleanField(widget=forms.HiddenInput(), required=False)
|
||||||
|
create_new_open = forms.BooleanField(widget=forms.HiddenInput(), required=False)
|
|
@ -0,0 +1,36 @@
|
||||||
|
{% extends "admin/base_site.html" %}
|
||||||
|
{% load i18n l10n staticfiles admin_urls %}
|
||||||
|
|
||||||
|
{% block extrastyle %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||||
|
› <a href="{% url 'admin:app_list' app_label='orders' %}">Slices</a>
|
||||||
|
› <a href="{% url 'admin:orders_order_changelist' %}">Slices</a>
|
||||||
|
› {{ title }}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form action="" method="post">{% csrf_token %}
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<div style="margin:20px;">
|
||||||
|
{{ form.as_admin }}
|
||||||
|
</div>
|
||||||
|
{% for obj in queryset %}
|
||||||
|
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||||
|
{% endfor %}
|
||||||
|
<input type="hidden" name="action" value="bill_selected_orders" />
|
||||||
|
<input type="hidden" name="step" value="{{ step }}" />
|
||||||
|
<input type="submit" value="{% trans "Yes, create slivers" %}" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue