mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +00:00
Déplace les templates dans préférences.
This commit is contained in:
parent
f4b6f10d1e
commit
4fef4a6057
20 changed files with 294 additions and 395 deletions
|
@ -30,7 +30,7 @@ from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
from .models import Facture, Article, Banque, Paiement, Cotisation, Vente
|
||||||
from .models import CustomInvoice, CostEstimate, DocumentTemplate
|
from .models import CustomInvoice, CostEstimate
|
||||||
|
|
||||||
|
|
||||||
class FactureAdmin(VersionAdmin):
|
class FactureAdmin(VersionAdmin):
|
||||||
|
@ -74,11 +74,6 @@ class CotisationAdmin(VersionAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class DocumentTemplateAdmin(VersionAdmin):
|
|
||||||
"""Admin class for DocumentTemplate"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Facture, FactureAdmin)
|
admin.site.register(Facture, FactureAdmin)
|
||||||
admin.site.register(Article, ArticleAdmin)
|
admin.site.register(Article, ArticleAdmin)
|
||||||
admin.site.register(Banque, BanqueAdmin)
|
admin.site.register(Banque, BanqueAdmin)
|
||||||
|
@ -87,4 +82,3 @@ admin.site.register(Vente, VenteAdmin)
|
||||||
admin.site.register(Cotisation, CotisationAdmin)
|
admin.site.register(Cotisation, CotisationAdmin)
|
||||||
admin.site.register(CustomInvoice, CustomInvoiceAdmin)
|
admin.site.register(CustomInvoice, CustomInvoiceAdmin)
|
||||||
admin.site.register(CostEstimate, CostEstimateAdmin)
|
admin.site.register(CostEstimate, CostEstimateAdmin)
|
||||||
admin.site.register(DocumentTemplate, DocumentTemplateAdmin)
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ from re2o.field_permissions import FieldPermissionFormMixin
|
||||||
from re2o.mixins import FormRevMixin
|
from re2o.mixins import FormRevMixin
|
||||||
from .models import (
|
from .models import (
|
||||||
Article, Paiement, Facture, Banque,
|
Article, Paiement, Facture, Banque,
|
||||||
CustomInvoice, Vente, CostEstimate, DocumentTemplate
|
CustomInvoice, Vente, CostEstimate,
|
||||||
)
|
)
|
||||||
from .payment_methods import balance
|
from .payment_methods import balance
|
||||||
|
|
||||||
|
@ -316,37 +316,3 @@ class RechargeForm(FormRevMixin, Form):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return self.cleaned_data
|
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()
|
|
||||||
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -33,7 +33,6 @@ each.
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
import os
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q, Max
|
from django.db.models import Q, Max
|
||||||
|
@ -956,56 +955,3 @@ def cotisation_post_delete(**_kwargs):
|
||||||
"""
|
"""
|
||||||
regen('mac_ip_list')
|
regen('mac_ip_list')
|
||||||
regen('mailing')
|
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)
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% bootstrap_form_errors discount_form %}
|
{% bootstrap_form_errors discount_form %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form class="form" method="post" enctype="multipart/form-data">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form factureform %}
|
{% bootstrap_form factureform %}
|
||||||
{% if payment_method %}
|
{% if payment_method %}
|
||||||
|
|
|
@ -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 %}
|
|
||||||
<h2>{% trans "Document templates list" %}</h2>
|
|
||||||
{% can_create Banque %}
|
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:add-document-template' %}">
|
|
||||||
<i class="fa fa-cart-plus"></i> {% trans "Add a document template" %}
|
|
||||||
</a>
|
|
||||||
{% acl_end %}
|
|
||||||
<a class="btn btn-danger btn-sm" role="button" href="{% url 'cotisations:del-document-template' %}">
|
|
||||||
<i class="fa fa-trash"></i> {% trans "Delete one or several document templates" %}
|
|
||||||
</a>
|
|
||||||
{% include 'cotisations/aff_document_template.html' %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -181,25 +181,5 @@ urlpatterns = [
|
||||||
views.control,
|
views.control,
|
||||||
name='control'
|
name='control'
|
||||||
),
|
),
|
||||||
url(
|
url(r'^$', views.index, name='index'),
|
||||||
r'^add_document_template/$',
|
|
||||||
views.add_document_template,
|
|
||||||
name='add-document-template'
|
|
||||||
),
|
|
||||||
url(
|
|
||||||
r'^edit_document_template/(?P<documenttemplateid>[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'),
|
|
||||||
] + payment_methods.urls.urlpatterns
|
] + payment_methods.urls.urlpatterns
|
||||||
|
|
|
@ -70,7 +70,6 @@ from .models import (
|
||||||
CustomInvoice,
|
CustomInvoice,
|
||||||
BaseInvoice,
|
BaseInvoice,
|
||||||
CostEstimate,
|
CostEstimate,
|
||||||
DocumentTemplate
|
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
FactureForm,
|
FactureForm,
|
||||||
|
@ -85,8 +84,6 @@ from .forms import (
|
||||||
CustomInvoiceForm,
|
CustomInvoiceForm,
|
||||||
DiscountForm,
|
DiscountForm,
|
||||||
CostEstimateForm,
|
CostEstimateForm,
|
||||||
DocumentTemplateForm,
|
|
||||||
DelDocumentTemplateForm
|
|
||||||
)
|
)
|
||||||
from .tex import render_invoice, render_voucher, escape_chars
|
from .tex import render_invoice, render_voucher, escape_chars
|
||||||
from .payment_methods.forms import payment_method_factory
|
from .payment_methods.forms import payment_method_factory
|
||||||
|
@ -1056,102 +1053,6 @@ def credit_solde(request, user, **_kwargs):
|
||||||
}, 'cotisations/facture.html', request)
|
}, '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
|
@login_required
|
||||||
@can_view(Facture)
|
@can_view(Facture)
|
||||||
def voucher_pdf(request, invoice, **_kwargs):
|
def voucher_pdf(request, invoice, **_kwargs):
|
||||||
|
|
|
@ -40,7 +40,8 @@ from .models import (
|
||||||
HomeOption,
|
HomeOption,
|
||||||
RadiusKey,
|
RadiusKey,
|
||||||
SwitchManagementCred,
|
SwitchManagementCred,
|
||||||
Reminder
|
Reminder,
|
||||||
|
DocumentTemplate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +102,12 @@ class ReminderAdmin(VersionAdmin):
|
||||||
"""Class reminder for switch"""
|
"""Class reminder for switch"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentTemplateAdmin(VersionAdmin):
|
||||||
|
"""Admin class for DocumentTemplate"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(OptionalUser, OptionalUserAdmin)
|
admin.site.register(OptionalUser, OptionalUserAdmin)
|
||||||
admin.site.register(OptionalMachine, OptionalMachineAdmin)
|
admin.site.register(OptionalMachine, OptionalMachineAdmin)
|
||||||
admin.site.register(OptionalTopologie, OptionalTopologieAdmin)
|
admin.site.register(OptionalTopologie, OptionalTopologieAdmin)
|
||||||
|
@ -113,3 +120,4 @@ admin.site.register(RadiusKey, RadiusKeyAdmin)
|
||||||
admin.site.register(SwitchManagementCred, SwitchManagementCredAdmin)
|
admin.site.register(SwitchManagementCred, SwitchManagementCredAdmin)
|
||||||
admin.site.register(AssoOption, AssoOptionAdmin)
|
admin.site.register(AssoOption, AssoOptionAdmin)
|
||||||
admin.site.register(MailMessageOption, MailMessageOptionAdmin)
|
admin.site.register(MailMessageOption, MailMessageOptionAdmin)
|
||||||
|
admin.site.register(DocumentTemplate, DocumentTemplateAdmin)
|
||||||
|
|
|
@ -43,10 +43,12 @@ from .models import (
|
||||||
RadiusKey,
|
RadiusKey,
|
||||||
SwitchManagementCred,
|
SwitchManagementCred,
|
||||||
RadiusOption,
|
RadiusOption,
|
||||||
CotisationsOption
|
CotisationsOption,
|
||||||
|
DocumentTemplate
|
||||||
)
|
)
|
||||||
from topologie.models import Switch
|
from topologie.models import Switch
|
||||||
|
|
||||||
|
|
||||||
class EditOptionalUserForm(ModelForm):
|
class EditOptionalUserForm(ModelForm):
|
||||||
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
|
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -376,3 +378,36 @@ class DelMailContactForm(Form):
|
||||||
else:
|
else:
|
||||||
self.fields['mailcontacts'].queryset = MailContact.objects.all()
|
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()
|
||||||
|
|
62
preferences/migrations/0059_auto_20190120_1739.py
Normal file
62
preferences/migrations/0059_auto_20190120_1739.py
Normal file
|
@ -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),
|
||||||
|
]
|
|
@ -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),
|
|
||||||
]
|
|
|
@ -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'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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.'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -24,6 +24,7 @@
|
||||||
Reglages généraux, machines, utilisateurs, mail, general pour l'application.
|
Reglages généraux, machines, utilisateurs, mail, general pour l'application.
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import os
|
||||||
|
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
@ -35,9 +36,8 @@ from django.forms import ValidationError
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
import machines.models
|
import machines.models
|
||||||
import cotisations.models
|
|
||||||
|
|
||||||
from re2o.mixins import AclMixin
|
from re2o.mixins import AclMixin, RevMixin
|
||||||
from re2o.aes_field import AESEncryptedField
|
from re2o.aes_field import AESEncryptedField
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -696,7 +696,7 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
|
|
||||||
|
|
||||||
def default_invoice():
|
def default_invoice():
|
||||||
tpl, _ = cotisations.models.DocumentTemplate.objects.get_or_create(
|
tpl, _ = DocumentTemplate.objects.get_or_create(
|
||||||
name="Re2o default invoice",
|
name="Re2o default invoice",
|
||||||
template="templates/default_invoice.tex"
|
template="templates/default_invoice.tex"
|
||||||
)
|
)
|
||||||
|
@ -704,7 +704,7 @@ def default_invoice():
|
||||||
|
|
||||||
|
|
||||||
def default_voucher():
|
def default_voucher():
|
||||||
tpl, _ = cotisations.models.DocumentTemplate.objects.get_or_create(
|
tpl, _ = DocumentTemplate.objects.get_or_create(
|
||||||
name="Re2o default voucher",
|
name="Re2o default voucher",
|
||||||
template="templates/default_voucher.tex"
|
template="templates/default_voucher.tex"
|
||||||
)
|
)
|
||||||
|
@ -716,14 +716,14 @@ class CotisationsOption(AclMixin, PreferencesModel):
|
||||||
verbose_name = _("cotisations options")
|
verbose_name = _("cotisations options")
|
||||||
|
|
||||||
invoice_template = models.OneToOneField(
|
invoice_template = models.OneToOneField(
|
||||||
'cotisations.DocumentTemplate',
|
'preferences.DocumentTemplate',
|
||||||
verbose_name=_("Template for invoices"),
|
verbose_name=_("Template for invoices"),
|
||||||
related_name="invoice_template",
|
related_name="invoice_template",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
default=default_invoice,
|
default=default_invoice,
|
||||||
)
|
)
|
||||||
voucher_template = models.OneToOneField(
|
voucher_template = models.OneToOneField(
|
||||||
'cotisations.DocumentTemplate',
|
'preferences.DocumentTemplate',
|
||||||
verbose_name=_("Template for subscription voucher"),
|
verbose_name=_("Template for subscription voucher"),
|
||||||
related_name="voucher_template",
|
related_name="voucher_template",
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
@ -733,3 +733,56 @@ class CotisationsOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Send voucher by email when the invoice is controlled."),
|
verbose_name=_("Send voucher by email when the invoice is controlled."),
|
||||||
default=False,
|
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)
|
||||||
|
|
|
@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td><a href="{{template.template.url}}">{{template.template}}</a></td>
|
<td><a href="{{template.template.url}}">{{template.template}}</a></td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% can_edit 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 %}
|
{% acl_end %}
|
||||||
{% history_button template %}
|
{% history_button template %}
|
||||||
</td>
|
</td>
|
|
@ -350,6 +350,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="panel panel-default" id="templates">
|
||||||
|
<div class="panel-heading" data-toggle="collapse" href="#collapse_templates">
|
||||||
|
<h4 class="panel-title">
|
||||||
|
<a><i class="fa fa-edit"></i> {% trans "Document templates" %}</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div id="collapse_templates" class="panel-collapse panel-body collapse">
|
||||||
|
{% can_create DocumentTemplate %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-document-template' %}">
|
||||||
|
<i class="fa fa-cart-plus"></i> {% trans "Add a document template" %}
|
||||||
|
</a>
|
||||||
|
{% acl_end %}
|
||||||
|
<a class="btn btn-danger btn-sm" role="button" href="{% url 'preferences:del-document-template' %}">
|
||||||
|
<i class="fa fa-trash"></i> {% trans "Delete one or several document templates" %}
|
||||||
|
</a>
|
||||||
|
{% include 'preferences/aff_document_template.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-default" id="cotisation">
|
<div class="panel panel-default" id="cotisation">
|
||||||
<div class="panel-heading" data-toggle="collapse" href="#collapse_cotisation">
|
<div class="panel-heading" data-toggle="collapse" href="#collapse_cotisation">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">
|
||||||
|
|
|
@ -111,5 +111,20 @@ urlpatterns = [
|
||||||
name='edit-switchmanagementcred'
|
name='edit-switchmanagementcred'
|
||||||
),
|
),
|
||||||
url(r'^del_switchmanagementcred/(?P<switchmanagementcredid>[0-9]+)$', views.del_switchmanagementcred, name='del-switchmanagementcred'),
|
url(r'^del_switchmanagementcred/(?P<switchmanagementcredid>[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<documenttemplateid>[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'),
|
url(r'^$', views.display_options, name='display-options'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -48,7 +48,9 @@ from .forms import (
|
||||||
ServiceForm,
|
ServiceForm,
|
||||||
ReminderForm,
|
ReminderForm,
|
||||||
RadiusKeyForm,
|
RadiusKeyForm,
|
||||||
SwitchManagementCredForm
|
SwitchManagementCredForm,
|
||||||
|
DocumentTemplateForm,
|
||||||
|
DelDocumentTemplateForm
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
Service,
|
Service,
|
||||||
|
@ -65,6 +67,7 @@ from .models import (
|
||||||
SwitchManagementCred,
|
SwitchManagementCred,
|
||||||
RadiusOption,
|
RadiusOption,
|
||||||
CotisationsOption,
|
CotisationsOption,
|
||||||
|
DocumentTemplate
|
||||||
)
|
)
|
||||||
from . import models
|
from . import models
|
||||||
from . import forms
|
from . import forms
|
||||||
|
@ -90,6 +93,7 @@ def display_options(request):
|
||||||
switchmanagementcred_list = SwitchManagementCred.objects.all()
|
switchmanagementcred_list = SwitchManagementCred.objects.all()
|
||||||
radiusoptions, _ = RadiusOption.objects.get_or_create()
|
radiusoptions, _ = RadiusOption.objects.get_or_create()
|
||||||
cotisationsoptions, _created = CotisationsOption.objects.get_or_create()
|
cotisationsoptions, _created = CotisationsOption.objects.get_or_create()
|
||||||
|
document_template_list = DocumentTemplate.objects.order_by('name')
|
||||||
return form({
|
return form({
|
||||||
'useroptions': useroptions,
|
'useroptions': useroptions,
|
||||||
'machineoptions': machineoptions,
|
'machineoptions': machineoptions,
|
||||||
|
@ -105,6 +109,7 @@ def display_options(request):
|
||||||
'switchmanagementcred_list': switchmanagementcred_list,
|
'switchmanagementcred_list': switchmanagementcred_list,
|
||||||
'radiusoptions' : radiusoptions,
|
'radiusoptions' : radiusoptions,
|
||||||
'cotisationsoptions': cotisationsoptions,
|
'cotisationsoptions': cotisationsoptions,
|
||||||
|
'document_template_list': document_template_list,
|
||||||
}, 'preferences/display_preferences.html', request)
|
}, 'preferences/display_preferences.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,3 +413,86 @@ def del_mailcontact(request, instances):
|
||||||
request
|
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)
|
||||||
|
|
Loading…
Reference in a new issue