Skip to content

Commit

Permalink
ICMSLST-1052 Add GMP template.
Browse files Browse the repository at this point in the history
The UI is incorrect and needs to reuse the templates in the real form.
  • Loading branch information
MattHolmes123 committed Mar 1, 2024
1 parent 620942a commit fe9ff46
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 101 deletions.
2 changes: 2 additions & 0 deletions pii-ner-exclude.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3893,3 +3893,5 @@ README
CertificateOfGoodManufacturingPracticeApplicationTemplate
TemplateCls
Application Type"
Django 4.2.10
the International Accreditation Forum
26 changes: 9 additions & 17 deletions web/domains/case/export/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def __init__(self, *args, **kwargs):
type_code=ExportApplicationType.Types.MANUFACTURE
).country_group.countries.filter(is_active=True)

# Contact isn't a field in the template.
if "contact" in self.fields:
self.fields["contact"].queryset = application_contacts(self.instance)

def clean_is_pesticide_on_free_sale_uk(self):
"""Perform extra logic even thought this is the edit form where every field is optional"""

Expand Down Expand Up @@ -234,12 +238,6 @@ class EditCOMForm(OptionalFormMixin, PrepareCertManufactureFormBase):
All fields are optional to allow partial record saving.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Moved from PrepareCertManufactureFormBase to EditCOMForm as it shouldn't
# be set when creating a COM template.
self.fields["contact"].queryset = application_contacts(self.instance)


class SubmitCOMForm(EditCOMForm):
"""Form used when submitting the application.
Expand Down Expand Up @@ -270,10 +268,6 @@ def clean_is_manufacturer(self):
return val


class EditCOMTemplateForm(OptionalFormMixin, PrepareCertManufactureFormBase):
"""COM Template form."""


class EditCFSFormBase(forms.ModelForm):
class Meta:
model = CertificateOfFreeSaleApplication
Expand Down Expand Up @@ -601,7 +595,9 @@ def __init__(self, *args, **kwargs):
if self.instance.responsible_person_address_entry_type == AddressEntryType.SEARCH:
self.fields["responsible_person_address"].widget.attrs["readonly"] = True

self.fields["contact"].queryset = application_contacts(self.instance)
# Contact isn't a field in the template.
if "contact" in self.fields:
self.fields["contact"].queryset = application_contacts(self.instance)

# These are only sometimes required and will be checked in the clean method
self.fields["auditor_accredited"].required = False
Expand Down Expand Up @@ -678,16 +674,12 @@ def clean(self) -> dict[str, Any]:
return cleaned_data


class EditGMPTemplateForm(OptionalFormMixin, EditGMPFormBase):
"""GMP Template form."""


def form_class_for_application_type(type_code: str) -> type[ModelForm]:
types_forms: dict[Any, type[ModelForm]] = {
# These form classes have no required fields, no data cleaning methods.
ExportApplicationType.Types.FREE_SALE: EditCFSTemplateForm,
ExportApplicationType.Types.GMP: EditGMPTemplateForm,
ExportApplicationType.Types.MANUFACTURE: EditCOMTemplateForm,
ExportApplicationType.Types.GMP: EditGMPForm,
ExportApplicationType.Types.MANUFACTURE: EditCOMForm,
}

try:
Expand Down
74 changes: 40 additions & 34 deletions web/domains/case/export/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,37 @@ class ExportApplicationABC(models.Model):
class Meta:
abstract = True

application_type = models.ForeignKey(
"web.ExportApplicationType", on_delete=models.PROTECT, blank=False, null=False
last_update_datetime = models.DateTimeField(blank=False, null=False, auto_now=True)
variation_no = models.IntegerField(blank=False, null=False, default=0)

countries = models.ManyToManyField(
"web.Country",
help_text=(
"A certificate will be created for each country selected. You may"
" select up to 40 countries. You cannot select the same country"
" twice, you must submit a new application."
),
)

last_update_datetime = models.DateTimeField(blank=False, null=False, auto_now=True)
def is_import_application(self) -> bool:
return False


class ExportApplication(ExportApplicationABC, ApplicationBase):
class Meta:
indexes = [
models.Index(fields=["status"], name="EA_status_idx"),
BTreeIndex(
fields=["reference"],
name="EA_search_case_reference_idx",
opclasses=["text_pattern_ops"],
),
models.Index(fields=["-submit_datetime"], name="EA_submit_datetime_idx"),
]

application_type = models.ForeignKey(
"web.ExportApplicationType", on_delete=models.PROTECT, blank=False, null=False
)
last_updated_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
Expand All @@ -83,7 +108,6 @@ class Meta:
)

variation_requests = models.ManyToManyField("web.VariationRequest")
variation_no = models.IntegerField(blank=False, null=False, default=0)
case_notes = models.ManyToManyField("web.CaseNote")
further_information_requests = models.ManyToManyField("web.FurtherInformationRequest")
update_requests = models.ManyToManyField("web.UpdateRequest")
Expand Down Expand Up @@ -122,15 +146,6 @@ class Meta:
),
)

countries = models.ManyToManyField(
"web.Country",
help_text=(
"A certificate will be created for each country selected. You may"
" select up to 40 countries. You cannot select the same country"
" twice, you must submit a new application."
),
)

agent = models.ForeignKey("web.Exporter", on_delete=models.PROTECT, null=True, related_name="+")
agent_office = models.ForeignKey(
"web.Office", on_delete=models.PROTECT, null=True, related_name="+"
Expand All @@ -143,22 +158,6 @@ class Meta:
# Used in workbasket to clear applications
cleared_by = models.ManyToManyField("web.User")

def is_import_application(self) -> bool:
return False


class ExportApplication(ExportApplicationABC, ApplicationBase):
class Meta:
indexes = [
models.Index(fields=["status"], name="EA_status_idx"),
BTreeIndex(
fields=["reference"],
name="EA_search_case_reference_idx",
opclasses=["text_pattern_ops"],
),
models.Index(fields=["-submit_datetime"], name="EA_submit_datetime_idx"),
]

def get_edit_view_name(self) -> str:
if self.process_type == ProcessTypes.COM:
return "export:com-edit"
Expand Down Expand Up @@ -210,7 +209,7 @@ class Meta:

@final
class CertificateOfManufactureApplication( # type: ignore[misc]
CertificateOfManufactureApplicationABC, ExportApplication
ExportApplication, CertificateOfManufactureApplicationABC
):
PROCESS_TYPE = ProcessTypes.COM
IS_FINAL = True
Expand Down Expand Up @@ -382,10 +381,9 @@ class CFSProductActiveIngredient(models.Model):
)


@final
class CertificateOfGoodManufacturingPracticeApplication(ExportApplication):
PROCESS_TYPE = ProcessTypes.GMP
IS_FINAL = True
class CertificateOfGoodManufacturingPracticeApplicationABC(models.Model):
class Meta:
abstract = True

class CertificateTypes(TypedTextChoices):
ISO_22716 = ("ISO_22716", "ISO 22716")
Expand Down Expand Up @@ -519,6 +517,14 @@ class CountryType(TypedTextChoices):
supporting_documents = models.ManyToManyField("GMPFile")


@final
class CertificateOfGoodManufacturingPracticeApplication( # type: ignore[misc]
ExportApplication, CertificateOfGoodManufacturingPracticeApplicationABC
):
PROCESS_TYPE = ProcessTypes.GMP
IS_FINAL = True


class GMPFile(File):
class Type(TypedTextChoices):
# ISO_22716 file types
Expand Down
9 changes: 8 additions & 1 deletion web/domains/case/export/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,15 @@ def set_template_data(
:param type_code: App type.
"""

if template.application_type == ExportApplicationType.Types.MANUFACTURE:
template_data = template.com_template
elif template.application_type == ExportApplicationType.Types.GMP:
template_data = template.gmp_template
else:
raise ValueError(f"Unable to create template for app type: {template.application_type}")

# Get data that we can save in the real application
data = model_to_dict(template.com_template, exclude=["id", "template"])
data = model_to_dict(template_data, exclude=["id", "template"])
form_class = form_class_for_application_type(type_code)
form = form_class(instance=application, data=data)

Expand Down
30 changes: 22 additions & 8 deletions web/domains/cat/forms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from collections.abc import Iterable

import django_filters
from django import forms

from web.domains.case.export.forms import PrepareCertManufactureFormBase
from web.forms.mixins import OptionalFormMixin
from web.domains.case.export.forms import EditCOMForm, EditGMPForm
from web.models import (
CertificateApplicationTemplate,
CertificateOfGoodManufacturingPracticeApplicationTemplate,
CertificateOfManufactureApplicationTemplate,
ExportApplicationType,
)
Expand Down Expand Up @@ -42,11 +44,23 @@ class Meta:
widgets = {"description": forms.Textarea({"rows": 4})}


class CertificateOfManufactureTemplateForm(OptionalFormMixin, PrepareCertManufactureFormBase):
class Meta:
fields = [f for f in PrepareCertManufactureFormBase.Meta.fields if f != "contact"]
help_texts = PrepareCertManufactureFormBase.Meta.help_texts
labels = PrepareCertManufactureFormBase.Meta.labels
widgets = PrepareCertManufactureFormBase.Meta.widgets
def copy_form_fields(form_fields: Iterable[str], *exclude: str) -> list[str]:
"""Return a copy of the supplied form fields excluding values in exclude"""

return [f for f in form_fields if f not in exclude]


class CertificateOfManufactureTemplateForm(EditCOMForm):
class Meta:
fields = copy_form_fields(EditCOMForm.Meta.fields, "contact")
help_texts = EditCOMForm.Meta.help_texts
labels = EditCOMForm.Meta.labels
widgets = EditCOMForm.Meta.widgets
model = CertificateOfManufactureApplicationTemplate


class CertificateOfGoodManufacturingPracticeTemplateForm(EditGMPForm):
class Meta:
model = CertificateOfGoodManufacturingPracticeApplicationTemplate
fields = copy_form_fields(EditGMPForm.Meta.fields, "contact")
widgets = EditGMPForm.Meta.widgets
26 changes: 7 additions & 19 deletions web/domains/cat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db import models

from web.domains.case.export.models import (
CertificateOfGoodManufacturingPracticeApplicationABC,
CertificateOfManufactureApplicationABC,
ExportApplicationABC,
)
Expand Down Expand Up @@ -62,24 +63,6 @@ def user_can_edit(self, user: "User") -> bool:
class CertificateOfManufactureApplicationTemplate( # type: ignore[misc]
ExportApplicationABC, CertificateOfManufactureApplicationABC
):
# Relationships to ignore from ExportApplicationABC
application_type = None
last_updated_by = None
variation_requests = None
case_notes = None
further_information_requests = None
update_requests = None
case_emails = None
submitted_by = None
created_by = None
exporter = None
exporter_office = None
contact = None
agent = None
agent_office = None
case_owner = None
cleared_by = None

template = models.OneToOneField(
"web.CertificateApplicationTemplate", on_delete=models.CASCADE, related_name="com_template"
)
Expand All @@ -88,4 +71,9 @@ class CertificateOfManufactureApplicationTemplate( # type: ignore[misc]
class CertificateOfFreeSaleApplicationTemplate: ... # noqa: E701


class CertificateOfGoodManufacturingPracticeApplicationTemplate: ... # noqa: E701
class CertificateOfGoodManufacturingPracticeApplicationTemplate( # type: ignore[misc]
ExportApplicationABC, CertificateOfGoodManufacturingPracticeApplicationABC
):
template = models.OneToOneField(
"web.CertificateApplicationTemplate", on_delete=models.CASCADE, related_name="gmp_template"
)
Loading

0 comments on commit fe9ff46

Please sign in to comment.