1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-06-20 17:41:55 +02:00

Lock invoices, delete them

This commit is contained in:
Yohann D'ANELLO
2020-08-07 11:04:54 +02:00
parent 1fb14ea33d
commit 679ac3a652
10 changed files with 352 additions and 161 deletions

View File

@ -2359,6 +2359,22 @@
"description": "Voir toutes les notes"
}
},
{
"model": "permission.permission",
"pk": 151,
"fields": {
"model": [
"treasury",
"invoice"
],
"query": "{}",
"type": "delete",
"mask": 3,
"field": "",
"permanent": false,
"description": "Supprimer une facture"
}
},
{
"model": "permission.role",
"pk": 1,
@ -2539,7 +2555,8 @@
143,
146,
147,
150
150,
151
]
}
},
@ -2695,7 +2712,8 @@
147,
148,
149,
150
150,
151
]
}
},

View File

@ -4,9 +4,8 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from django import forms
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from note_kfet.inputs import DatePickerInput, AmountInput
from note_kfet.inputs import AmountInput
from .models import Invoice, Product, Remittance, SpecialTransactionProxy
@ -16,19 +15,25 @@ class InvoiceForm(forms.ModelForm):
Create and generate invoices.
"""
# Django forms don't support date fields. We have to add it manually
date = forms.DateField(
initial=timezone.now,
widget=DatePickerInput(),
)
def clean(self):
if self.instance and self.instance.locked:
for field_name in self.fields:
self.cleaned_data[field_name] = getattr(self.instance, field_name)
self.errors.clear()
return self.cleaned_data
return super().clean()
def clean_date(self):
self.instance.date = self.data.get("date")
return self.instance.date
def save(self, commit=True):
"""
If the invoice is locked, don't save it
"""
if not self.instance.locked:
super().save(commit)
return self.instance
class Meta:
model = Invoice
exclude = ('bde', 'tex', )
exclude = ('bde', 'date', 'tex', )
class ProductForm(forms.ModelForm):

View File

@ -61,6 +61,13 @@ class Invoice(models.Model):
acquitted = models.BooleanField(
verbose_name=_("Acquitted"),
default=False,
)
locked = models.BooleanField(
verbose_name=_("Locked"),
help_text=_("An invoice can't be edited when it is locked."),
default=False,
)
tex = models.TextField(
@ -74,6 +81,12 @@ class Invoice(models.Model):
The advantage is to never change the template.
Warning: editing this model regenerate the tex source, so be careful.
"""
old_invoice = Invoice.objects.filter(id=self.id)
if old_invoice.exists():
if old_invoice.get().locked:
raise ValidationError(_("This invoice is locked and can no longer be edited."))
products = self.products.all()
self.place = "Gif-sur-Yvette"
@ -103,7 +116,7 @@ class Product(models.Model):
invoice = models.ForeignKey(
Invoice,
on_delete=models.PROTECT,
on_delete=models.CASCADE,
related_name="products",
verbose_name=_("invoice"),
)

View File

@ -14,19 +14,39 @@ class InvoiceTable(tables.Table):
"""
List all invoices.
"""
id = tables.LinkColumn("treasury:invoice_update",
args=[A("pk")],
text=lambda record: _("Invoice #{:d}").format(record.id), )
id = tables.LinkColumn(
"treasury:invoice_update",
args=[A("pk")],
text=lambda record: _("Invoice #{:d}").format(record.id),
)
invoice = tables.LinkColumn("treasury:invoice_render",
verbose_name=_("Invoice"),
args=[A("pk")],
accessor="pk",
text="",
attrs={
'a': {'class': 'fa fa-file-pdf-o'},
'td': {'data-turbolinks': 'false'}
})
invoice = tables.LinkColumn(
"treasury:invoice_render",
verbose_name=_("Invoice"),
args=[A("pk")],
accessor="pk",
text="",
attrs={
'a': {'class': 'fa fa-file-pdf-o'},
'td': {'data-turbolinks': 'false'}
}
)
delete = tables.LinkColumn(
'treasury:invoice_delete',
args=[A('pk')],
verbose_name=_("delete"),
text=_("Delete"),
attrs={
'th': {
'id': 'delete-membership-header'
},
'a': {
'class': 'btn btn-danger',
'data-type': 'delete-membership'
}
},
)
class Meta:
attrs = {

View File

@ -3,9 +3,9 @@
from django.urls import path
from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceRenderView, RemittanceListView,\
RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView, UnlinkTransactionToRemittanceView,\
SogeCreditListView, SogeCreditManageView
from .views import InvoiceCreateView, InvoiceListView, InvoiceUpdateView, InvoiceDeleteView, InvoiceRenderView,\
RemittanceListView, RemittanceCreateView, RemittanceUpdateView, LinkTransactionToRemittanceView,\
UnlinkTransactionToRemittanceView, SogeCreditListView, SogeCreditManageView
app_name = 'treasury'
urlpatterns = [
@ -13,6 +13,7 @@ urlpatterns = [
path('invoice/', InvoiceListView.as_view(), name='invoice_list'),
path('invoice/create/', InvoiceCreateView.as_view(), name='invoice_create'),
path('invoice/<int:pk>/', InvoiceUpdateView.as_view(), name='invoice_update'),
path('invoice/<int:pk>/delete/', InvoiceDeleteView.as_view(), name='invoice_delete'),
path('invoice/render/<int:pk>/', InvoiceRenderView.as_view(), name='invoice_render'),
# Remittance app paths

View File

@ -18,7 +18,7 @@ from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, UpdateView, DetailView
from django.views.generic.base import View, TemplateView
from django.views.generic.edit import BaseFormView
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView
from note.models import SpecialTransaction, NoteSpecial, Alias
from note_kfet.settings.base import BASE_DIR
@ -97,14 +97,20 @@ class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
form.helper = FormHelper()
# Remove form tag on the generation of the form in the template (already present on the template)
form.helper.form_tag = False
# Fill the intial value for the date field, with the initial date of the model instance
form.fields['date'].initial = form.instance.date
# The formset handles the set of the products
form_set = ProductFormSet(instance=form.instance)
form_set = ProductFormSet(instance=self.object)
context['formset'] = form_set
context['helper'] = ProductFormSetHelper()
context['no_cache'] = True
if self.object.locked:
for field_name in form.fields:
form.fields[field_name].disabled = True
for f in form_set.forms:
for field_name in f.fields:
f.fields[field_name].disabled = True
return context
def form_valid(self, form):
@ -131,6 +137,17 @@ class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
return reverse_lazy('treasury:invoice_list')
class InvoiceDeleteView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
"""
Delete a non-validated WEI registration
"""
model = Invoice
extra_context = {"title": _("Delete invoice")}
def get_success_url(self):
return reverse_lazy('treasury:invoice_list')
class InvoiceRenderView(LoginRequiredMixin, View):
"""
Render Invoice as a generated PDF with the given information and a LaTeX template