SciPost Code Repository

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

make various improvements to subsidy list

add status of up to date to subsidies

fix #142
parent b72fadac
No related branches found
No related tags found
No related merge requests found
......@@ -27,10 +27,12 @@ SUBSIDY_PROMISED = "promised"
SUBSIDY_INVOICED = "invoiced"
SUBSIDY_RECEIVED = "received"
SUBSIDY_WITHDRAWN = "withdrawn"
SUBSIDY_UPTODATE = "uptodate"
SUBSIDY_STATUS = (
(SUBSIDY_PROMISED, "promised"),
(SUBSIDY_INVOICED, "invoiced"),
(SUBSIDY_RECEIVED, "received"),
(SUBSIDY_WITHDRAWN, "withdrawn"),
(SUBSIDY_PROMISED, "Promised"),
(SUBSIDY_INVOICED, "Invoiced"),
(SUBSIDY_RECEIVED, "Received"),
(SUBSIDY_WITHDRAWN, "Withdrawn"),
(SUBSIDY_UPTODATE, "Up to date"),
)
......@@ -7,16 +7,20 @@ import re
from django import forms
from django.contrib.auth import get_user_model
from django.utils.dates import MONTHS
from django.db.models import Q, Sum
from django.db.models import Q, Case, DateField, Sum, Value, When, F
from django.utils import timezone
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Div, Field, ButtonHolder, Submit
from crispy_forms.bootstrap import InlineRadios
from crispy_bootstrap5.bootstrap5 import FloatingField
from dal import autocomplete
from dateutil.rrule import rrule, MONTHLY
from finances.constants import (
SUBSIDY_STATUS,
SUBSIDY_TYPE_SPONSORSHIPAGREEMENT,
SUBSIDY_TYPES,
)
from organizations.models import Organization
from scipost.fields import UserModelChoiceField
......@@ -65,14 +69,34 @@ class SubsidySearchForm(forms.Form):
required=False,
label="Country name or code",
)
ordering = forms.ChoiceField(
status = forms.MultipleChoiceField(
label="Status",
choices=SUBSIDY_STATUS,
required=False,
)
type = forms.MultipleChoiceField(
choices=SUBSIDY_TYPES,
required=False,
)
orderby = forms.ChoiceField(
label="Order by",
choices=(
("amount", "Amount"),
("date_from", "Date from"),
("date_until", "Date until"),
("annot_renewal_action_date", "Renewal date"),
),
initial="date_from",
widget=forms.RadioSelect,
required=False,
)
ordering = forms.ChoiceField(
label="Ordering",
choices=(
# FIXME: Emperically, the ordering appers to be reversed for dates?
("-", "Ascending"),
("+", "Descending"),
),
required=False,
)
def __init__(self, *args, **kwargs):
......@@ -80,10 +104,19 @@ class SubsidySearchForm(forms.Form):
self.helper = FormHelper()
self.helper.layout = Layout(
Div(
Div(FloatingField("organization_query"), css_class="col-lg-5"),
Div(FloatingField("country"), css_class="col-lg-3"),
Div(InlineRadios("ordering"), css_class="col-lg-4"),
css_class="row",
Div(
Div(FloatingField("organization_query"), css_class="col-12"),
Div(
Div(FloatingField("country"), css_class="col-12 col-lg-4"),
Div(FloatingField("orderby"), css_class="col-6 col-lg-4"),
Div(FloatingField("ordering"), css_class="col-6 col-lg-4"),
css_class="row mb-0",
),
css_class="col-12 col-lg",
),
Div(Field("status", size=6), css_class="col-12 col-lg-auto"),
Div(Field("type", size=6), css_class="col-12 col-lg-auto"),
css_class="row mb-0",
),
)
......@@ -92,6 +125,19 @@ class SubsidySearchForm(forms.Form):
subsidies = Subsidy.objects.all()
else:
subsidies = Subsidy.objects.obtained()
# Include `renewal_action_date` property in queryset
subsidies = subsidies.annotate(
annot_renewal_action_date=Case(
When(
Q(subsidy_type=SUBSIDY_TYPE_SPONSORSHIPAGREEMENT),
then=F("date_until") - datetime.timedelta(days=122),
),
default=Value(None),
output_field=DateField(),
)
)
if self.cleaned_data["organization_query"]:
subsidies = subsidies.filter(
Q(organization__name__icontains=self.cleaned_data["organization_query"])
......@@ -105,13 +151,30 @@ class SubsidySearchForm(forms.Form):
subsidies = subsidies.filter(
organization__country__icontains=self.cleaned_data["country"],
)
if self.cleaned_data["ordering"]:
if self.cleaned_data["ordering"] == "amount":
subsidies = subsidies.order_by("-amount")
if self.cleaned_data["ordering"] == "date_from":
subsidies = subsidies.order_by("-date_from")
if self.cleaned_data["ordering"] == "date_until":
subsidies = subsidies.order_by("-date_until")
if status := self.cleaned_data["status"]:
subsidies = subsidies.filter(status__in=status)
if subsidy_type := self.cleaned_data["type"]:
subsidies = subsidies.filter(subsidy_type__in=subsidy_type)
# Ordering of subsidies
# Only order if both fields are set
if (orderby_value := self.cleaned_data.get("orderby")) and (
ordering_value := self.cleaned_data.get("ordering")
):
# Remove the + from the ordering value, causes a Django error
ordering_value = ordering_value.replace("+", "")
# Ordering string is built by the ordering (+/-), and the field name
# from the orderby field split by "," and joined together
subsidies = subsidies.order_by(
*[
ordering_value + order_part
for order_part in orderby_value.split(",")
]
)
return subsidies
......@@ -123,22 +186,27 @@ class SubsidyPaymentForm(forms.ModelForm):
"reference",
"amount",
"date_scheduled",
"invoice",
"proof_of_payment",
)
widgets = {
"date_scheduled": forms.DateInput(attrs={"type": "date"}),
}
invoice = forms.ChoiceField(required=False)
proof_of_payment = forms.ChoiceField(required=False)
def __init__(self, *args, **kwargs):
subsidy = kwargs.pop("subsidy")
super().__init__(*args, **kwargs)
self.fields["subsidy"].initial = subsidy
self.fields["subsidy"].widget = forms.HiddenInput()
self.fields["invoice"].queryset = subsidy.attachments.invoices()
self.fields[
"proof_of_payment"
].queryset = subsidy.attachments.proofs_of_payment()
self.fields["invoice"].choices = [
(att.id, f"{att.attachment.name.split('/')[-1]}")
for att in subsidy.attachments.invoices()
]
self.fields["proof_of_payment"].choices = [
(att.id, f"{att.attachment.name.split('/')[-1]}")
for att in subsidy.attachments.proofs_of_payment()
]
self.helper = FormHelper()
self.helper.layout = Layout(
Field("subsidy"),
......
# Generated by Django 3.2.18 on 2023-11-13 17:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('finances', '0027_remove_subsidyattachment_name'),
]
operations = [
migrations.AlterField(
model_name='subsidy',
name='status',
field=models.CharField(choices=[('promised', 'Promised'), ('invoiced', 'Invoiced'), ('received', 'Received'), ('withdrawn', 'Withdrawn'), ('uptodate', 'Up to date')], max_length=32),
),
]
{% extends 'finances/base.html' %}
{% load crispy_forms_tags %}
{% block meta_description %}{{ block.super }} Subsidies List{% endblock meta_description %}
{% block pagetitle %}: Subsidies{% endblock pagetitle %}
{% block meta_description %}
{{ block.super }} Subsidies List
{% endblock meta_description %}
{% block pagetitle %}
: Subsidies
{% endblock pagetitle %}
{% load static %}
{% load bootstrap %}
{% block breadcrumb_items %}
{{ block.super }}
<span class="breadcrumb-item">Subsidies</span>
{% endblock %}
{% block breadcrumb_items %}{{ block.super }} <span class="breadcrumb-item">Subsidies</span>{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<h1 class="highlight">Subsidies</h1>
{% if perms.scipost.can_manage_subsidies %}
<ul>
<li><a href="{% url 'finances:subsidy_create' %}">Add a Subsidy</a></li>
<li><a href="{% url 'finances:subsidies_old' %}" target="_blank">Go to the old list page</a></li>
</ul>
<ul>
<li>
<a href="{% url 'finances:subsidy_create' %}">Add a Subsidy</a>
</li>
<li>
<a href="{% url 'finances:subsidies_old' %}" target="_blank">Go to the old list page</a>
</li>
</ul>
{% endif %}
</div>
</div>
<div class="row">
<div class="col">
<div class="card mb-2">
<div class="card-header">
Search / filter
<span id="indicator-subsidy-list"
class="htmx-indicator p-2"
>
<button class="btn btn-warning" type="button" disabled>
<strong>Loading...</strong>
<div class="spinner-grow spinner-grow-sm ms-2" role="status" aria-hidden="true"></div>
</button>
</span>
</div>
<div class="card-body">
<form
id="subsidy-search-form"
hx-post="{% url 'finances:_hx_subsidy_list' %}"
hx-trigger="load, keyup delay:500ms, change"
hx-target="#subsidy-table-tbody"
hx-indicator="#indicator-subsidy-list"
>
{% crispy form %}
</form>
</div>
<div class="card-header d-flex flex-row align-items-center justify-content-between">
<span class="fs-5">Search / filter</span>
<span>
<span id="indicator-subsidy-list" class="htmx-indicator p-2">
<button class="btn btn-warning" type="button" disabled>
<strong>Loading...</strong>
<div class="spinner-grow spinner-grow-sm ms-2"
role="status"
aria-hidden="true"></div>
</button>
</span>
<button id="refresh-button" class="m-2 btn btn-primary">
{% include "bi/arrow-clockwise.html" %}
&nbsp;Refresh
</button>
</span>
</div>
<div class="card-body">
<form id="subsidy-search-form"
hx-post="{% url 'finances:_hx_subsidy_list' %}"
hx-trigger="load, keyup delay:500ms, change, click from:#refresh-button"
hx-target="#subsidy-table-tbody"
hx-indicator="#indicator-subsidy-list">
{% crispy form %}
</form>
</div>
</div>
<table class="table">
<thead class="table-light">
<tr>
<th>From Organization</th>
<th>Type</th>
<th>Amount</th>
<th>From</th>
<th>Until</th>
{% if perms.scipost.can_manage_subsidies %}
<th>Status</th>
<th><span class="small" style="writing-mode: vertical-lr;">Renewable?</span></th>
<th><span class="small" style="writing-mode: vertical-lr;">Renewed?</span></th>
<th>Renewal<br/>action date</th>
{% endif %}
</tr>
<tbody id="subsidy-table-tbody">
</tbody>
</table>
</div>
</div>
<thead class="table-light">
<tr>
<th>From Organization</th>
<th>Type</th>
<th>Amount</th>
<th>From</th>
<th>Until</th>
{% endblock content %}
{% if perms.scipost.can_manage_subsidies %}
<th>Status</th>
<th>
<span class="small" style="writing-mode: vertical-lr;">Renewable?</span>
</th>
<th>
<span class="small" style="writing-mode: vertical-lr;">Renewed?</span>
</th>
<th>
Renewal
<br />
action date
</th>
{% endif %}
</tr>
<tbody id="subsidy-table-tbody">
</tbody>
</table>
</div>
</div>
{% endblock content %}
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