Skip to content

Commit

Permalink
ICMSLST-2986 Add endpoint to link agents to access requests.
Browse files Browse the repository at this point in the history
  • Loading branch information
MattHolmes123 committed Oct 3, 2024
1 parent 5e266e0 commit 1cfc09e
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 81 deletions.
98 changes: 61 additions & 37 deletions web/domains/case/access/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,7 @@ class Meta:
]


class LinkOrgAccessRequestFormBase(ModelForm):
instance: ImporterAccessRequest | ExporterAccessRequest

def clean(self) -> dict[str, Any]:
cleaned_data = super().clean()

if self.instance.is_agent_request:
link = cleaned_data.get("link")
agent_link = cleaned_data.get("agent_link")

if agent_link and agent_link.get_main_org() != link:
self.add_error("agent_link", "Agent organisation is not linked to main org.")

return cleaned_data


class LinkImporterAccessRequestForm(LinkOrgAccessRequestFormBase):
class LinkImporterAccessRequestForm(ModelForm):
link = ModelChoiceField(
label="Link Importer",
help_text=(
Expand All @@ -91,24 +75,12 @@ class LinkImporterAccessRequestForm(LinkOrgAccessRequestFormBase):
widget=ImporterWidget,
)

agent_link = ModelChoiceField(
label="Link Agent Importer",
help_text=(
"Search an agent importer to link."
" Importers returned are matched against name, registerer number"
", eori number and user name/email."
),
queryset=Importer.objects.filter(is_active=True, main_importer__isnull=False),
widget=ImporterAgentWidget,
required=False,
)

class Meta:
model = ImporterAccessRequest
fields = ["link", "agent_link"]
fields = ["link"]


class LinkExporterAccessRequestForm(LinkOrgAccessRequestFormBase):
class LinkExporterAccessRequestForm(ModelForm):
link = ModelChoiceField(
label="Link Exporter",
help_text=(
Expand All @@ -119,20 +91,72 @@ class LinkExporterAccessRequestForm(LinkOrgAccessRequestFormBase):
widget=ExporterWidget,
)

class Meta:
model = ExporterAccessRequest
fields = ["link"]


class LinkOrgAgentFormBase(ModelForm):
instance: ImporterAccessRequest | ExporterAccessRequest

def clean(self) -> dict[str, Any]:
cleaned_data = super().clean()

link = self.instance.link
agent_link = cleaned_data.get("agent_link")

if agent_link and agent_link.get_main_org() != link:
self.add_error("agent_link", "Agent organisation is not linked to main organisation.")

return cleaned_data


class LinkImporterAgentForm(LinkOrgAgentFormBase):
class Meta:
model = ImporterAccessRequest
fields = ["agent_link"]

agent_link = ModelChoiceField(
label="Link Agent Importer",
help_text=(
"Search an agent importer to link."
" Importers returned are matched against name, registerer number"
", eori number and user name/email."
),
queryset=Importer.objects.none(),
widget=ImporterAgentWidget(attrs={"data-minimum-input-length": 0}),
required=True,
)

def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.fields["agent_link"].queryset = Importer.objects.filter(
is_active=True, main_importer=self.instance.link
)


class LinkExporterAgentForm(LinkOrgAgentFormBase):
class Meta:
model = ExporterAccessRequest
fields = ["agent_link"]

agent_link = ModelChoiceField(
label="Link Agent Exporter",
help_text=(
"Search an agent exporter to link."
" Exporters returned are matched against name and registerer number."
),
queryset=Exporter.objects.filter(is_active=True, main_exporter__isnull=False),
widget=ExporterAgentWidget,
required=False,
queryset=Exporter.objects.none(),
widget=ExporterAgentWidget(attrs={"data-minimum-input-length": 0}),
required=True,
)

class Meta:
model = ExporterAccessRequest
fields = ["link", "agent_link"]
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)

self.fields["agent_link"].queryset = Exporter.objects.filter(
is_active=True, main_exporter=self.instance.link
)


class CloseAccessRequestForm(ModelForm):
Expand Down
5 changes: 5 additions & 0 deletions web/domains/case/access/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
views.link_access_request,
name="link-request",
),
path(
"case/<int:access_request_pk>/<orgtype:entity>/link-access-request-agent/",
views.LinkOrgAgentAccessRequestUpdateView.as_view(),
name="link-access-request-agent",
),
re_path(
"^case/(?P<access_request_pk>[0-9]+)/(?P<entity>importer|exporter)/close-access-request/$",
views.close_access_request,
Expand Down
102 changes: 97 additions & 5 deletions web/domains/case/access/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.mixins import (
LoginRequiredMixin,
PermissionRequiredMixin,
UserPassesTestMixin,
)
from django.db import transaction
from django.db.models import QuerySet
from django.http import HttpResponse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils import timezone
from django.views.generic import DetailView, TemplateView
from django.utils.decorators import method_decorator
from django.views.generic import DetailView, TemplateView, UpdateView
from django_ratelimit import UNSAFE
from django_ratelimit.decorators import ratelimit

Expand All @@ -18,6 +23,7 @@
ImporterAccessRequestFilter,
)
from web.domains.case.services import case_progress, reference
from web.flow.errors import ProcessError
from web.flow.models import ProcessTypes
from web.mail import emails
from web.models import (
Expand Down Expand Up @@ -237,9 +243,15 @@ def link_access_request(
)

form = form_cls(request.POST, instance=access_request)
original_link_pk = access_request.link.pk if access_request.link else None

if form.is_valid():
form.save()
saved_ar = form.save()

# Reset agent if org changes.
if saved_ar.link.pk != original_link_pk:
saved_ar.agent_link = None
saved_ar.save()

messages.success(
request,
Expand All @@ -256,12 +268,25 @@ def link_access_request(
else:
form = form_cls(instance=access_request)

if access_request.is_agent_request:
agent_form_cls = (
forms.LinkImporterAgentForm if entity == "importer" else forms.LinkExporterAgentForm
)
agent_form = agent_form_cls(instance=access_request)
else:
agent_form = None

context = {
"case_type": "access",
"process": access_request,
"has_approval_request": has_approval_request,
"form": form,
"show_agent_link": access_request.is_agent_request,
"link_access_request_agent_url": reverse(
"access:link-access-request-agent",
kwargs={"access_request_pk": access_request_pk, "entity": entity},
),
"agent_form": agent_form,
"org": org,
"create_org_url": reverse("importer-list" if entity == "importer" else "exporter-list"),
}
Expand All @@ -271,6 +296,57 @@ def link_access_request(
)


@method_decorator(transaction.atomic, name="post")
class LinkOrgAgentAccessRequestUpdateView(
LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin, UpdateView
):
permission_required = [Perms.sys.ilb_admin]
pk_url_kwarg = "access_request_pk"
model = AccessRequest
object: ImporterAccessRequest | ExporterAccessRequest
http_method_names = ["post"]

def get_queryset(self):
# Call select_for_update for atomic post method
return super().get_queryset().select_for_update()

def get_object(
self, queryset: QuerySet[ImporterAccessRequest | ExporterAccessRequest] | None = None
) -> ImporterAccessRequest | ExporterAccessRequest:
obj = super().get_object(queryset)

return obj.get_specific_model()

def test_func(self):
# Override queryset so select_for_update is not called
access_request = self.get_object(AccessRequest.objects.all())

try:
case_progress.access_request_in_processing(access_request)
except ProcessError:
return False

return True and access_request.is_agent_request

def get_form_class(self):
entity = self.kwargs["entity"]

return forms.LinkImporterAgentForm if entity == "importer" else forms.LinkExporterAgentForm

def form_invalid(
self, form: forms.LinkImporterAgentForm | forms.LinkExporterAgentForm
) -> HttpResponseRedirect:
# This is a post only endpoint so unable to display form errors like normal.
messages.warning(self.request, "Unable to link agent.")
return redirect(self.get_success_url())

def get_success_url(self) -> str:
return reverse(
"access:link-request",
kwargs={"access_request_pk": self.object.pk, "entity": self.kwargs["entity"]},
)


@login_required
@permission_required(Perms.sys.ilb_admin, raise_exception=True)
def close_access_request(
Expand All @@ -295,6 +371,8 @@ def close_access_request(
is_active=True, status=ApprovalRequest.Statuses.OPEN
)

agent_missing = access_request.is_agent_request and not access_request.agent_link

if request.method == "POST":
if has_open_approval_request:
messages.error(
Expand All @@ -311,6 +389,19 @@ def close_access_request(
)
)

if agent_missing:
messages.error(
request,
"You cannot close this Access Request because you need to link the agent.",
)

return redirect(
reverse(
"access:close-request",
kwargs={"access_request_pk": access_request.pk, "entity": entity},
)
)

task = case_progress.get_expected_task(access_request, Task.TaskType.PROCESS)
form = forms.CloseAccessRequestForm(request.POST, instance=access_request)

Expand Down Expand Up @@ -345,6 +436,7 @@ def close_access_request(
"process": access_request,
"form": form,
"has_open_approval_request": has_open_approval_request,
"agent_missing": agent_missing,
}

return render(
Expand All @@ -354,7 +446,7 @@ def close_access_request(
)


class AccessRequestHistoryView(PermissionRequiredMixin, DetailView):
class AccessRequestHistoryView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
# DetailView Config
model = AccessRequest
pk_url_kwarg = "access_request_pk"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ <h3>Close Access Request</h3>
<div class="info-box info-box-info">
You cannot close this Access Request because you have already started the Approval Process. To close this Access Request you must first withdraw the Approval Request.
</div>
{% elif agent_missing %}
<div class="info-box info-box-info">
You cannot close this Access Request because you need to link the agent.
</div>
{% else %}
<div class="container setOutForm">
{% call forms.form(action='', method='post', csrf_input=csrf_input) -%}
Expand Down
34 changes: 25 additions & 9 deletions web/templates/web/domains/case/access/management.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,37 @@ <h3>Link Importer to Access Request</h3>
You cannot re-link this Access Request because you have already started the Approval Process. You must first Withdraw / Restart the Approval Request.
</div>
{% else %}
<p>
If the {{ org }} is not found in the below list, click <a href="{{ create_org_url }}" target="_blank" rel="noopener noreferrer">this link</a>
to create a new {{ org }} (opens in new tab) before returning here to complete the access request. You may need to reload the browser page to see the new {{ org }}.
</p>
<div class="eight columns">
{% call forms.form(method='post', csrf_input=csrf_input) -%}
{{ fields.field(form.link) }}
{% if show_agent_link %}
{{ fields.field(form.agent_link, show_optional_indicator=False) }}
{% endif %}
<p>
If the {{ org }} is not found in the above list, click <a href="{{ create_org_url }}" target="_blank" rel="noopener noreferrer">this link</a>
to create a new {{ org }} (opens in new tab) before returning here to complete the access request. You may need to reload the browser page to see the new {{ org }}.
</p>
<button type="submit" class="button primary-button">Link</button>
{% for field in form %}
{{ fields.field(field) }}
{{ forms.submit_button(btn_label="Link") }}
{% endfor %}
{% endcall %}
</div>
{% endif %}
</div>
{% if show_agent_link %}
<div class="row">
<h3>Link Agent to Access Request</h3>
{% if process.link %}
<div class="eight columns">
{% call forms.form(action=link_access_request_agent_url, method='post', csrf_input=csrf_input) -%}
{% for field in agent_form %}
{{ fields.field(field) }}
{{ forms.submit_button(btn_label="Link agent") }}
{% endfor %}
{% endcall %}
</div>
{% else %}
<div class="info-box info-box-info">You need to link the {{ org }} before linking the agent</div>
{% endif %}
</div>
{% endif %}
{% endblock %}

{% block page_js %}
Expand Down
Loading

0 comments on commit 1cfc09e

Please sign in to comment.