Cosmetics
This commit is contained in:
parent
f83571afc9
commit
23d62c2d77
13
TODO.md
13
TODO.md
|
@ -131,15 +131,10 @@ Remember that, as always with QuerySets, any subsequent chained methods which im
|
||||||
|
|
||||||
* AccountAdminMixin auto adds 'account__name' on searchfields and handle account_link on fieldsets
|
* AccountAdminMixin auto adds 'account__name' on searchfields and handle account_link on fieldsets
|
||||||
|
|
||||||
* account defiition:
|
* Separate panel from server passwords? Store passwords on panel?
|
||||||
* identify a customer or a person
|
|
||||||
* has one main system user for running website
|
|
||||||
* pangea staff are different accounts
|
|
||||||
* An account identify a person
|
|
||||||
* Maybe merge users into accounts? again. Account contains main_users, users contains FTP shit
|
|
||||||
* Separate panel from server passwords?
|
|
||||||
* Store passwords on panel?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
* What fields we really need on contacts? name email phone and what more?
|
* What fields we really need on contacts? name email phone and what more?
|
||||||
|
|
||||||
|
|
||||||
|
* Redirect junk emails and delete every 30 days?
|
||||||
|
|
|
@ -30,10 +30,7 @@ class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin):
|
||||||
'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'),
|
'fields': ('first_name', 'last_name', 'email', ('type', 'language'), 'comments'),
|
||||||
}),
|
}),
|
||||||
(_("Permissions"), {
|
(_("Permissions"), {
|
||||||
'fields': ('is_superuser', 'is_active')
|
'fields': ('is_superuser',)
|
||||||
}),
|
|
||||||
(_("Important dates"), {
|
|
||||||
'fields': ('last_login', 'date_joined')
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
@ -47,6 +44,7 @@ class AccountAdmin(auth.UserAdmin, ExtendedModelAdmin):
|
||||||
'fields': ('is_superuser', 'is_active')
|
'fields': ('is_superuser', 'is_active')
|
||||||
}),
|
}),
|
||||||
(_("Important dates"), {
|
(_("Important dates"), {
|
||||||
|
'classes': ('collapse',),
|
||||||
'fields': ('last_login', 'date_joined')
|
'fields': ('last_login', 'date_joined')
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
from optparse import make_option
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
|
||||||
from django.db import transaction
|
|
||||||
|
|
||||||
from orchestra.apps.accounts.models import Account
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(Command, self).__init__(*args, **kwargs)
|
|
||||||
self.option_list = BaseCommand.option_list + (
|
|
||||||
make_option('--noinput', action='store_false', dest='interactive',
|
|
||||||
default=True),
|
|
||||||
make_option('--username', action='store', dest='username'),
|
|
||||||
make_option('--password', action='store', dest='password'),
|
|
||||||
make_option('--email', action='store', dest='email'),
|
|
||||||
)
|
|
||||||
|
|
||||||
option_list = BaseCommand.option_list
|
|
||||||
help = 'Used to create an initial account.'
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
interactive = options.get('interactive')
|
|
||||||
if not interactive:
|
|
||||||
email = options.get('email')
|
|
||||||
username = options.get('username')
|
|
||||||
password = options.get('password')
|
|
||||||
account = Account.objects.create(name=username)
|
|
||||||
account.main_user = account.users.create_superuser(username, email, password, account=account, is_main=True)
|
|
||||||
account.save()
|
|
|
@ -1,15 +0,0 @@
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.contrib.auth.management.commands import createsuperuser
|
|
||||||
|
|
||||||
from orchestra.apps.accounts.models import Account
|
|
||||||
|
|
||||||
|
|
||||||
class Command(createsuperuser.Command):
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
super(Command, self).handle(*args, **options)
|
|
||||||
raise NotImplementedError
|
|
||||||
users = get_user_model().objects.filter()
|
|
||||||
if len(users) == 1 and not Account.objects.all().exists():
|
|
||||||
user = users[0]
|
|
||||||
user.account = Account.objects.create(user=user)
|
|
||||||
user.save()
|
|
|
@ -3,8 +3,11 @@ import zipfile
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.http import HttpResponse
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.http import HttpResponse, HttpResponseServerError
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin.forms import adminmodelformset_factory
|
from orchestra.admin.forms import adminmodelformset_factory
|
||||||
|
@ -13,6 +16,26 @@ from orchestra.utils.html import html_to_pdf
|
||||||
|
|
||||||
from .forms import SelectSourceForm
|
from .forms import SelectSourceForm
|
||||||
|
|
||||||
|
def validate_contact(bill):
|
||||||
|
""" checks if all the preconditions for bill generation are met """
|
||||||
|
msg = ''
|
||||||
|
if not hasattr(bill.account, 'invoicecontact'):
|
||||||
|
account = force_text(bill.account)
|
||||||
|
link = reverse('admin:accounts_account_change', args=(bill.account_id,))
|
||||||
|
link += '#invoicecontact-group'
|
||||||
|
msg += _('Related account "%s" doesn\'t have a declared invoice contact\n') % account
|
||||||
|
msg += _('You should <a href="%s">provide</a> one') % link
|
||||||
|
main = type(bill).account.field.rel.to.get_main()
|
||||||
|
if not hasattr(main, 'invoicecontact'):
|
||||||
|
account = force_text(main)
|
||||||
|
link = reverse('admin:accounts_account_change', args=(main.id,))
|
||||||
|
link += '#invoicecontact-group'
|
||||||
|
msg += _('Main account "%s" doesn\'t have a declared invoice contact\n') % account
|
||||||
|
msg += _('You should <a href="%s">provide</a> one') % link
|
||||||
|
if msg:
|
||||||
|
# TODO custom template
|
||||||
|
return HttpResponseServerError(mark_safe(msg))
|
||||||
|
|
||||||
|
|
||||||
def download_bills(modeladmin, request, queryset):
|
def download_bills(modeladmin, request, queryset):
|
||||||
if queryset.count() > 1:
|
if queryset.count() > 1:
|
||||||
|
@ -35,6 +58,9 @@ download_bills.url_name = 'download'
|
||||||
|
|
||||||
def view_bill(modeladmin, request, queryset):
|
def view_bill(modeladmin, request, queryset):
|
||||||
bill = queryset.get()
|
bill = queryset.get()
|
||||||
|
error = validate_contact(bill)
|
||||||
|
if error:
|
||||||
|
return error
|
||||||
html = bill.html or bill.render()
|
html = bill.html or bill.render()
|
||||||
return HttpResponse(html)
|
return HttpResponse(html)
|
||||||
view_bill.verbose_name = _("View")
|
view_bill.verbose_name = _("View")
|
||||||
|
@ -46,6 +72,10 @@ def close_bills(modeladmin, request, queryset):
|
||||||
if not queryset:
|
if not queryset:
|
||||||
messages.warning(request, _("Selected bills should be in open state"))
|
messages.warning(request, _("Selected bills should be in open state"))
|
||||||
return
|
return
|
||||||
|
for bill in queryset:
|
||||||
|
error = validate_contact(bill)
|
||||||
|
if error:
|
||||||
|
return error
|
||||||
SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm, extra=0)
|
SelectSourceFormSet = adminmodelformset_factory(modeladmin, SelectSourceForm, extra=0)
|
||||||
formset = SelectSourceFormSet(queryset=queryset)
|
formset = SelectSourceFormSet(queryset=queryset)
|
||||||
if request.POST.get('post') == 'generic_confirmation':
|
if request.POST.get('post') == 'generic_confirmation':
|
||||||
|
@ -79,6 +109,10 @@ close_bills.url_name = 'close'
|
||||||
|
|
||||||
|
|
||||||
def send_bills(modeladmin, request, queryset):
|
def send_bills(modeladmin, request, queryset):
|
||||||
|
for bill in queryset:
|
||||||
|
error = validate_contact(bill)
|
||||||
|
if error:
|
||||||
|
return error
|
||||||
for bill in queryset:
|
for bill in queryset:
|
||||||
bill.send()
|
bill.send()
|
||||||
modeladmin.log_change(request, bill, 'Sent')
|
modeladmin.log_change(request, bill, 'Sent')
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin, messages
|
||||||
|
from django.contrib.admin.utils import unquote
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
|
@ -11,8 +13,7 @@ from orchestra.apps.accounts.admin import AccountAdminMixin
|
||||||
from . import settings
|
from . import settings
|
||||||
from .actions import download_bills, view_bill, close_bills, send_bills
|
from .actions import download_bills, view_bill, close_bills, send_bills
|
||||||
from .filters import BillTypeListFilter
|
from .filters import BillTypeListFilter
|
||||||
from .models import (Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, ProForma,
|
from .models import Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, ProForma, BillLine
|
||||||
BillLine)
|
|
||||||
|
|
||||||
|
|
||||||
PAYMENT_STATE_COLORS = {
|
PAYMENT_STATE_COLORS = {
|
||||||
|
@ -144,14 +145,18 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
qs = qs.annotate(models.Count('lines'))
|
qs = qs.annotate(models.Count('lines'))
|
||||||
qs = qs.prefetch_related('lines', 'lines__sublines')
|
qs = qs.prefetch_related('lines', 'lines__sublines')
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
# def change_view(self, request, object_id, **kwargs):
|
def change_view(self, request, object_id, **kwargs):
|
||||||
# opts = self.model._meta
|
bill = self.get_object(request, unquote(object_id))
|
||||||
# if opts.module_name == 'bill':
|
# TODO raise404, here and everywhere
|
||||||
# obj = self.get_object(request, unquote(object_id))
|
if not hasattr(bill.account, 'invoicecontact'):
|
||||||
# return redirect(
|
create_link = reverse('admin:accounts_account_change', args=(bill.account_id,))
|
||||||
# reverse('admin:bills_%s_change' % obj.type.lower(), args=[obj.pk]))
|
create_link += '#invoicecontact-group'
|
||||||
# return super(BillAdmin, self).change_view(request, object_id, **kwargs)
|
messages.warning(request, mark_safe(_(
|
||||||
|
'Be aware, related contact doesn\'t have a billing contact defined, '
|
||||||
|
'bill can not be generated until one is <a href="%s">provided</a>' % create_link
|
||||||
|
)))
|
||||||
|
return super(BillAdmin, self).change_view(request, object_id, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Bill, BillAdmin)
|
admin.site.register(Bill, BillAdmin)
|
||||||
|
|
|
@ -144,6 +144,7 @@ class Bill(models.Model):
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
|
html = self.html or self.render()
|
||||||
self.account.send_email(
|
self.account.send_email(
|
||||||
template=settings.BILLS_EMAIL_NOTIFICATION_TEMPLATE,
|
template=settings.BILLS_EMAIL_NOTIFICATION_TEMPLATE,
|
||||||
context={
|
context={
|
||||||
|
@ -151,7 +152,7 @@ class Bill(models.Model):
|
||||||
},
|
},
|
||||||
contacts=(Contact.BILLING,),
|
contacts=(Contact.BILLING,),
|
||||||
attachments=[
|
attachments=[
|
||||||
('%s.pdf' % self.number, html_to_pdf(self.html), 'application/pdf')
|
('%s.pdf' % self.number, html_to_pdf(html), 'application/pdf')
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
self.is_sent = True
|
self.is_sent = True
|
||||||
|
|
|
@ -96,11 +96,7 @@ class ContactInline(InvoiceContactInline):
|
||||||
|
|
||||||
|
|
||||||
def has_invoice(account):
|
def has_invoice(account):
|
||||||
try:
|
return hasattr(account, 'invoicecontact')
|
||||||
account.invoicecontact
|
|
||||||
except InvoiceContact.DoesNotExist:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
has_invoice.boolean = True
|
has_invoice.boolean = True
|
||||||
has_invoice.admin_order_field = 'invoicecontact'
|
has_invoice.admin_order_field = 'invoicecontact'
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,9 @@ class InvoiceContact(models.Model):
|
||||||
country = models.CharField(_("country"), max_length=20,
|
country = models.CharField(_("country"), max_length=20,
|
||||||
default=settings.CONTACTS_DEFAULT_COUNTRY)
|
default=settings.CONTACTS_DEFAULT_COUNTRY)
|
||||||
vat = models.CharField(_("VAT number"), max_length=64)
|
vat = models.CharField(_("VAT number"), max_length=64)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
accounts.register(Contact)
|
accounts.register(Contact)
|
||||||
|
|
|
@ -13,7 +13,7 @@ from .models import Address
|
||||||
class MailSystemUserBackend(ServiceController):
|
class MailSystemUserBackend(ServiceController):
|
||||||
verbose_name = _("Mail system user")
|
verbose_name = _("Mail system user")
|
||||||
model = 'mails.Mailbox'
|
model = 'mails.Mailbox'
|
||||||
# TODO related_models = ('resources__content_type') ??
|
# TODO related_models = ('resources__content_type') ?? needed for updating disk usage from resource.data
|
||||||
|
|
||||||
DEFAULT_GROUP = 'postfix'
|
DEFAULT_GROUP = 'postfix'
|
||||||
|
|
||||||
|
@ -35,16 +35,33 @@ class MailSystemUserBackend(ServiceController):
|
||||||
"# Sieve Filter\n"
|
"# Sieve Filter\n"
|
||||||
"# Generated by Orchestra %s\n\n" % now
|
"# Generated by Orchestra %s\n\n" % now
|
||||||
)
|
)
|
||||||
if mailbox.use_custom_filtering:
|
if mailbox.custom_filtering:
|
||||||
context['filtering'] += mailbox.custom_filtering
|
context['filtering'] += mailbox.custom_filtering
|
||||||
else:
|
else:
|
||||||
context['filtering'] += settings.EMAILS_DEFAUL_FILTERING
|
context['filtering'] += settings.EMAILS_DEFAUL_FILTERING
|
||||||
context['filter_path'] = os.path.join(context['home'], '.orchestra.sieve')
|
context['filter_path'] = os.path.join(context['home'], '.orchestra.sieve')
|
||||||
self.append("echo '%(filtering)s' > %(filter_path)s" % context)
|
self.append("echo '%(filtering)s' > %(filter_path)s" % context)
|
||||||
|
|
||||||
|
def set_quota(self, mailbox, context):
|
||||||
|
if not hasattr(mailbox, 'resources'):
|
||||||
|
return
|
||||||
|
context.update({
|
||||||
|
'maildir_path': '~%(username)s/Maildir' % context,
|
||||||
|
'maildirsize_path': '~%(username)s/Maildir/maildirsize' % context,
|
||||||
|
'quota': mailbox.resources.disk.allocated*1000*1000,
|
||||||
|
})
|
||||||
|
self.append("mkdir -p %(maildir_path)s" % context)
|
||||||
|
self.append(
|
||||||
|
"sed -i '1s/.*/%(quota)s,S/' %(maildirsize_path)s || {"
|
||||||
|
" echo '%(quota)s,S' > %(maildirsize_path)s && "
|
||||||
|
" chown %(username)s %(maildirsize_path)s;"
|
||||||
|
"}" % context
|
||||||
|
)
|
||||||
|
|
||||||
def save(self, mailbox):
|
def save(self, mailbox):
|
||||||
context = self.get_context(mailbox)
|
context = self.get_context(mailbox)
|
||||||
self.create_user(context)
|
self.create_user(context)
|
||||||
|
self.set_quota(mailbox, context)
|
||||||
self.generate_filter(mailbox, context)
|
self.generate_filter(mailbox, context)
|
||||||
|
|
||||||
def delete(self, mailbox):
|
def delete(self, mailbox):
|
||||||
|
@ -56,7 +73,7 @@ class MailSystemUserBackend(ServiceController):
|
||||||
|
|
||||||
def get_context(self, mailbox):
|
def get_context(self, mailbox):
|
||||||
context = {
|
context = {
|
||||||
'name': mailbox.nam,
|
'name': mailbox.name,
|
||||||
'username': mailbox.name,
|
'username': mailbox.name,
|
||||||
'password': mailbox.password if mailbox.is_active else '*%s' % mailbox.password,
|
'password': mailbox.password if mailbox.is_active else '*%s' % mailbox.password,
|
||||||
'group': self.DEFAULT_GROUP
|
'group': self.DEFAULT_GROUP
|
||||||
|
@ -155,6 +172,6 @@ class MaildirDisk(ServiceMonitor):
|
||||||
def get_context(self, mailbox):
|
def get_context(self, mailbox):
|
||||||
context = MailSystemUserBackend().get_context(mailbox)
|
context = MailSystemUserBackend().get_context(mailbox)
|
||||||
context['home'] = settings.EMAILS_HOME % context
|
context['home'] = settings.EMAILS_HOME % context
|
||||||
context['maildir_path'] = os.path.join(context['home'], 'Maildir/maildirsize')
|
context['rr_path'] = os.path.join(context['home'], 'Maildir/maildirsize')
|
||||||
context['object_id'] = mailbox.pk
|
context['object_id'] = mailbox.pk
|
||||||
return context
|
return context
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.core import services
|
from orchestra.core import services
|
||||||
|
@ -30,6 +31,10 @@ class Mailbox(models.Model):
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def active(self):
|
||||||
|
return self.is_active and self.account.is_active
|
||||||
|
|
||||||
|
|
||||||
class Address(models.Model):
|
class Address(models.Model):
|
||||||
|
|
|
@ -68,7 +68,7 @@ class BackendLog(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def execution_time(self):
|
def execution_time(self):
|
||||||
return (self.last_update-self.created).total_seconds()
|
return (self.updated_at-self.created_at).total_seconds()
|
||||||
|
|
||||||
def backend_class(self):
|
def backend_class(self):
|
||||||
return ServiceBackend.get_backend(self.backend)
|
return ServiceBackend.get_backend(self.backend)
|
||||||
|
|
|
@ -29,7 +29,7 @@ class OrderAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||||
def display_billed_until(self, order):
|
def display_billed_until(self, order):
|
||||||
value = order.billed_until
|
value = order.billed_until
|
||||||
color = ''
|
color = ''
|
||||||
if value and value < timezone.now():
|
if value and value < timezone.now().date():
|
||||||
color = 'style="color:red;"'
|
color = 'style="color:red;"'
|
||||||
return '<span title="{raw}" {color}>{human}</span>'.format(
|
return '<span title="{raw}" {color}>{human}</span>'.format(
|
||||||
raw=escape(str(value)), color=color, human=escape(naturaldate(value)),
|
raw=escape(str(value)), color=color, human=escape(naturaldate(value)),
|
||||||
|
|
|
@ -15,12 +15,12 @@ class BillSelectedOptionsForm(AdminFormMixin, forms.Form):
|
||||||
label=_("Billing point"), widget=widgets.AdminDateWidget,
|
label=_("Billing point"), widget=widgets.AdminDateWidget,
|
||||||
help_text=_("Date you want to bill selected orders"))
|
help_text=_("Date you want to bill selected orders"))
|
||||||
fixed_point = forms.BooleanField(initial=False, required=False,
|
fixed_point = forms.BooleanField(initial=False, required=False,
|
||||||
label=_("fixed point"),
|
label=_("Fixed point"),
|
||||||
help_text=_("Deisgnates whether you want the billing point to be an "
|
help_text=_("Deisgnates whether you want the billing point to be an "
|
||||||
"exact date, or adapt it to the billing period."))
|
"exact date, or adapt it to the billing period."))
|
||||||
is_proforma = forms.BooleanField(initial=False, required=False,
|
is_proforma = forms.BooleanField(initial=False, required=False,
|
||||||
label=_("Pro-forma, billing simulation"),
|
label=_("Pro-forma (billing simulation)"),
|
||||||
help_text=_("O."))
|
help_text=_("Creates a Pro Forma instead of billing the orders."))
|
||||||
new_open = forms.BooleanField(initial=False, required=False,
|
new_open = forms.BooleanField(initial=False, required=False,
|
||||||
label=_("Create a new open bill"),
|
label=_("Create a new open bill"),
|
||||||
help_text=_("Deisgnates whether you want to put this orders on a new "
|
help_text=_("Deisgnates whether you want to put this orders on a new "
|
||||||
|
|
|
@ -58,6 +58,26 @@ class OrderQuerySet(models.QuerySet):
|
||||||
return self.exclude(**qs)
|
return self.exclude(**qs)
|
||||||
return self.filter(**qs)
|
return self.filter(**qs)
|
||||||
|
|
||||||
|
def get_related(self, **options):
|
||||||
|
Service = get_model(settings.ORDERS_SERVICE_MODEL)
|
||||||
|
conflictive = self.filter(service__metric='')
|
||||||
|
conflictive = conflictive.exclude(service__billing_period=Service.NEVER)
|
||||||
|
conflictive = conflictive.select_related('service').group_by('account_id', 'service')
|
||||||
|
qs = Q()
|
||||||
|
for account_id, services in conflictive.iteritems():
|
||||||
|
for service, orders in services.iteritems():
|
||||||
|
end = datetime.date.min
|
||||||
|
bp = None
|
||||||
|
for order in orders:
|
||||||
|
bp = service.handler.get_billing_point(order, **options)
|
||||||
|
end = max(end, bp)
|
||||||
|
qs = qs | Q(
|
||||||
|
Q(service=service, account=account_id, registered_on__lt=end) &
|
||||||
|
Q(Q(billed_until__isnull=True) | Q(billed_until__lt=end))
|
||||||
|
)
|
||||||
|
ids = self.values_list('id', flat=True)
|
||||||
|
return self.model.objects.filter(qs).exclude(id__in=ids)
|
||||||
|
|
||||||
def pricing_orders(self, ini, end):
|
def pricing_orders(self, ini, end):
|
||||||
return self.filter(billed_until__isnull=False, billed_until__gt=ini,
|
return self.filter(billed_until__isnull=False, billed_until__gt=ini,
|
||||||
registered_on__lt=end)
|
registered_on__lt=end)
|
||||||
|
@ -103,7 +123,7 @@ class Order(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_orders(cls, instance, service=None):
|
def update_orders(cls, instance, service=None):
|
||||||
if service is None:
|
if service is None:
|
||||||
Service = get_model(*settings.ORDERS_SERVICE_MODEL.split('.'))
|
Service = get_model(settings.ORDERS_SERVICE_MODEL)
|
||||||
services = Service.get_services(instance)
|
services = Service.get_services(instance)
|
||||||
else:
|
else:
|
||||||
services = [service]
|
services = [service]
|
||||||
|
|
|
@ -14,7 +14,7 @@ def monitor(resource_id):
|
||||||
# Execute monitors
|
# Execute monitors
|
||||||
for monitor_name in resource.monitors:
|
for monitor_name in resource.monitors:
|
||||||
backend = ServiceMonitor.get_backend(monitor_name)
|
backend = ServiceMonitor.get_backend(monitor_name)
|
||||||
model = get_model(*backend.model.split('.'))
|
model = get_model(backend.model)
|
||||||
operations = []
|
operations = []
|
||||||
# Execute monitor
|
# Execute monitor
|
||||||
for obj in model.objects.all():
|
for obj in model.objects.all():
|
||||||
|
|
|
@ -14,7 +14,7 @@ from .models import SystemUser
|
||||||
|
|
||||||
class SystemUserAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
class SystemUserAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_display = ('username', 'account_link', 'shell', 'home', 'is_active',)
|
list_display = ('username', 'account_link', 'shell', 'home', 'is_active',)
|
||||||
list_filter = ('is_active',)
|
list_filter = ('is_active', 'shell')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {
|
(None, {
|
||||||
'fields': ('username', 'password', 'account_link', 'is_active')
|
'fields': ('username', 'password', 'account_link', 'is_active')
|
||||||
|
|
Loading…
Reference in New Issue