diff --git a/client/package.json b/client/package.json index 1d13ee857..893a8b907 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "haztrak", - "version": "0.1.0", + "version": "0.3.0", "private": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "^6.2.1", diff --git a/client/src/components/HandlerDetails/HandlerDetails.tsx b/client/src/components/HandlerDetails/HandlerDetails.tsx index d344d1343..eabc0a701 100644 --- a/client/src/components/HandlerDetails/HandlerDetails.tsx +++ b/client/src/components/HandlerDetails/HandlerDetails.tsx @@ -27,7 +27,7 @@ function HandlerDetails({ handler }: HandlerDetailsProps) {

{handler.name}

- {handler.electronicSignaturesInfo && ( + {handler.signed && (

signed

diff --git a/client/src/components/ManifestForm/ManifestForm.tsx b/client/src/components/ManifestForm/ManifestForm.tsx index d48b0bcfb..76208b918 100644 --- a/client/src/components/ManifestForm/ManifestForm.tsx +++ b/client/src/components/ManifestForm/ManifestForm.tsx @@ -9,6 +9,7 @@ import { WasteLineTable } from 'components/ManifestForm/WasteLine/WasteLineTable import React, { useEffect, useState } from 'react'; import { Button, Col, Row } from 'react-bootstrap'; import { FormProvider, SubmitHandler, useFieldArray, useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom'; import htApi from 'services'; import { addMsg, useAppDispatch } from 'store'; import { Manifest } from 'types/Manifest'; @@ -21,6 +22,8 @@ import AddWasteLine from './WasteLine'; interface ManifestFormProps { readOnly?: boolean; manifestData?: Manifest; + siteId?: string; + mtn?: string; } /** @@ -28,12 +31,13 @@ interface ManifestFormProps { * as the current method of viewing manifest when the form is read only. * @constructor */ -function ManifestForm({ readOnly, manifestData }: ManifestFormProps) { +function ManifestForm({ readOnly, manifestData, siteId, mtn }: ManifestFormProps) { // Top level ManifestForm methods and objects const manifestMethods = useForm({ values: manifestData }); // On load, focus the generator EPA ID. useEffect(() => manifestMethods.setFocus('generator.epaSiteId'), []); const dispatch = useAppDispatch(); + const navigate = useNavigate(); const onSubmit: SubmitHandler = (data: Manifest) => { // ToDo: on submit, validate the user input htApi @@ -63,10 +67,8 @@ function ManifestForm({ readOnly, manifestData }: ManifestFormProps) { ); }); }; - // Generator controls const generator: ManifestHandler = manifestMethods.getValues('generator'); - console.log(generator); // Transporter controls const [transFormShow, setTransFormShow] = useState(false); @@ -95,9 +97,11 @@ function ManifestForm({ readOnly, manifestData }: ManifestFormProps) { <> -

{`${ - manifestData?.manifestTrackingNumber || 'Draft' - } Manifest`}

+
+

{`${ + manifestData?.manifestTrackingNumber || 'Draft' + } Manifest`}

+
@@ -293,13 +297,16 @@ function ManifestForm({ readOnly, manifestData }: ManifestFormProps) {
- {readOnly ? ( - <> - ) : ( - - )} + +
; @@ -28,8 +30,8 @@ function TransporterTable({ transporters, arrayFieldMethods, readOnly }: Transpo # EPA ID Name - e-Signer? - Edit + Signed + {readOnly ? <> : Edit} @@ -39,7 +41,13 @@ function TransporterTable({ transporters, arrayFieldMethods, readOnly }: Transpo {transporter.order} {transporter.epaSiteId} {transporter.name} - {transporter.canEsign ? 'yes' : 'no'} + + {transporter.signed ? ( + + ) : ( + + )} + {readOnly ? ( <> diff --git a/client/src/features/manifest/ManifestDetails.tsx b/client/src/features/manifest/ManifestDetails.tsx index b6257c401..6e8f24ce0 100644 --- a/client/src/features/manifest/ManifestDetails.tsx +++ b/client/src/features/manifest/ManifestDetails.tsx @@ -7,7 +7,7 @@ import { useParams } from 'react-router-dom'; import { Manifest } from 'types/Manifest'; function ManifestDetails() { - const { mtn, action } = useParams(); + const { mtn, action, siteId } = useParams(); useTitle(`${mtn}`); const [manifestData, loading, error] = useHtAPI(`trak/manifest/${mtn}`); @@ -21,7 +21,7 @@ function ManifestDetails() { return loading ? ( ) : manifestData ? ( - + ) : ( <> ); diff --git a/client/src/types/Handler/handler.ts b/client/src/types/Handler/handler.ts index d57d52cda..27b9cabbc 100644 --- a/client/src/types/Handler/handler.ts +++ b/client/src/types/Handler/handler.ts @@ -50,13 +50,25 @@ export interface Handler { gisPrimary?: boolean; } +/** + * The Signature that appears on paper versions of the manifest + */ +export interface PaperSignature { + printedName: string; + signatureDate: string; +} + /** * The ManifestHandler extends to Handler schema and adds manifest specific data */ export interface ManifestHandler extends Handler { - // paperSignatureInfo: PaperSignature + paperSignatureInfo?: PaperSignature; electronicSignaturesInfo?: Array; siteType?: 'Generator' | 'Broken' | 'Transporter' | 'Tsdf'; + /** + * Property on by back end to signify whether the handler has signed + */ + signed?: boolean; } /** diff --git a/server/apps/trak/models/address_model.py b/server/apps/trak/models/address_model.py index da1f05cfa..029025545 100644 --- a/server/apps/trak/models/address_model.py +++ b/server/apps/trak/models/address_model.py @@ -1,8 +1,10 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from apps.trak.models.base_model import TrakBaseModel -class Address(models.Model): + +class Address(TrakBaseModel): """ Used to capture RCRAInfo address instances (mail, site). """ @@ -122,9 +124,3 @@ def __str__(self): if self.street_number: return f"{self.street_number} {self.address1}" return f" {self.address1}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/base_model.py b/server/apps/trak/models/base_model.py index b63e126f5..e753ae3ce 100644 --- a/server/apps/trak/models/base_model.py +++ b/server/apps/trak/models/base_model.py @@ -3,12 +3,34 @@ from django.db import models -class TrakManager(models.Manager, metaclass=ABCMeta): +class TrakBaseManager(models.Manager, metaclass=ABCMeta): """ Abstract class that defines an interface for our model managers """ + def __repr__(self): + field_values = ", ".join( + f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields + ) + return f"<{self.__class__.__name__}({field_values})>" + @abstractmethod def save(self, **kwargs) -> models.QuerySet: """Save instance and its related models to the database""" return self.create(**kwargs) + + +class TrakBaseModel(models.Model): + """Base class for all apps.trak models""" + + def __str__(self): + return f"{self.__class__.__name__}" + + def __repr__(self): + field_values = ", ".join( + f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields + ) + return f"<{self.__class__.__name__}({field_values})>" + + class Meta: + abstract = True diff --git a/server/apps/trak/models/contact_model.py b/server/apps/trak/models/contact_model.py index 9c80bc868..f6a7dbc63 100644 --- a/server/apps/trak/models/contact_model.py +++ b/server/apps/trak/models/contact_model.py @@ -4,7 +4,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from apps.trak.models.base_model import TrakManager +from apps.trak.models.base_model import TrakBaseManager, TrakBaseModel class EpaPhoneNumber(models.CharField): @@ -41,7 +41,7 @@ def __str__(self): return f"{self.number}" -class ContactManager(TrakManager): +class ContactManager(TrakBaseManager): """ Inter-model related functionality for Contact Model """ @@ -61,7 +61,7 @@ def save(self, **contact_data) -> models.QuerySet: return super().save(**contact_data) -class Contact(models.Model): +class Contact(TrakBaseModel): """ RCRAInfo contact including personnel information such as name, email, company, includes a phone related field. @@ -108,9 +108,3 @@ def __str__(self): return f"{first.capitalize()} {middle.capitalize()} {last.capitalize()}" except AttributeError: return f"contact {self.pk}: {self.first_name} {self.middle_initial} {self.last_name}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/handler_model.py b/server/apps/trak/models/handler_model.py index 68b1824ac..a9d2ae474 100644 --- a/server/apps/trak/models/handler_model.py +++ b/server/apps/trak/models/handler_model.py @@ -5,14 +5,14 @@ from django.db import models from .address_model import Address -from .base_model import TrakManager +from .base_model import TrakBaseManager, TrakBaseModel from .contact_model import Contact, EpaPhone from .signature_model import ESignature, PaperSignature logger = logging.getLogger(__name__) -class HandlerManager(TrakManager): +class HandlerManager(TrakBaseManager): """ Inter-model related functionality for Handler Model """ @@ -50,12 +50,6 @@ def save(self, **handler_data): except KeyError as exc: logger.warning(f"error while creating handler {exc}") - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" - def get_emergency_phone(self) -> Union[EpaPhone, None]: """Check if emergency phone is present and create an EpaPhone row""" try: @@ -78,7 +72,7 @@ def get_address(self, key) -> Address: raise ValidationError(exc) -class Handler(models.Model): +class Handler(TrakBaseModel): """ RCRAInfo Handler model definition for entities on the uniform hazardous waste manifests """ @@ -105,12 +99,12 @@ class Handler(models.Model): max_length=200, ) site_address = models.ForeignKey( - Address, + "Address", on_delete=models.CASCADE, related_name="site_address", ) mail_address = models.ForeignKey( - Address, + "Address", on_delete=models.CASCADE, related_name="mail_address", ) @@ -123,12 +117,12 @@ class Handler(models.Model): blank=True, ) contact = models.ForeignKey( - Contact, + "Contact", on_delete=models.CASCADE, verbose_name="Contact Information", ) emergency_phone = models.ForeignKey( - EpaPhone, + "EpaPhone", on_delete=models.CASCADE, null=True, blank=True, @@ -160,14 +154,8 @@ class Handler(models.Model): def __str__(self): return f"{self.epa_id}" - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" - -class ManifestHandlerManager(TrakManager): +class ManifestHandlerManager(TrakBaseManager): """ Inter-model related functionality for ManifestHandler Model """ @@ -183,12 +171,15 @@ def save(self, **handler_data) -> models.QuerySet: try: if Handler.objects.filter(epa_id=handler_data["handler"]["epa_id"]).exists(): handler = Handler.objects.get(epa_id=handler_data["handler"]["epa_id"]) + handler_data.pop("handler") logger.debug(f"using existing Handler {handler}") else: - handler = Handler.objects.save(**handler_data["handler"]) + handler = Handler.objects.save(**handler_data.pop("handler")) logger.debug(f"Handler created {handler}") - manifest_handler = ManifestHandler.objects.create( - handler=handler, paper_signature=paper_signature + manifest_handler = self.model.objects.create( + handler=handler, + paper_signature=paper_signature, + **handler_data, ) logger.debug(f"ManifestHandler created {manifest_handler}") for e_signature_data in e_signatures: @@ -204,7 +195,7 @@ def save(self, **handler_data) -> models.QuerySet: raise exc -class ManifestHandler(models.Model): +class ManifestHandler(TrakBaseModel): """ ManifestHandler which contains a reference to hazardous waste handler and data specific to that handler on the given manifest. @@ -225,11 +216,12 @@ class ManifestHandler(models.Model): help_text="The signature associated with hazardous waste custody exchange", ) + @property + def signed(self) -> bool: + """Returns True if one of the signature types is present""" + e_signature_exists = ESignature.objects.filter(manifest_handler=self).exists() + paper_signature_exists = self.paper_signature is not None + return paper_signature_exists or e_signature_exists + def __str__(self): return f"ManifestHandler: {self.handler.epa_id}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/manifest_model.py b/server/apps/trak/models/manifest_model.py index f378418ba..326405ebc 100644 --- a/server/apps/trak/models/manifest_model.py +++ b/server/apps/trak/models/manifest_model.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from apps.trak.models import ManifestHandler -from apps.trak.models.base_model import TrakManager +from apps.trak.models.base_model import TrakBaseManager, TrakBaseModel from .transporter_model import Transporter from .waste_model import WasteLine @@ -37,7 +37,7 @@ def validate_mtn(value): ) -class ManifestManager(TrakManager): +class ManifestManager(TrakBaseManager): """ Inter-model related functionality for Manifest Model """ @@ -76,7 +76,7 @@ def save(self, **manifest_data: Dict): return manifest -class Manifest(models.Model): +class Manifest(TrakBaseModel): """ Model definition the e-Manifest Uniform Hazardous Waste Manifest """ @@ -278,7 +278,7 @@ def __str__(self): return f"{self.mtn}" -class AdditionalInfo(models.Model): +class AdditionalInfo(TrakBaseModel): """ Entity containing Additional Information. Relevant to Both Manifest and individual WastesLines. Shipment rejection related info is stored in this object. @@ -327,9 +327,3 @@ class Meta: def __str__(self): return f"{self.original_mtn or 'Unknown'}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/rcra_profile_model.py b/server/apps/trak/models/rcra_profile_model.py index 2b52476aa..130167655 100644 --- a/server/apps/trak/models/rcra_profile_model.py +++ b/server/apps/trak/models/rcra_profile_model.py @@ -1,8 +1,10 @@ from django.contrib.auth.models import User from django.db import models +from apps.trak.models.base_model import TrakBaseModel -class RcraProfile(models.Model): + +class RcraProfile(TrakBaseModel): """ Provides the user's RcraProfile information, excluding their RCRAInfo API key (see ProfileUpdateSerializer). Has a one-to-one relationship with @@ -38,12 +40,6 @@ class RcraProfile(models.Model): def __str__(self): return f"{self.user.username}" - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" - def sync(self): """Launch task to sync use profile. ToDo: remove this method""" from ..tasks import sync_user_sites diff --git a/server/apps/trak/models/signature_model.py b/server/apps/trak/models/signature_model.py index bba44585d..f07cd0caf 100644 --- a/server/apps/trak/models/signature_model.py +++ b/server/apps/trak/models/signature_model.py @@ -3,12 +3,12 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from apps.trak.models.base_model import TrakManager +from apps.trak.models.base_model import TrakBaseManager, TrakBaseModel logger = logging.getLogger(__name__) -class Signer(models.Model): +class Signer(TrakBaseModel): """EPA manifest signer definition""" class Role(models.TextChoices): @@ -77,7 +77,7 @@ def __str__(self): ) -class ESignatureManager(TrakManager): +class ESignatureManager(TrakBaseManager): """ Inter-model related functionality for ESignature Model """ @@ -92,7 +92,7 @@ def save(self, **e_signature_data): return super().save(**e_signature_data) -class ESignature(models.Model): +class ESignature(TrakBaseModel): """EPA electronic signature""" objects = ESignatureManager() @@ -138,17 +138,11 @@ def __str__(self): ) return f"e-signature on {self.sign_date}" - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" - class Meta: verbose_name = "e-Signature" -class PaperSignature(models.Model): +class PaperSignature(TrakBaseModel): """ Contains printed name of the handler Signee and Date of Signature for paper manifests. @@ -162,11 +156,5 @@ class PaperSignature(models.Model): def __str__(self): return f"{self.printed_name} ({self.sign_date.strftime('%Y-%m-%d %H:%M:%S')})" - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" - def __hash__(self): return hash((self.printed_name, self.sign_date)) diff --git a/server/apps/trak/models/site_model.py b/server/apps/trak/models/site_model.py index 0877752c4..0495228d4 100644 --- a/server/apps/trak/models/site_model.py +++ b/server/apps/trak/models/site_model.py @@ -1,10 +1,11 @@ from django.core.validators import MinValueValidator from django.db import models +from .base_model import TrakBaseModel from .handler_model import Handler -class Site(models.Model): +class Site(TrakBaseModel): """ Haztrak Site model used to control access to Handler object. @@ -30,9 +31,3 @@ class Site(models.Model): def __str__(self): return f"{self.epa_site.epa_id}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/site_permission_model.py b/server/apps/trak/models/site_permission_model.py index 88d014f8b..5ad04f24d 100644 --- a/server/apps/trak/models/site_permission_model.py +++ b/server/apps/trak/models/site_permission_model.py @@ -1,6 +1,7 @@ from django.core.exceptions import ValidationError from django.db import models +from .base_model import TrakBaseModel from .rcra_profile_model import RcraProfile from .site_model import Site @@ -11,7 +12,7 @@ ] -class SitePermission(models.Model): +class SitePermission(TrakBaseModel): """ RCRAInfo Site Permissions per module connected to a user's RcraProfile and the corresponding Site @@ -50,13 +51,6 @@ class SitePermission(models.Model): def __str__(self): return f"{self.profile.user}: {self.site}" - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - - return f"<{self.__class__.__name__}({field_values})>" - def clean(self): if self.site_manager: fields = ["annual_report", "biennial_report", "e_manifest", "my_rcra_id", "wiets"] diff --git a/server/apps/trak/models/transporter_model.py b/server/apps/trak/models/transporter_model.py index 3af746bc8..4c49c18b0 100644 --- a/server/apps/trak/models/transporter_model.py +++ b/server/apps/trak/models/transporter_model.py @@ -2,7 +2,7 @@ from django.db import models -from .handler_model import Handler, ManifestHandler, ManifestHandlerManager +from .handler_model import ManifestHandler, ManifestHandlerManager class TransporterManager(ManifestHandlerManager): @@ -15,9 +15,7 @@ def save(self, **transporter_data: Dict): Create a Transporter from a manifest instance and handler dict ToDo: fix this implementation """ - handler_data = transporter_data.pop("handler") - handler = Handler.objects.save(**handler_data) - return self.create(handler=handler, **transporter_data) + return super().save(**transporter_data) class Transporter(ManifestHandler): @@ -36,9 +34,3 @@ class Transporter(ManifestHandler): def __str__(self): return f"{self.handler.epa_id}: transporter {self.order} on {self.manifest.mtn}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"<{self.__class__.__name__}({field_values})>" diff --git a/server/apps/trak/models/waste_model.py b/server/apps/trak/models/waste_model.py index d63fb98ba..dead2f33a 100644 --- a/server/apps/trak/models/waste_model.py +++ b/server/apps/trak/models/waste_model.py @@ -1,9 +1,9 @@ from django.db import models -from apps.trak.models.base_model import TrakManager +from apps.trak.models.base_model import TrakBaseManager, TrakBaseModel -class WasteLineManager(TrakManager): +class WasteLineManager(TrakBaseManager): """ Inter-model related functionality for Contact Model """ @@ -12,7 +12,7 @@ def save(self, **waste_data) -> models.QuerySet: return super().save(**waste_data) -class WasteLine(models.Model): +class WasteLine(TrakBaseModel): """ ToDo: Every place we have as a JSON field likely should be stored in separate table Model definition for hazardous waste listed on a uniform hazardous waste manifest. @@ -80,9 +80,3 @@ class WasteLine(models.Model): def __str__(self): return f"{self.manifest} line {self.line_number}" - - def __repr__(self): - field_values = ", ".join( - f"{field.name}={getattr(self, field.name)!r}" for field in self._meta.fields - ) - return f"" diff --git a/server/apps/trak/serializers/address_ser.py b/server/apps/trak/serializers/address_ser.py index 471e172ba..33fedd0af 100644 --- a/server/apps/trak/serializers/address_ser.py +++ b/server/apps/trak/serializers/address_ser.py @@ -36,6 +36,7 @@ class AddressSerializer(TrakBaseSerializer): streetNumber = serializers.CharField( source="street_number", required=False, + allow_blank=True, ) state = LocalityField( choices=Address.EpaStates.choices, diff --git a/server/apps/trak/serializers/handler_ser.py b/server/apps/trak/serializers/handler_ser.py index 63dd339cc..1bfb710b5 100644 --- a/server/apps/trak/serializers/handler_ser.py +++ b/server/apps/trak/serializers/handler_ser.py @@ -66,6 +66,9 @@ class HandlerSerializer(TrakBaseSerializer): default=False, ) + def update(self, instance, validated_data): + return self.Meta.model.objects.save(**validated_data) + def create(self, validated_data): return self.Meta.model.objects.save(**validated_data) @@ -101,6 +104,7 @@ class ManifestHandlerSerializer(HandlerSerializer): source="paper_signature", required=False, ) + signed = serializers.ReadOnlyField() def update(self, instance, validated_data: Dict): return self.Meta.model.objects.update(instance, **validated_data) @@ -122,6 +126,8 @@ def to_internal_value(self, data: Dict): if "paperSignatureInfo" in data: instance["paperSignatureInfo"] = data.pop("paperSignatureInfo") instance["handler"] = data + if "order" in instance["handler"]: + instance["order"] = instance["handler"]["order"] return super().to_internal_value(instance) class Meta: @@ -130,4 +136,5 @@ class Meta: "handler", "electronicSignaturesInfo", "paperSignatureInfo", + "signed", ] diff --git a/server/apps/trak/serializers/manifest_ser.py b/server/apps/trak/serializers/manifest_ser.py index b2167d848..64cd61627 100644 --- a/server/apps/trak/serializers/manifest_ser.py +++ b/server/apps/trak/serializers/manifest_ser.py @@ -36,6 +36,7 @@ class AdditionalInfoSerializer(serializers.ModelSerializer): ) handlingInstructions = serializers.CharField( allow_null=True, + allow_blank=True, required=False, source="handling_instructions", ) diff --git a/server/apps/trak/serializers/transporter_ser.py b/server/apps/trak/serializers/transporter_ser.py index 7a6ef9ea6..c1c5bee78 100644 --- a/server/apps/trak/serializers/transporter_ser.py +++ b/server/apps/trak/serializers/transporter_ser.py @@ -1,37 +1,28 @@ -from apps.trak.models import Transporter -from apps.trak.serializers.handler_ser import HandlerSerializer +from rest_framework.exceptions import ValidationError -from .base_ser import TrakBaseSerializer +from apps.trak.models import Transporter +from apps.trak.serializers.handler_ser import ManifestHandlerSerializer -class TransporterSerializer(TrakBaseSerializer): +class TransporterSerializer(ManifestHandlerSerializer): """ Transporter model serializer for JSON marshalling/unmarshalling """ - handler = HandlerSerializer() - class Meta: model = Transporter fields = [ "handler", "order", + "paperSignatureInfo", + "electronicSignaturesInfo", + "signed", ] - def to_representation(self, obj): - """flatten handler foreign key to transporter representation.""" - representation = super().to_representation(obj) - profile_representation = representation.pop("handler") - for key in profile_representation: - representation[key] = profile_representation[key] - return representation - def to_internal_value(self, data): """Move fields related to handler to an internal handler dictionary.""" - handler_internal = {} - for key in HandlerSerializer.Meta.fields: - if key in data: - handler_internal[key] = data.pop(key) - data["handler"] = handler_internal - internal = super().to_internal_value(data) - return internal + try: + internal = super().to_internal_value(data) + return internal + except ValidationError as exc: + raise exc diff --git a/server/apps/trak/services/site_service.py b/server/apps/trak/services/site_service.py index ed89fc6a2..ed3d0ce3b 100644 --- a/server/apps/trak/services/site_service.py +++ b/server/apps/trak/services/site_service.py @@ -39,7 +39,7 @@ def sync_rcra_manifest(self, *, site_id: str = None) -> Dict[str, List[str]]: ) # ToDo: uncomment this after we have manifest development fixtures # limit the number of manifest to sync at a time - tracking_numbers = tracking_numbers[0:10] + tracking_numbers = tracking_numbers[0:5] logger.info(f"Pulling {tracking_numbers} from RCRAInfo") results: Dict[str, List[str]] = manifest_service.pull_manifests( tracking_numbers=tracking_numbers diff --git a/server/apps/trak/tests/conftest.py b/server/apps/trak/tests/conftest.py index 54c3b2c20..fb92b562f 100644 --- a/server/apps/trak/tests/conftest.py +++ b/server/apps/trak/tests/conftest.py @@ -224,12 +224,16 @@ def create_e_signature( @pytest.fixture -def manifest_handler_factory(db, handler_factory): +def manifest_handler_factory(db, handler_factory, paper_signature_factory): """Abstract factory for Haztrak ManifestHandler model""" - def create_manifest_handler(handler: Optional[Handler] = None) -> ManifestHandler: + def create_manifest_handler( + handler: Optional[Handler] = None, + paper_signature: Optional[PaperSignature] = None, + ) -> ManifestHandler: return ManifestHandler.objects.create( handler=handler or handler_factory(), + paper_signature=paper_signature or paper_signature_factory(), ) return create_manifest_handler diff --git a/server/apps/trak/tests/models/test_handler.py b/server/apps/trak/tests/models/test_handler.py index 1dd624496..d3374df58 100644 --- a/server/apps/trak/tests/models/test_handler.py +++ b/server/apps/trak/tests/models/test_handler.py @@ -12,3 +12,29 @@ def test_manager_creates_new_handler(self, handler_serializer) -> None: ManifestHandler.objects.save(handler=handler_serializer.validated_data) new_handler = Handler.objects.get(epa_id=handler_serializer.validated_data["epa_id"]) assert isinstance(new_handler, Handler) + + def test_signed_both_signatures_exists(self, manifest_handler_factory, e_signature_factory): + manifest_handler = manifest_handler_factory() + e_signature_factory(manifest_handler=manifest_handler) + assert manifest_handler.signed is True + + def test_signed_only_paper(self, manifest_handler_factory): + manifest_handler = manifest_handler_factory() + assert manifest_handler.signed is True + + def test_signed_only_electronic(self, handler_factory, e_signature_factory): + manifest_handler = ManifestHandler( + handler=handler_factory(), + paper_signature=None, + ) + manifest_handler.save() + e_signature_factory(manifest_handler=manifest_handler) + assert manifest_handler.signed is True + + def test_signed_with_no_signatures(self, handler_factory): + manifest_handler = ManifestHandler( + handler=handler_factory(), + paper_signature=None, + ) + manifest_handler.save() + assert manifest_handler.signed is False diff --git a/server/pyproject.toml b/server/pyproject.toml index 215a7f1b2..040dc5f55 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "haztrak" -version = "0.2.0" +version = "0.3.0" description = "An open-source web app illustrating how waste management software can interface with RCRAInfo to track hazardous waste" readme = "README.md" authors = [