From 70da6ae67a1e7b25bb2e8565b234c0583ddf9bd7 Mon Sep 17 00:00:00 2001 From: George Katsikas <giorgakis.katsikas@gmail.com> Date: Thu, 14 Mar 2024 13:52:37 +0100 Subject: [PATCH] link orphaned subsidyattachments to payments --- scipost_django/common/forms.py | 3 +- scipost_django/finances/forms.py | 91 ++++++++++--------- .../_hx_subsidyattachment_link_form.html | 19 +++- .../_hx_subsidyattachment_list_item.html | 25 ++++- scipost_django/finances/views.py | 21 ++++- 5 files changed, 106 insertions(+), 53 deletions(-) diff --git a/scipost_django/common/forms.py b/scipost_django/common/forms.py index 97d602e06..4ee0c4376 100644 --- a/scipost_django/common/forms.py +++ b/scipost_django/common/forms.py @@ -5,7 +5,6 @@ __license__ = "AGPL v3" from django import forms from crispy_forms.helper import FormHelper from django.core.validators import EmailValidator -from django.forms import CharField class HTMXInlineCRUDModelForm(forms.ModelForm): @@ -27,7 +26,7 @@ class MultiEmailValidator(EmailValidator): # Should not be an Email field because browser validation is unwanted. -class MultiEmailField(CharField): +class MultiEmailField(forms.CharField): default_validators = [MultiEmailValidator()] diff --git a/scipost_django/finances/forms.py b/scipost_django/finances/forms.py index e1a203af9..3cfa2f395 100644 --- a/scipost_django/finances/forms.py +++ b/scipost_django/finances/forms.py @@ -290,14 +290,8 @@ class SubsidyPaymentForm(forms.ModelForm): class SubsidyAttachmentInlineLinkForm(forms.ModelForm): class Meta: model = SubsidyAttachment - fields = [ - "subsidy", - ] + fields = [] - filename = forms.CharField( - label="Filename", - required=True, - ) subsidy = forms.ModelChoiceField( queryset=Subsidy.objects.all(), widget=HTMXDynSelWidget( @@ -312,57 +306,72 @@ class SubsidyAttachmentInlineLinkForm(forms.ModelForm): required=False, ) + subsidy_payment = forms.ModelChoiceField( + queryset=SubsidyPayment.objects.none(), + widget=forms.RadioSelect(), + required=False, + ) + + payment_attachment_type = forms.ChoiceField( + choices=( + ("proof_of_payment", "Proof of payment"), + ("invoice", "Invoice"), + ), + widget=forms.RadioSelect(), + ) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.fields["payment_attachment_type"].initial = "proof_of_payment" + + # Set the queryset to the payments of the subsidy if the subsidy is set + if subsidy := self.initial.get("subsidy"): + self.fields["subsidy_payment"].queryset = subsidy.payments.all() + if subsidy_payment := self.initial.get("subsidy_payment"): + self.fields["subsidy_payment"].initial = subsidy_payment + + if inferred_subsidy := getattr(subsidy_payment, "subsidy", None): + self.fields["subsidy"].initial = inferred_subsidy + self.fields["subsidy_payment"].queryset = ( + inferred_subsidy.payments.all() + ) + self.helper = FormHelper() self.helper.layout = Layout( Div( - Div(Field("filename"), css_class="col-6 col"), - Div(Field("subsidy"), css_class="col-6 col"), + Div(Field("subsidy"), css_class="col-5 col"), + Div(Field("payment_attachment_type"), css_class="col-2 col"), + Div(Field("subsidy_payment"), css_class="col-5 col"), css_class="row mb-0", ) ) - self.fields["filename"].initial = self.instance.filename + def clean_subsidy(self): + return def clean(self): - orphaned = self.cleaned_data.get("subsidy") is None - filename = self.cleaned_data["filename"] - - # Allow misnamed orphans - if orphaned: - return + return self.cleaned_data - filename_regex = ( - "^SciPost_" - "[0-9]{4,}(-[0-9]{4,})?_[A-Z]{2,}_[\w]+_" - "(Agreement|Invoice|ProofOfPayment|Other)" - "(-[0-9]{2,})?(_[\w]+)?\.(pdf|docx|png)$" - ) - pattern = re.compile(filename_regex) - if not pattern.match(filename): - self.add_error( - "filename", - "The filename does not match the required regex pattern " - f"'{filename_regex}'", - ) + def save(self): + # Link to payment + if subsidy_payment := self.cleaned_data["subsidy_payment"]: + if attachment_type := self.cleaned_data["payment_attachment_type"]: + setattr(subsidy_payment, attachment_type, self.instance) - def save(self, commit=True): - instance: "SubsidyAttachment" = super().save(commit=False) + self.instance.subsidy = subsidy_payment.subsidy - filename = self.cleaned_data["filename"] - old_relative_path = instance.attachment.name - new_relative_path = instance.attachment.name.replace( - instance.filename, filename - ) + subsidy_payment.save() + self.instance.save() - instance.attachment.storage.save(new_relative_path, instance.attachment) - instance.attachment.name = new_relative_path - instance.save() - instance.attachment.storage.delete(old_relative_path) + return self.instance - return instance + def clean_subsidy_payment(self): + if subsidy_payment := self.cleaned_data["subsidy_payment"]: + subsidy_payment = SubsidyPayment.objects.get(id=subsidy_payment.id) + else: + self.add_error("subsidy_payment", "Please select a payment") + return subsidy_payment class SubsidyAttachmentForm(forms.ModelForm): diff --git a/scipost_django/finances/templates/finances/_hx_subsidyattachment_link_form.html b/scipost_django/finances/templates/finances/_hx_subsidyattachment_link_form.html index 1c68cc170..e4978f5bf 100644 --- a/scipost_django/finances/templates/finances/_hx_subsidyattachment_link_form.html +++ b/scipost_django/finances/templates/finances/_hx_subsidyattachment_link_form.html @@ -1,7 +1,18 @@ {% load crispy_forms_tags %} -<form hx-post="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}" - hx-trigger="change delay:1000ms" - hx-swap="outerHTML"> - {% crispy form %} +{% with subsidy_id=request.POST.subsidy %} + <form hx-post="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}" + hx-trigger="change delay:1000ms" + hx-swap="outerHTML"> + {% crispy form %} + + {% if subsidy_id %} + <div id="subsidy-{{ subsidy_id }}-payment-form"> + <a class="btn btn-sm btn-primary" + hx-get="{% url 'finances:_hx_subsidypayment_form' subsidy_id=subsidy_id %}" + hx-target="#subsidy-{{ subsidy_id }}-payment-form">Add a new payment</a> + </div> + {% endif %} + + {% endwith %} </form> diff --git a/scipost_django/finances/templates/finances/_hx_subsidyattachment_list_item.html b/scipost_django/finances/templates/finances/_hx_subsidyattachment_list_item.html index 73e89a671..52affdfe2 100644 --- a/scipost_django/finances/templates/finances/_hx_subsidyattachment_list_item.html +++ b/scipost_django/finances/templates/finances/_hx_subsidyattachment_list_item.html @@ -1,11 +1,28 @@ <div class="row border-bottom"> - <div class="col-auto d-flex flex-column justify-content-between"> - <span>{{ attachment.get_kind_display }}</span> - <span>{{ attachment.date|date:"SHORT_DATE_FORMAT" }}</span> + <div class="col d-flex flex-row mb-3"> + <div class="me-2"> + <div class="text-muted">Kind</div> + <div>{{ attachment.get_kind_display }}</div> + </div> + <div class="me-2"> + <div class="text-muted">Date</div> + <div>{{ attachment.date }}</div> + </div> + <div class="me-2"> + <div class="text-muted">Filename</div> + <div>{{ attachment.filename }}</div> + </div> + <div class="me-2 overflow-hidden text-trunctate"> + <div class="text-muted">Description</div> + <div>{{ attachment.description }}</div> + </div> + + </div> + <div class="col-auto d-flex flex-column"> <a href="{% url 'finances:subsidyattachment_update' pk=attachment.id %}"><span class="text-warning">Update</span></a> <a href="{% url 'finances:subsidy_attachment' attachment_id=attachment.id %}">View</a> </div> - <div class="col" + <div class="col-12" hx-get="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}" hx-trigger="revealed once"> <div class="spinner-grow spinner-grow-sm ms-2" diff --git a/scipost_django/finances/views.py b/scipost_django/finances/views.py index 8ad2a5632..909499c80 100644 --- a/scipost_django/finances/views.py +++ b/scipost_django/finances/views.py @@ -581,7 +581,22 @@ def _hx_subsidyattachment_list_page(request): def _hx_subsidyattachment_link_form(request, attachment_id): attachment = get_object_or_404(SubsidyAttachment, pk=attachment_id) - form = SubsidyAttachmentInlineLinkForm(request.POST or None, instance=attachment) + subsidy_id = request.POST.get("subsidy", None) + subsidy = Subsidy.objects.get(pk=subsidy_id) if subsidy_id else None + subsidy_payment_id = request.POST.get("subsidy_payment", None) + subsidy_payment = ( + SubsidyPayment.objects.get(pk=subsidy_payment_id) + if subsidy_payment_id + else None + ) + form = SubsidyAttachmentInlineLinkForm( + request.POST or None, + instance=attachment, + initial={ + "subsidy": subsidy or getattr(attachment, "subsidy", None), + "subsidy_payment": subsidy_payment, + }, + ) if form.is_valid(): form.save() @@ -589,7 +604,9 @@ def _hx_subsidyattachment_link_form(request, attachment_id): "attachment": attachment, "form": form, } - return render(request, "finances/_hx_subsidyattachment_link_form.html", context) + return TemplateResponse( + request, "finances/_hx_subsidyattachment_link_form.html", context + ) class HXDynselSubsidyResultPage(HXDynselResultPage): -- GitLab