You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
272 lines
12 KiB
Python
272 lines
12 KiB
Python
from django.contrib import admin, messages
|
|
from django.forms import Textarea
|
|
from django.http import HttpResponse, HttpResponseRedirect
|
|
from django.db.models import TextField
|
|
from django.urls import path, reverse
|
|
from functools import update_wrapper
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.shortcuts import render
|
|
|
|
from .models import Ledger, Statement, Receipt, Transaction, Bill, StatementSubmitted, StatementConfirmed,\
|
|
StatementUnSubmitted
|
|
|
|
|
|
@admin.register(Ledger)
|
|
class LedgerAdmin(admin.ModelAdmin):
|
|
search_fields = ('name', )
|
|
|
|
|
|
class BillOnStatementInline(admin.TabularInline):
|
|
model = Bill
|
|
extra = 0
|
|
sortable_options = []
|
|
fields = ['short_description', 'explanation', 'amount', 'paid_by', 'proof']
|
|
formfield_overrides = {
|
|
TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
|
|
}
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
if obj is not None and obj.submitted:
|
|
return self.fields
|
|
return super(BillOnStatementInline, self).get_readonly_fields(request, obj)
|
|
|
|
|
|
@admin.register(StatementUnSubmitted)
|
|
class StatementUnSubmittedAdmin(admin.ModelAdmin):
|
|
fields = ['short_description', 'explanation', 'excursion', 'submitted']
|
|
inlines = [BillOnStatementInline]
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
readonly_fields = ['submitted', 'excursion']
|
|
if obj is not None and obj.submitted:
|
|
return readonly_fields + self.fields
|
|
else:
|
|
return readonly_fields
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
|
|
def wrap(view):
|
|
def wrapper(*args, **kwargs):
|
|
return self.admin_site.admin_view(view)(*args, **kwargs)
|
|
|
|
wrapper.model_admin = self
|
|
return update_wrapper(wrapper, view)
|
|
|
|
custom_urls = [
|
|
path(
|
|
"<path:object_id>/submit/",
|
|
wrap(self.submit_view),
|
|
name="%s_%s_submit" % (self.opts.app_label, self.opts.model_name),
|
|
),
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def submit_view(self, request, object_id):
|
|
statement = Statement.objects.get(pk=object_id)
|
|
if statement.submitted:
|
|
messages.error(request,
|
|
_("%(name)s is already submitted.") % {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
|
|
|
|
if "apply" in request.POST:
|
|
statement.submit(get_member(request))
|
|
messages.success(request,
|
|
_("Successfully submited %(name)s. The finance department will notify the requestors as soon as possible.") % {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
|
|
context = dict(self.admin_site.each_context(request),
|
|
title=_('Submit statement'),
|
|
opts=self.opts,
|
|
statement=statement)
|
|
|
|
return render(request, 'admin/submit_statement.html', context=context)
|
|
|
|
|
|
class TransactionOnSubmittedStatementInline(admin.TabularInline):
|
|
model = Transaction
|
|
fields = ['amount', 'member', 'reference', 'ledger']
|
|
formfield_overrides = {
|
|
TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
|
|
}
|
|
extra = 0
|
|
|
|
|
|
class BillOnSubmittedStatementInline(BillOnStatementInline):
|
|
model = Bill
|
|
extra = 0
|
|
sortable_options = []
|
|
fields = ['short_description', 'explanation', 'amount', 'paid_by', 'proof', 'costs_covered']
|
|
formfield_overrides = {
|
|
TextField: {'widget': Textarea(attrs={'rows': 1, 'cols': 40})}
|
|
}
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
return ['short_description', 'explanation', 'amount', 'paid_by', 'proof']
|
|
|
|
|
|
@admin.register(StatementSubmitted)
|
|
class StatementSubmittedAdmin(admin.ModelAdmin):
|
|
fields = ['short_description', 'explanation', 'excursion', 'submitted']
|
|
list_display = ['__str__', 'is_valid', 'submitted_date', 'submitted_by']
|
|
ordering = ('-submitted_date',)
|
|
inlines = [BillOnSubmittedStatementInline, TransactionOnSubmittedStatementInline]
|
|
|
|
def has_add_permission(self, request, obj=None):
|
|
return False
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
readonly_fields = ['submitted']
|
|
if obj is not None and obj.submitted:
|
|
return readonly_fields + self.fields
|
|
else:
|
|
return readonly_fields
|
|
|
|
def get_urls(self):
|
|
urls = super().get_urls()
|
|
|
|
def wrap(view):
|
|
def wrapper(*args, **kwargs):
|
|
return self.admin_site.admin_view(view)(*args, **kwargs)
|
|
|
|
wrapper.model_admin = self
|
|
return update_wrapper(wrapper, view)
|
|
|
|
custom_urls = [
|
|
path(
|
|
"<path:object_id>/overview/",
|
|
wrap(self.overview_view),
|
|
name="%s_%s_overview" % (self.opts.app_label, self.opts.model_name),
|
|
),
|
|
path(
|
|
"<path:object_id>/reduce_transactions/",
|
|
wrap(self.reduce_transactions_view),
|
|
name="%s_%s_reduce_transactions" % (self.opts.app_label, self.opts.model_name),
|
|
),
|
|
]
|
|
return custom_urls + urls
|
|
|
|
def overview_view(self, request, object_id):
|
|
statement = StatementSubmitted.objects.get(pk=object_id)
|
|
if not statement.submitted:
|
|
messages.error(request,
|
|
_("%(name)s is not yet submitted.") % {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
|
|
if "transaction_execution_confirm" in request.POST:
|
|
res = statement.confirm(confirmer=get_member(request))
|
|
if not res:
|
|
# this should NOT happen!
|
|
messages.error(request,
|
|
_("An error occured while trying to confirm %(name)s. Please try again.") % {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name)))
|
|
|
|
messages.success(request,
|
|
_("Successfully confirmed %(name)s. I hope you executed the associated transactions, I wont remind you again.")
|
|
% {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
|
|
if "confirm" in request.POST:
|
|
res = statement.validity
|
|
if res == Statement.VALID:
|
|
context = dict(self.admin_site.each_context(request),
|
|
title=_('Statement confirmed'),
|
|
opts=self.opts,
|
|
statement=statement)
|
|
return render(request, 'admin/confirmed_statement.html', context=context)
|
|
elif res == Statement.NON_MATCHING_TRANSACTIONS:
|
|
messages.error(request,
|
|
_("Transactions do not match the covered expenses. Please correct the mistakes listed below.")
|
|
% {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
|
|
elif res == Statement.MISSING_LEDGER:
|
|
messages.error(request,
|
|
_("Some transactions have no ledger configured. Please fill in the gaps.")
|
|
% {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_overview' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
|
|
|
|
if "reject" in request.POST:
|
|
statement.submitted = False
|
|
statement.save()
|
|
messages.success(request,
|
|
_("Successfully rejected %(name)s. The requestor can reapply, when needed.")
|
|
% {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_changelist' % (self.opts.app_label, self.opts.model_name)))
|
|
|
|
if "generate_transactions" in request.POST:
|
|
if statement.transaction_set.count() > 0:
|
|
messages.error(request,
|
|
_("%(name)s already has transactions. Please delete them first, if you want to generate new ones") % {'name': str(statement)})
|
|
else:
|
|
statement.generate_transactions()
|
|
messages.success(request,
|
|
_("Successfully generated transactions for %(name)s") % {'name': str(statement)})
|
|
return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
|
|
context = dict(self.admin_site.each_context(request),
|
|
title=_('View submitted statement'),
|
|
opts=self.opts,
|
|
statement=statement,
|
|
transaction_issues=statement.transaction_issues,
|
|
nights=statement.excursion.night_count,
|
|
price_per_night=statement.real_night_cost,
|
|
duration=statement.excursion.duration,
|
|
staff_count=statement.real_staff_count,
|
|
kilometers_traveled=statement.excursion.kilometers_traveled,
|
|
means_of_transport=statement.excursion.get_tour_approach(),
|
|
euro_per_km=statement.euro_per_km,
|
|
allowance_per_day=statement.ALLOWANCE_PER_DAY,
|
|
total_bills=statement.total_bills,
|
|
nights_per_yl=statement.nights_per_yl,
|
|
allowance_per_yl=statement.allowance_per_yl,
|
|
transportation_per_yl=statement.transportation_per_yl,
|
|
total_per_yl=statement.total_per_yl,
|
|
total_staff=statement.total_staff,
|
|
total=statement.total)
|
|
|
|
return render(request, 'admin/overview_submitted_statement.html', context=context)
|
|
|
|
def reduce_transactions_view(self, request, object_id):
|
|
statement = StatementSubmitted.objects.get(pk=object_id)
|
|
statement.reduce_transactions()
|
|
messages.success(request,
|
|
_("Successfully reduced transactions for %(name)s.") % {'name': str(statement)})
|
|
return HttpResponseRedirect(request.GET['redirectTo'])
|
|
#return HttpResponseRedirect(reverse('admin:%s_%s_change' % (self.opts.app_label, self.opts.model_name), args=(statement.pk,)))
|
|
|
|
|
|
@admin.register(StatementConfirmed)
|
|
class StatementConfirmedAdmin(admin.ModelAdmin):
|
|
fields = ['short_description', 'explanation', 'excursion', 'confirmed']
|
|
#readonly_fields = fields
|
|
list_display = ['__str__', 'total_pretty', 'confirmed_date', 'confirmed_by']
|
|
ordering = ('-confirmed_date',)
|
|
|
|
def has_add_permission(self, request, obj=None):
|
|
return False
|
|
|
|
|
|
@admin.register(Transaction)
|
|
class TransactionAdmin(admin.ModelAdmin):
|
|
list_display = ['member', 'ledger', 'amount', 'reference', 'statement', 'confirmed',
|
|
'confirmed_date', 'confirmed_by']
|
|
list_filter = ('ledger', 'member', 'statement', 'confirmed')
|
|
search_fields = ('reference', )
|
|
fields = ['reference', 'amount', 'member', 'ledger', 'statement']
|
|
|
|
def get_readonly_fields(self, request, obj=None):
|
|
if obj is not None and obj.confirmed:
|
|
return self.fields
|
|
return super(TransactionAdmin, self).get_readonly_fields(request, obj)
|
|
|
|
|
|
@admin.register(Bill)
|
|
class BillAdmin(admin.ModelAdmin):
|
|
list_display = ['__str__', 'statement', 'short_description', 'pretty_amount', 'paid_by', 'refunded']
|
|
list_filter = ('statement', 'paid_by', 'refunded')
|
|
search_fields = ('reference', 'statement')
|
|
|
|
|
|
def get_member(request):
|
|
if not hasattr(request.user, 'member'):
|
|
return None
|
|
else:
|
|
return request.user.member
|