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