From 4fef4a6057a72d34188262e6ba4db4bddd77fa46 Mon Sep 17 00:00:00 2001 From: Hugo LEVY-FALK Date: Mon, 21 Jan 2019 00:54:02 +0100 Subject: [PATCH] =?UTF-8?q?D=C3=A9place=20les=20templates=20dans=20pr?= =?UTF-8?q?=C3=A9f=C3=A9rences.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cotisations/admin.py | 8 +- cotisations/forms.py | 36 +------ .../migrations/0039_documenttemplate.py | 29 ------ .../migrations/0040_auto_20190120_1708.py | 20 ---- cotisations/models.py | 54 ---------- .../templates/cotisations/facture.html | 2 +- .../cotisations/index_document_template.html | 42 -------- cotisations/urls.py | 22 +---- cotisations/views.py | 99 ------------------- preferences/admin.py | 10 +- preferences/forms.py | 37 ++++++- .../migrations/0059_auto_20190120_1739.py | 62 ++++++++++++ .../migrations/0059_cotisationsoption.py | 37 ------- .../migrations/0060_assooption_pres_name.py | 20 ---- ...061_cotisationsoption_send_voucher_mail.py | 20 ---- preferences/models.py | 65 ++++++++++-- .../preferences}/aff_document_template.html | 2 +- .../preferences/display_preferences.html | 19 ++++ preferences/urls.py | 15 +++ preferences/views.py | 90 ++++++++++++++++- 20 files changed, 294 insertions(+), 395 deletions(-) delete mode 100644 cotisations/migrations/0039_documenttemplate.py delete mode 100644 cotisations/migrations/0040_auto_20190120_1708.py delete mode 100644 cotisations/templates/cotisations/index_document_template.html create mode 100644 preferences/migrations/0059_auto_20190120_1739.py delete mode 100644 preferences/migrations/0059_cotisationsoption.py delete mode 100644 preferences/migrations/0060_assooption_pres_name.py delete mode 100644 preferences/migrations/0061_cotisationsoption_send_voucher_mail.py rename {cotisations/templates/cotisations => preferences/templates/preferences}/aff_document_template.html (96%) diff --git a/cotisations/admin.py b/cotisations/admin.py index 9eb0f071..4b47ccc8 100644 --- a/cotisations/admin.py +++ b/cotisations/admin.py @@ -30,7 +30,7 @@ from django.contrib import admin from reversion.admin import VersionAdmin from .models import Facture, Article, Banque, Paiement, Cotisation, Vente -from .models import CustomInvoice, CostEstimate, DocumentTemplate +from .models import CustomInvoice, CostEstimate class FactureAdmin(VersionAdmin): @@ -74,11 +74,6 @@ class CotisationAdmin(VersionAdmin): pass -class DocumentTemplateAdmin(VersionAdmin): - """Admin class for DocumentTemplate""" - pass - - admin.site.register(Facture, FactureAdmin) admin.site.register(Article, ArticleAdmin) admin.site.register(Banque, BanqueAdmin) @@ -87,4 +82,3 @@ admin.site.register(Vente, VenteAdmin) admin.site.register(Cotisation, CotisationAdmin) admin.site.register(CustomInvoice, CustomInvoiceAdmin) admin.site.register(CostEstimate, CostEstimateAdmin) -admin.site.register(DocumentTemplate, DocumentTemplateAdmin) diff --git a/cotisations/forms.py b/cotisations/forms.py index 21f8f42f..3f99382b 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -48,7 +48,7 @@ from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin from .models import ( Article, Paiement, Facture, Banque, - CustomInvoice, Vente, CostEstimate, DocumentTemplate + CustomInvoice, Vente, CostEstimate, ) from .payment_methods import balance @@ -316,37 +316,3 @@ class RechargeForm(FormRevMixin, Form): } ) return self.cleaned_data - - -class DocumentTemplateForm(FormRevMixin, ModelForm): - """ - Form used to create a document template. - """ - class Meta: - model = DocumentTemplate - fields = '__all__' - - def __init__(self, *args, **kwargs): - prefix = kwargs.pop('prefix', self.Meta.model.__name__) - super(DocumentTemplateForm, self).__init__( - *args, prefix=prefix, **kwargs) - - -class DelDocumentTemplateForm(FormRevMixin, Form): - """ - Form used to delete one or more document templatess. - The use must choose the one to delete by checking the boxes. - """ - document_templates = forms.ModelMultipleChoiceField( - queryset=DocumentTemplate.objects.none(), - label=_("Available document templates"), - widget=forms.CheckboxSelectMultiple - ) - - def __init__(self, *args, **kwargs): - instances = kwargs.pop('instances', None) - super(DelDocumentTemplateForm, self).__init__(*args, **kwargs) - if instances: - self.fields['document_templates'].queryset = instances - else: - self.fields['document_templates'].queryset = Banque.objects.all() diff --git a/cotisations/migrations/0039_documenttemplate.py b/cotisations/migrations/0039_documenttemplate.py deleted file mode 100644 index ee93da92..00000000 --- a/cotisations/migrations/0039_documenttemplate.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2019-01-03 16:48 -from __future__ import unicode_literals - -from django.db import migrations, models -import re2o.mixins - - -class Migration(migrations.Migration): - - dependencies = [ - ('cotisations', '0038_auto_20181231_1657'), - ] - - operations = [ - migrations.CreateModel( - name='DocumentTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('template', models.FileField(upload_to='templates/', verbose_name='template')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ], - options={ - 'verbose_name_plural': 'document templates', - 'verbose_name': 'document template', - }, - bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), - ), - ] diff --git a/cotisations/migrations/0040_auto_20190120_1708.py b/cotisations/migrations/0040_auto_20190120_1708.py deleted file mode 100644 index b7f5b279..00000000 --- a/cotisations/migrations/0040_auto_20190120_1708.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2019-01-20 23:08 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cotisations', '0039_documenttemplate'), - ] - - operations = [ - migrations.AlterField( - model_name='documenttemplate', - name='name', - field=models.CharField(max_length=125, unique=True, verbose_name='name'), - ), - ] diff --git a/cotisations/models.py b/cotisations/models.py index 88407d33..7346a634 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -33,7 +33,6 @@ each. from __future__ import unicode_literals from dateutil.relativedelta import relativedelta -import os from django.db import models from django.db.models import Q, Max @@ -956,56 +955,3 @@ def cotisation_post_delete(**_kwargs): """ regen('mac_ip_list') regen('mailing') - - -class DocumentTemplate(RevMixin, AclMixin, models.Model): - """Represent a template in order to create documents such as invoice or - subscription voucher. - """ - template = models.FileField( - upload_to='templates/', - verbose_name=_('template') - ) - name = models.CharField( - max_length=125, - verbose_name=_('name'), - unique=True - ) - - class Meta: - verbose_name = _("document template") - verbose_name_plural = _("document templates") - - def __str__(self): - return str(self.name) - -@receiver(models.signals.post_delete, sender=DocumentTemplate) -def auto_delete_file_on_delete(sender, instance, **kwargs): - """ - Deletes file from filesystem - when corresponding `DocumentTemplate` object is deleted. - """ - if instance.template: - if os.path.isfile(instance.template.path): - os.remove(instance.template.path) - - -@receiver(models.signals.pre_save, sender=DocumentTemplate) -def auto_delete_file_on_change(sender, instance, **kwargs): - """ - Deletes old file from filesystem - when corresponding `DocumentTemplate` object is updated - with new file. - """ - if not instance.pk: - return False - - try: - old_file = DocumentTemplate.objects.get(pk=instance.pk).template - except DocumentTemplate.DoesNotExist: - return False - - new_file = instance.template - if not old_file == new_file: - if os.path.isfile(old_file.path): - os.remove(old_file.path) diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index 5dddb305..65b05199 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -51,7 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% bootstrap_form_errors discount_form %} {% endif %} -
+ {% csrf_token %} {% bootstrap_form factureform %} {% if payment_method %} diff --git a/cotisations/templates/cotisations/index_document_template.html b/cotisations/templates/cotisations/index_document_template.html deleted file mode 100644 index f5fb9ae2..00000000 --- a/cotisations/templates/cotisations/index_document_template.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "cotisations/sidebar.html" %} -{% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il -se veut agnostique au réseau considéré, de manière à être installable en -quelques clics. - -Copyright © 2019 Hugo LEVY-FALK - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -{% endcomment %} - -{% load bootstrap3 %} -{% load acl %} -{% load i18n %} - -{% block title %}{% trans "Document Templates" %}{% endblock %} - -{% block content %} -

{% trans "Document templates list" %}

- {% can_create Banque %} - - {% trans "Add a document template" %} - - {% acl_end %} - - {% trans "Delete one or several document templates" %} - - {% include 'cotisations/aff_document_template.html' %} -{% endblock %} - diff --git a/cotisations/urls.py b/cotisations/urls.py index 8dbb84ec..0c7256f7 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -181,25 +181,5 @@ urlpatterns = [ views.control, name='control' ), - url( - r'^add_document_template/$', - views.add_document_template, - name='add-document-template' - ), - url( - r'^edit_document_template/(?P[0-9]+)$', - views.edit_document_template, - name='edit-document-template' - ), - url( - r'^del_document_template/$', - views.del_document_template, - name='del-document-template' - ), - url( - r'^index_document_template/$', - views.index_document_template, - name='index-document-template' - ), - url(r'^$', views.index, name='index'), + url(r'^$', views.index, name='index'), ] + payment_methods.urls.urlpatterns diff --git a/cotisations/views.py b/cotisations/views.py index e4b25453..b6d5a8a6 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -70,7 +70,6 @@ from .models import ( CustomInvoice, BaseInvoice, CostEstimate, - DocumentTemplate ) from .forms import ( FactureForm, @@ -85,8 +84,6 @@ from .forms import ( CustomInvoiceForm, DiscountForm, CostEstimateForm, - DocumentTemplateForm, - DelDocumentTemplateForm ) from .tex import render_invoice, render_voucher, escape_chars from .payment_methods.forms import payment_method_factory @@ -1056,102 +1053,6 @@ def credit_solde(request, user, **_kwargs): }, 'cotisations/facture.html', request) -@login_required -@can_create(DocumentTemplate) -def add_document_template(request): - """ - View used to add a document template. - """ - document_template = DocumentTemplateForm( - request.POST or None, - request.FILES or None, - ) - if document_template.is_valid(): - document_template.save() - messages.success( - request, - _("The document template was created.") - ) - return redirect(reverse('cotisations:index-document-template')) - return form({ - 'factureform': document_template, - 'action_name': _("Add"), - 'title': _("New document template") - }, 'cotisations/facture.html', request) - - -@login_required -@can_edit(DocumentTemplate) -def edit_document_template(request, document_template_instance, **_kwargs): - """ - View used to edit a document_template. - """ - document_template = DocumentTemplateForm( - request.POST or None, - request.FILES or None, - instance=document_template_instance) - if document_template.is_valid(): - if document_template.changed_data: - document_template.save() - messages.success( - request, - _("The document template was edited.") - ) - return redirect(reverse('cotisations:index-document-template')) - return form({ - 'factureform': document_template, - 'action_name': _("Edit"), - 'title': _("Edit document template") - }, 'cotisations/facture.html', request) - - -@login_required -@can_delete_set(DocumentTemplate) -def del_document_template(request, instances): - """ - View used to delete a set of document template. - """ - document_template = DelDocumentTemplateForm( - request.POST or None, instances=instances) - if document_template.is_valid(): - document_template_del = document_template.cleaned_data['document_templates'] - for document_template in document_template_del: - try: - document_template.delete() - messages.success( - request, - _("The document template %(document_template)s was deleted.") % { - 'document_template': document_template - } - ) - except ProtectedError: - messages.error( - request, - _("The document template %(document_template)s can't be deleted \ - because it is currently being used.") % { - 'document_template': document_template - } - ) - return redirect(reverse('cotisations:index-document-template')) - return form({ - 'factureform': document_template, - 'action_name': _("Delete"), - 'title': _("Delete document template") - }, 'cotisations/facture.html', request) - - -@login_required -@can_view_all(DocumentTemplate) -def index_document_template(request): - """ - View used to display the list of all available document templates. - """ - document_template_list = DocumentTemplate.objects.order_by('name') - return render(request, 'cotisations/index_document_template.html', { - 'document_template_list': document_template_list - }) - - @login_required @can_view(Facture) def voucher_pdf(request, invoice, **_kwargs): diff --git a/preferences/admin.py b/preferences/admin.py index b7b171d0..efeefc87 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -40,7 +40,8 @@ from .models import ( HomeOption, RadiusKey, SwitchManagementCred, - Reminder + Reminder, + DocumentTemplate ) @@ -101,6 +102,12 @@ class ReminderAdmin(VersionAdmin): """Class reminder for switch""" pass + +class DocumentTemplateAdmin(VersionAdmin): + """Admin class for DocumentTemplate""" + pass + + admin.site.register(OptionalUser, OptionalUserAdmin) admin.site.register(OptionalMachine, OptionalMachineAdmin) admin.site.register(OptionalTopologie, OptionalTopologieAdmin) @@ -113,3 +120,4 @@ admin.site.register(RadiusKey, RadiusKeyAdmin) admin.site.register(SwitchManagementCred, SwitchManagementCredAdmin) admin.site.register(AssoOption, AssoOptionAdmin) admin.site.register(MailMessageOption, MailMessageOptionAdmin) +admin.site.register(DocumentTemplate, DocumentTemplateAdmin) diff --git a/preferences/forms.py b/preferences/forms.py index b1902d8d..d2bede7c 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -43,10 +43,12 @@ from .models import ( RadiusKey, SwitchManagementCred, RadiusOption, - CotisationsOption + CotisationsOption, + DocumentTemplate ) from topologie.models import Switch + class EditOptionalUserForm(ModelForm): """Formulaire d'édition des options de l'user. (solde, telephone..)""" class Meta: @@ -376,3 +378,36 @@ class DelMailContactForm(Form): else: self.fields['mailcontacts'].queryset = MailContact.objects.all() + +class DocumentTemplateForm(FormRevMixin, ModelForm): + """ + Form used to create a document template. + """ + class Meta: + model = DocumentTemplate + fields = '__all__' + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(DocumentTemplateForm, self).__init__( + *args, prefix=prefix, **kwargs) + + +class DelDocumentTemplateForm(FormRevMixin, Form): + """ + Form used to delete one or more document templatess. + The use must choose the one to delete by checking the boxes. + """ + document_templates = forms.ModelMultipleChoiceField( + queryset=DocumentTemplate.objects.none(), + label=_("Available document templates"), + widget=forms.CheckboxSelectMultiple + ) + + def __init__(self, *args, **kwargs): + instances = kwargs.pop('instances', None) + super(DelDocumentTemplateForm, self).__init__(*args, **kwargs) + if instances: + self.fields['document_templates'].queryset = instances + else: + self.fields['document_templates'].queryset = Banque.objects.all() diff --git a/preferences/migrations/0059_auto_20190120_1739.py b/preferences/migrations/0059_auto_20190120_1739.py new file mode 100644 index 00000000..23447ce8 --- /dev/null +++ b/preferences/migrations/0059_auto_20190120_1739.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2019-01-20 23:39 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import preferences.models +import re2o.mixins + + +def create_defaults(apps, schema_editor): + CotisationsOption = apps.get_model('preferences', 'CotisationsOption') + CotisationsOption.objects.get_or_create() + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0058_auto_20190108_1650'), + ] + + operations = [ + migrations.CreateModel( + name='CotisationsOption', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('send_voucher_mail', models.BooleanField(default=False, verbose_name='Send voucher by email when the invoice is controlled.')), + ], + options={ + 'verbose_name': 'cotisations options', + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='DocumentTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('template', models.FileField(upload_to='templates/', verbose_name='template')), + ('name', models.CharField(max_length=125, unique=True, verbose_name='name')), + ], + options={ + 'verbose_name': 'document template', + 'verbose_name_plural': 'document templates', + }, + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + migrations.AddField( + model_name='assooption', + name='pres_name', + field=models.CharField(default='', help_text='Displayed on subscription vouchers', max_length=255, verbose_name='President of the association'), + ), + migrations.AddField( + model_name='cotisationsoption', + name='invoice_template', + field=models.OneToOneField(default=preferences.models.default_invoice, on_delete=django.db.models.deletion.PROTECT, related_name='invoice_template', to='preferences.DocumentTemplate', verbose_name='Template for invoices'), + ), + migrations.AddField( + model_name='cotisationsoption', + name='voucher_template', + field=models.OneToOneField(default=preferences.models.default_voucher, on_delete=django.db.models.deletion.PROTECT, related_name='voucher_template', to='preferences.DocumentTemplate', verbose_name='Template for subscription voucher'), + ), + migrations.RunPython(create_defaults), + ] diff --git a/preferences/migrations/0059_cotisationsoption.py b/preferences/migrations/0059_cotisationsoption.py deleted file mode 100644 index 9fe53203..00000000 --- a/preferences/migrations/0059_cotisationsoption.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2019-01-03 19:56 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion -import re2o.mixins -import preferences.models - - -def initialize_invoice_template(apps, schema_editor): - CotisationsOption = apps.get_model('preferences', 'CotisationsOption') - CotisationsOption.objects.get_or_create() - - -class Migration(migrations.Migration): - - dependencies = [ - ('cotisations', '0039_documenttemplate'), - ('preferences', '0058_auto_20190108_1650'), - ] - - operations = [ - migrations.CreateModel( - name='CotisationsOption', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('invoice_template', models.OneToOneField(default=preferences.models.default_invoice,on_delete=django.db.models.deletion.PROTECT, related_name='invoice_template', to='cotisations.DocumentTemplate', verbose_name='Template for invoices')), - ('voucher_template', models.OneToOneField(default=preferences.models.default_voucher, on_delete=django.db.models.deletion.PROTECT, related_name='voucher_template', to='cotisations.DocumentTemplate', verbose_name='Template for subscription voucher')), - ], - options={ - 'verbose_name': 'cotisations options', - }, - bases=(re2o.mixins.AclMixin, models.Model), - ), - migrations.RunPython(initialize_invoice_template), - ] diff --git a/preferences/migrations/0060_assooption_pres_name.py b/preferences/migrations/0060_assooption_pres_name.py deleted file mode 100644 index 986ad511..00000000 --- a/preferences/migrations/0060_assooption_pres_name.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2019-01-10 22:13 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0059_cotisationsoption'), - ] - - operations = [ - migrations.AddField( - model_name='assooption', - name='pres_name', - field=models.CharField(default='', help_text='Displayed on subscription vouchers', max_length=255, verbose_name='President of the association'), - ), - ] diff --git a/preferences/migrations/0061_cotisationsoption_send_voucher_mail.py b/preferences/migrations/0061_cotisationsoption_send_voucher_mail.py deleted file mode 100644 index 1476ef12..00000000 --- a/preferences/migrations/0061_cotisationsoption_send_voucher_mail.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.7 on 2019-01-20 18:03 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0060_assooption_pres_name'), - ] - - operations = [ - migrations.AddField( - model_name='cotisationsoption', - name='send_voucher_mail', - field=models.BooleanField(default=False, verbose_name='Send voucher by email when the invoice is controlled.'), - ), - ] diff --git a/preferences/models.py b/preferences/models.py index 9e2988e0..445d7139 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -24,6 +24,7 @@ Reglages généraux, machines, utilisateurs, mail, general pour l'application. """ from __future__ import unicode_literals +import os from django.utils.functional import cached_property from django.utils import timezone @@ -35,9 +36,8 @@ from django.forms import ValidationError from django.utils.translation import ugettext_lazy as _ import machines.models -import cotisations.models -from re2o.mixins import AclMixin +from re2o.mixins import AclMixin, RevMixin from re2o.aes_field import AESEncryptedField from datetime import timedelta @@ -696,7 +696,7 @@ class RadiusOption(AclMixin, PreferencesModel): def default_invoice(): - tpl, _ = cotisations.models.DocumentTemplate.objects.get_or_create( + tpl, _ = DocumentTemplate.objects.get_or_create( name="Re2o default invoice", template="templates/default_invoice.tex" ) @@ -704,7 +704,7 @@ def default_invoice(): def default_voucher(): - tpl, _ = cotisations.models.DocumentTemplate.objects.get_or_create( + tpl, _ = DocumentTemplate.objects.get_or_create( name="Re2o default voucher", template="templates/default_voucher.tex" ) @@ -716,14 +716,14 @@ class CotisationsOption(AclMixin, PreferencesModel): verbose_name = _("cotisations options") invoice_template = models.OneToOneField( - 'cotisations.DocumentTemplate', + 'preferences.DocumentTemplate', verbose_name=_("Template for invoices"), related_name="invoice_template", on_delete=models.PROTECT, default=default_invoice, ) voucher_template = models.OneToOneField( - 'cotisations.DocumentTemplate', + 'preferences.DocumentTemplate', verbose_name=_("Template for subscription voucher"), related_name="voucher_template", on_delete=models.PROTECT, @@ -733,3 +733,56 @@ class CotisationsOption(AclMixin, PreferencesModel): verbose_name=_("Send voucher by email when the invoice is controlled."), default=False, ) + + +class DocumentTemplate(RevMixin, AclMixin, models.Model): + """Represent a template in order to create documents such as invoice or + subscription voucher. + """ + template = models.FileField( + upload_to='templates/', + verbose_name=_('template') + ) + name = models.CharField( + max_length=125, + verbose_name=_('name'), + unique=True + ) + + class Meta: + verbose_name = _("document template") + verbose_name_plural = _("document templates") + + def __str__(self): + return str(self.name) + +@receiver(models.signals.post_delete, sender=DocumentTemplate) +def auto_delete_file_on_delete(sender, instance, **kwargs): + """ + Deletes file from filesystem + when corresponding `DocumentTemplate` object is deleted. + """ + if instance.template: + if os.path.isfile(instance.template.path): + os.remove(instance.template.path) + + +@receiver(models.signals.pre_save, sender=DocumentTemplate) +def auto_delete_file_on_change(sender, instance, **kwargs): + """ + Deletes old file from filesystem + when corresponding `DocumentTemplate` object is updated + with new file. + """ + if not instance.pk: + return False + + try: + old_file = DocumentTemplate.objects.get(pk=instance.pk).template + except DocumentTemplate.DoesNotExist: + return False + + new_file = instance.template + if not old_file == new_file: + if os.path.isfile(old_file.path): + os.remove(old_file.path) diff --git a/cotisations/templates/cotisations/aff_document_template.html b/preferences/templates/preferences/aff_document_template.html similarity index 96% rename from cotisations/templates/cotisations/aff_document_template.html rename to preferences/templates/preferences/aff_document_template.html index e35406d4..8d2184b4 100644 --- a/cotisations/templates/cotisations/aff_document_template.html +++ b/preferences/templates/preferences/aff_document_template.html @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {{template.template}} {% can_edit template %} - {% include 'buttons/edit.html' with href='cotisations:edit-document-template' id=template.id %} + {% include 'buttons/edit.html' with href='preferences:edit-document-template' id=template.id %} {% acl_end %} {% history_button template %} diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index f41e8397..a7d15679 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -350,6 +350,25 @@ with this program; if not, write to the Free Software Foundation, Inc., +
+ +
+ {% can_create DocumentTemplate %} + + {% trans "Add a document template" %} + + {% acl_end %} + + {% trans "Delete one or several document templates" %} + + {% include 'preferences/aff_document_template.html' %} +
+
+

diff --git a/preferences/urls.py b/preferences/urls.py index 75ce0a8a..9bfd67d3 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -111,5 +111,20 @@ urlpatterns = [ name='edit-switchmanagementcred' ), url(r'^del_switchmanagementcred/(?P[0-9]+)$', views.del_switchmanagementcred, name='del-switchmanagementcred'), + url( + r'^add_document_template/$', + views.add_document_template, + name='add-document-template' + ), + url( + r'^edit_document_template/(?P[0-9]+)$', + views.edit_document_template, + name='edit-document-template' + ), + url( + r'^del_document_template/$', + views.del_document_template, + name='del-document-template' + ), url(r'^$', views.display_options, name='display-options'), ] diff --git a/preferences/views.py b/preferences/views.py index 229b64e8..ecb3826e 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -48,7 +48,9 @@ from .forms import ( ServiceForm, ReminderForm, RadiusKeyForm, - SwitchManagementCredForm + SwitchManagementCredForm, + DocumentTemplateForm, + DelDocumentTemplateForm ) from .models import ( Service, @@ -65,6 +67,7 @@ from .models import ( SwitchManagementCred, RadiusOption, CotisationsOption, + DocumentTemplate ) from . import models from . import forms @@ -90,6 +93,7 @@ def display_options(request): switchmanagementcred_list = SwitchManagementCred.objects.all() radiusoptions, _ = RadiusOption.objects.get_or_create() cotisationsoptions, _created = CotisationsOption.objects.get_or_create() + document_template_list = DocumentTemplate.objects.order_by('name') return form({ 'useroptions': useroptions, 'machineoptions': machineoptions, @@ -105,6 +109,7 @@ def display_options(request): 'switchmanagementcred_list': switchmanagementcred_list, 'radiusoptions' : radiusoptions, 'cotisationsoptions': cotisationsoptions, + 'document_template_list': document_template_list, }, 'preferences/display_preferences.html', request) @@ -408,3 +413,86 @@ def del_mailcontact(request, instances): request ) + +@login_required +@can_create(DocumentTemplate) +def add_document_template(request): + """ + View used to add a document template. + """ + document_template = DocumentTemplateForm( + request.POST or None, + request.FILES or None, + ) + if document_template.is_valid(): + document_template.save() + messages.success( + request, + _("The document template was created.") + ) + return redirect(reverse('preferences:display-options')) + return form({ + 'preferenceform': document_template, + 'action_name': _("Add"), + 'title': _("New document template") + }, 'preferences/preferences.html', request) + + +@login_required +@can_edit(DocumentTemplate) +def edit_document_template(request, document_template_instance, **_kwargs): + """ + View used to edit a document_template. + """ + document_template = DocumentTemplateForm( + request.POST or None, + request.FILES or None, + instance=document_template_instance) + if document_template.is_valid(): + if document_template.changed_data: + document_template.save() + messages.success( + request, + _("The document template was edited.") + ) + return redirect(reverse('preferences:display-options')) + return form({ + 'preferenceform': document_template, + 'action_name': _("Edit"), + 'title': _("Edit document template") + }, 'preferences/preferences.html', request) + + +@login_required +@can_delete_set(DocumentTemplate) +def del_document_template(request, instances): + """ + View used to delete a set of document template. + """ + document_template = DelDocumentTemplateForm( + request.POST or None, instances=instances) + if document_template.is_valid(): + document_template_del = document_template.cleaned_data['document_templates'] + for document_template in document_template_del: + try: + document_template.delete() + messages.success( + request, + _("The document template %(document_template)s was deleted.") % { + 'document_template': document_template + } + ) + except ProtectedError: + messages.error( + request, + _("The document template %(document_template)s can't be deleted \ + because it is currently being used.") % { + 'document_template': document_template + } + ) + return redirect(reverse('preferences:display-options')) + return form({ + 'preferenceform': document_template, + 'action_name': _("Delete"), + 'title': _("Delete document template") + }, 'preferences/preferences.html', request)