diff --git a/RIGS/admin.py b/RIGS/admin.py index a822d636..3502e962 100644 --- a/RIGS/admin.py +++ b/RIGS/admin.py @@ -19,8 +19,8 @@ from reversion import revisions as reversion admin.site.register(models.VatRate, VersionAdmin) admin.site.register(models.Event, VersionAdmin) admin.site.register(models.EventItem, VersionAdmin) -admin.site.register(models.Invoice) -admin.site.register(models.Payment) +admin.site.register(models.Invoice, VersionAdmin) +admin.site.register(models.Payment, VersionAdmin) def approve_user(modeladmin, request, queryset): diff --git a/RIGS/finance.py b/RIGS/finance.py index 9bb1b0b1..c055f465 100644 --- a/RIGS/finance.py +++ b/RIGS/finance.py @@ -13,6 +13,9 @@ from django.db.models import Q from z3c.rml import rml2pdf from django.db.models import Q +from django.db import transaction +import reversion + from RIGS import models from django import forms @@ -192,7 +195,10 @@ class InvoiceWaiting(generic.ListView): class InvoiceEvent(generic.View): + @transaction.atomic() + @reversion.create_revision() def get(self, *args, **kwargs): + reversion.set_user(self.request.user) epk = kwargs.get('pk') event = models.Event.objects.get(pk=epk) invoice, created = models.Invoice.objects.get_or_create(event=event) @@ -223,6 +229,13 @@ class PaymentCreate(generic.CreateView): initial.update({'invoice': invoice}) return initial + @transaction.atomic() + @reversion.create_revision() + def form_valid(self, form, *args, **kwargs): + reversion.add_to_revision(form.cleaned_data['invoice']) + reversion.set_comment("Payment added") + return super().form_valid(form, *args, **kwargs) + def get_success_url(self): messages.info(self.request, "location.reload()") return reverse_lazy('closemodal') @@ -232,5 +245,12 @@ class PaymentDelete(generic.DeleteView): model = models.Payment template_name = 'payment_confirm_delete.html' + @transaction.atomic() + @reversion.create_revision() + def delete(self, *args, **kwargs): + reversion.add_to_revision(self.get_object().invoice) + reversion.set_comment("Payment removed") + return super().delete(*args, **kwargs) + def get_success_url(self): return self.request.POST.get('next') diff --git a/RIGS/models.py b/RIGS/models.py index 9b9c13cc..3cdba0c1 100644 --- a/RIGS/models.py +++ b/RIGS/models.py @@ -489,7 +489,8 @@ class Event(models.Model, RevisionMixin): super(Event, self).save(*args, **kwargs) -class EventItem(models.Model): +@reversion.register +class EventItem(models.Model, RevisionMixin): event = models.ForeignKey('Event', related_name='items', blank=True, on_delete=models.CASCADE) name = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) @@ -528,11 +529,14 @@ class EventAuthorisation(models.Model, RevisionMixin): return str("N%05d" % self.event.pk + ' (requested by ' + self.sent_by.initials + ')') -class Invoice(models.Model): +@reversion.register(follow=['payment_set']) +class Invoice(models.Model, RevisionMixin): event = models.OneToOneField('Event', on_delete=models.CASCADE) invoice_date = models.DateField(auto_now_add=True) void = models.BooleanField(default=False) + reversion_perm = 'RIGS.view_invoice' + @property def sum_total(self): return self.event.sum_total @@ -556,6 +560,13 @@ class Invoice(models.Model): def is_closed(self): return self.balance == 0 or self.void + def get_absolute_url(self): + return reverse_lazy('invoice_detail', kwargs={'pk': self.pk}) + + @property + def activity_feed_string(self): + return "#{} for Event {}".format(self.pk, "N%05d" % self.event.pk) + def __str__(self): return "%i: %s (%.2f)" % (self.pk, self.event, self.balance) @@ -563,7 +574,8 @@ class Invoice(models.Model): ordering = ['-invoice_date'] -class Payment(models.Model): +@reversion.register +class Payment(models.Model, RevisionMixin): CASH = 'C' INTERNAL = 'I' EXTERNAL = 'E' @@ -582,6 +594,8 @@ class Payment(models.Model): amount = models.DecimalField(max_digits=10, decimal_places=2, help_text='Please use ex. VAT') method = models.CharField(max_length=2, choices=METHODS, null=True, blank=True) + reversion_hide = True + def __str__(self): return "%s: %d" % (self.get_method_display(), self.amount) diff --git a/RIGS/templates/invoice_detail.html b/RIGS/templates/invoice_detail.html index 9343bcbf..2fea7cd2 100644 --- a/RIGS/templates/invoice_detail.html +++ b/RIGS/templates/invoice_detail.html @@ -103,5 +103,8 @@ +
+ {% include 'partials/last_edited.html' with target="invoice_history" %} +
{% endblock %} diff --git a/RIGS/templates/payment_confirm_delete.html b/RIGS/templates/payment_confirm_delete.html index ab58e243..0b47f4a5 100644 --- a/RIGS/templates/payment_confirm_delete.html +++ b/RIGS/templates/payment_confirm_delete.html @@ -3,35 +3,19 @@ {% block title %}Delete payment on invoice {{ object.invoice.pk }}{% endblock %} {% block content %} -
- +{% endblock %} diff --git a/assets/models.py b/assets/models.py index 236ca479..3a7b7a63 100644 --- a/assets/models.py +++ b/assets/models.py @@ -126,6 +126,8 @@ class Asset(models.Model, RevisionMixin): asset_id_prefix = models.CharField(max_length=8, default="") asset_id_number = models.IntegerField(default=1) + reversion_perm = 'assets.asset_finance' + def get_available_asset_id(wanted_prefix=""): sql = """ SELECT a.asset_id_number+1 diff --git a/versioning/urls.py b/versioning/urls.py index 44ef1f66..7c8fe5b1 100644 --- a/versioning/urls.py +++ b/versioning/urls.py @@ -16,19 +16,29 @@ urlpatterns = [ name='activity_feed'), ] +# Well except this specific hack for legacy URLs...if only the RIGS app had been named 'rigboard'! for app in apps.get_app_configs(): appname = str(app.label) - # Well except this specific hack for legacy URLs...if only the RIGS app had been named 'rigboard'! + if appname == 'RIGS': appname = 'rigboard' - urlpatterns += [path(appname + '/activity/', permission_required_with_403('RIGS.view_event')(views.ActivityTable.as_view()), - name='activity_table', kwargs={'app': appname, 'models': views.get_models(app.label)}), ] + table_name = 'activity_table' else: - urlpatterns += [path(appname + '/activity/', permission_required_with_403('RIGS.view_event')(views.ActivityTable.as_view()), - name=appname + '_activity_table', kwargs={'app': appname, 'models': views.get_models(app.label)}), ] - for model in views.get_models(app.label): + table_name = appname + '_activity_table' + + # TODO Permissions + urlpatterns += [path(appname + '/activity/', permission_required_with_403('RIGS.view_event')(views.ActivityTable.as_view()), + name=table_name, kwargs={'app': appname, 'models': views.get_models(app.label)}), ] + + for model in views.get_models(app=app.label): modelname = model.__name__.lower() - urlpatterns += [ - path(appname + '/' + modelname + '//history/', permission_required_with_403('{}.change_{}'.format(app.label, modelname))(views.VersionHistory.as_view()), - name='{}_history'.format(modelname), kwargs={'model': model, 'app': appname, }), - ] + if appname == 'rigboard': + urlpatterns += [ + path('{}//history/'.format(modelname), permission_required_with_403('{}.change_{}'.format(app.label, modelname))(views.VersionHistory.as_view()), + name='{}_history'.format(modelname), kwargs={'model': model, 'app': appname, }), + ] + else: + urlpatterns += [ + path('{}/{}//history/'.format(appname, modelname), permission_required_with_403('{}.change_{}'.format(app.label, modelname))(views.VersionHistory.as_view()), + name='{}_history'.format(modelname), kwargs={'model': model, 'app': appname, }), + ] diff --git a/versioning/views.py b/versioning/views.py index 0826c55d..bc32b05f 100644 --- a/versioning/views.py +++ b/versioning/views.py @@ -52,13 +52,21 @@ def get_models(app=None): return models +# TODO Default filter of having permission to view associated object +def filter_models(models, user): + if user is not None: + models = filter(lambda model: not hasattr(model, 'reversion_perm') or user.has_perm(model.reversion_perm), models) + + return models + + class ActivityTable(generic.ListView): model = RIGSVersion template_name = "activity_table.html" paginate_by = 25 def get_queryset(self): - versions = RIGSVersion.objects.get_for_multiple_models(self.kwargs['models']) + versions = RIGSVersion.objects.get_for_multiple_models(filter_models(self.kwargs['models'], self.request.user)) return versions.order_by("-revision__date_created") def get_context_data(self, **kwargs): @@ -69,17 +77,15 @@ class ActivityTable(generic.ListView): return context -# Appears on homepage - @method_decorator(never_cache, name='dispatch') # Disable browser based caching -class ActivityFeed(generic.ListView): +class ActivityFeed(generic.ListView): # Appears on homepage model = RIGSVersion template_name = "activity_feed_data.html" paginate_by = 25 def get_queryset(self): - versions = RIGSVersion.objects.get_for_multiple_models(get_models()) + versions = RIGSVersion.objects.get_for_multiple_models(filter_models(get_models(), self.request.user)) return versions.order_by("-revision__date_created") def get_context_data(self, **kwargs):