SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 70da6ae6 authored by George Katsikas's avatar George Katsikas :goat:
Browse files

link orphaned subsidyattachments to payments

parent ae34c4e9
No related branches found
No related tags found
No related merge requests found
...@@ -5,7 +5,6 @@ __license__ = "AGPL v3" ...@@ -5,7 +5,6 @@ __license__ = "AGPL v3"
from django import forms from django import forms
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from django.core.validators import EmailValidator from django.core.validators import EmailValidator
from django.forms import CharField
class HTMXInlineCRUDModelForm(forms.ModelForm): class HTMXInlineCRUDModelForm(forms.ModelForm):
...@@ -27,7 +26,7 @@ class MultiEmailValidator(EmailValidator): ...@@ -27,7 +26,7 @@ class MultiEmailValidator(EmailValidator):
# Should not be an Email field because browser validation is unwanted. # Should not be an Email field because browser validation is unwanted.
class MultiEmailField(CharField): class MultiEmailField(forms.CharField):
default_validators = [MultiEmailValidator()] default_validators = [MultiEmailValidator()]
......
...@@ -290,14 +290,8 @@ class SubsidyPaymentForm(forms.ModelForm): ...@@ -290,14 +290,8 @@ class SubsidyPaymentForm(forms.ModelForm):
class SubsidyAttachmentInlineLinkForm(forms.ModelForm): class SubsidyAttachmentInlineLinkForm(forms.ModelForm):
class Meta: class Meta:
model = SubsidyAttachment model = SubsidyAttachment
fields = [ fields = []
"subsidy",
]
filename = forms.CharField(
label="Filename",
required=True,
)
subsidy = forms.ModelChoiceField( subsidy = forms.ModelChoiceField(
queryset=Subsidy.objects.all(), queryset=Subsidy.objects.all(),
widget=HTMXDynSelWidget( widget=HTMXDynSelWidget(
...@@ -312,57 +306,72 @@ class SubsidyAttachmentInlineLinkForm(forms.ModelForm): ...@@ -312,57 +306,72 @@ class SubsidyAttachmentInlineLinkForm(forms.ModelForm):
required=False, 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): def __init__(self, *args, **kwargs):
super().__init__(*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 = FormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
Div( Div(
Div(Field("filename"), css_class="col-6 col"), Div(Field("subsidy"), css_class="col-5 col"),
Div(Field("subsidy"), css_class="col-6 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", css_class="row mb-0",
) )
) )
self.fields["filename"].initial = self.instance.filename def clean_subsidy(self):
return
def clean(self): def clean(self):
orphaned = self.cleaned_data.get("subsidy") is None return self.cleaned_data
filename = self.cleaned_data["filename"]
# Allow misnamed orphans
if orphaned:
return
filename_regex = ( def save(self):
"^SciPost_" # Link to payment
"[0-9]{4,}(-[0-9]{4,})?_[A-Z]{2,}_[\w]+_" if subsidy_payment := self.cleaned_data["subsidy_payment"]:
"(Agreement|Invoice|ProofOfPayment|Other)" if attachment_type := self.cleaned_data["payment_attachment_type"]:
"(-[0-9]{2,})?(_[\w]+)?\.(pdf|docx|png)$" setattr(subsidy_payment, attachment_type, self.instance)
)
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, commit=True): self.instance.subsidy = subsidy_payment.subsidy
instance: "SubsidyAttachment" = super().save(commit=False)
filename = self.cleaned_data["filename"] subsidy_payment.save()
old_relative_path = instance.attachment.name self.instance.save()
new_relative_path = instance.attachment.name.replace(
instance.filename, filename
)
instance.attachment.storage.save(new_relative_path, instance.attachment) return self.instance
instance.attachment.name = new_relative_path
instance.save()
instance.attachment.storage.delete(old_relative_path)
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): class SubsidyAttachmentForm(forms.ModelForm):
......
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<form hx-post="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}" {% with subsidy_id=request.POST.subsidy %}
hx-trigger="change delay:1000ms" <form hx-post="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}"
hx-swap="outerHTML"> hx-trigger="change delay:1000ms"
{% crispy form %} 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> </form>
<div class="row border-bottom"> <div class="row border-bottom">
<div class="col-auto d-flex flex-column justify-content-between"> <div class="col d-flex flex-row mb-3">
<span>{{ attachment.get_kind_display }}</span> <div class="me-2">
<span>{{ attachment.date|date:"SHORT_DATE_FORMAT" }}</span> <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:subsidyattachment_update' pk=attachment.id %}"><span class="text-warning">Update</span></a>
<a href="{% url 'finances:subsidy_attachment' attachment_id=attachment.id %}">View</a> <a href="{% url 'finances:subsidy_attachment' attachment_id=attachment.id %}">View</a>
</div> </div>
<div class="col" <div class="col-12"
hx-get="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}" hx-get="{% url "finances:_hx_subsidyattachment_link_form" attachment_id=attachment.id %}"
hx-trigger="revealed once"> hx-trigger="revealed once">
<div class="spinner-grow spinner-grow-sm ms-2" <div class="spinner-grow spinner-grow-sm ms-2"
......
...@@ -581,7 +581,22 @@ def _hx_subsidyattachment_list_page(request): ...@@ -581,7 +581,22 @@ def _hx_subsidyattachment_list_page(request):
def _hx_subsidyattachment_link_form(request, attachment_id): def _hx_subsidyattachment_link_form(request, attachment_id):
attachment = get_object_or_404(SubsidyAttachment, pk=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(): if form.is_valid():
form.save() form.save()
...@@ -589,7 +604,9 @@ def _hx_subsidyattachment_link_form(request, attachment_id): ...@@ -589,7 +604,9 @@ def _hx_subsidyattachment_link_form(request, attachment_id):
"attachment": attachment, "attachment": attachment,
"form": form, "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): class HXDynselSubsidyResultPage(HXDynselResultPage):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment