Skip to content

Commit

Permalink
Merge pull request #362 from shibaken/main
Browse files Browse the repository at this point in the history
shibaken/main to dbca-wa/main
  • Loading branch information
jawaidm authored Oct 24, 2023
2 parents 6fb4185 + 9077f89 commit f92b9d8
Show file tree
Hide file tree
Showing 13 changed files with 494 additions and 197 deletions.
5 changes: 5 additions & 0 deletions mooringlicensing/components/approvals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ class Approval(RevisionedMixin):
(INTERNAL_STATUS_SUBMITTED, 'Mooring Site Licence application submitted'),
(INTERNAL_STATUS_APPROVED, 'Mooring Site Licence application approved'),
)
APPROVED_STATUSES = [
APPROVAL_STATUS_CURRENT,
APPROVAL_STATUS_SURRENDERED,
APPROVAL_STATUS_FULFILLED,
]
lodgement_number = models.CharField(max_length=9, blank=True, unique=True)
status = models.CharField(max_length=40, choices=STATUS_CHOICES,
default=STATUS_CHOICES[0][0])
Expand Down
21 changes: 11 additions & 10 deletions mooringlicensing/components/proposals/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
Mooring,
)
from mooringlicensing.components.proposals.serializers import (
ProposalForEndorserSerializer,
ProposalSerializer,
InternalProposalSerializer,
#SaveProposalSerializer,
Expand Down Expand Up @@ -777,12 +778,21 @@ class ProposalViewSet(viewsets.ModelViewSet):
serializer_class = ProposalSerializer
lookup_field = 'id'

def get_serializer_class(self):
if not self.kwargs.get('id').isnumeric():
# Endorser is accessing this proposal. We don't want to send all the proposal data.
return ProposalForEndorserSerializer
return super(ProposalViewSet, self).get_serializer_class()

# def retrieve(self, request, *args, **kwargs):
# return super(ProposalViewSet, self).retrieve(request, *args, **kwargs)

def get_object(self):
logger.info(f'Getting object in the ProposalViewSet...')
# obj = super(ProposalViewSet, self).get_object()
if self.kwargs.get('id').isnumeric():
obj = super(ProposalViewSet, self).get_object()
else:
# When AUP holder accesses this proposal for endorsement
uuid = self.kwargs.get('id')
obj = AuthorisedUserApplication.objects.get(uuid=uuid)
obj = obj.proposal
Expand Down Expand Up @@ -815,15 +825,6 @@ def get_queryset(self):
logger.warning("User is neither customer nor internal user: {} <{}>".format(request_user.get_full_name(), request_user.email))
return Proposal.objects.none()

# def retrieve(self, request, *args, **kwargs):
# try:
# temp = super(ProposalViewSet, self).retrieve(request, *args)
# return temp
# except Exception as e:
# uuid = kwargs.get('id')
# proposal = AuthorisedUserApplication.objects.get(uuid=uuid)
# return Response(self.serializer_class(proposal.proposal).data)

def internal_serializer_class(self):
try:
return InternalProposalSerializer
Expand Down
321 changes: 290 additions & 31 deletions mooringlicensing/components/proposals/models.py

Large diffs are not rendered by default.

103 changes: 103 additions & 0 deletions mooringlicensing/components/proposals/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,109 @@ def get_assessor_process(self,obj):
return False


# class ProposalSerializerForEndorser(serializers.ModelSerializer):
# class Meta:
# model = Proposal
# fields = (
# 'id',
# )

class ProposalForEndorserSerializer(BaseProposalSerializer):
for_endorser = serializers.SerializerMethodField()
readonly = serializers.SerializerMethodField()

class Meta:
model = Proposal
fields = (
'id',
'for_endorser',
'readonly',

'application_type_code',
'application_type_text',
'approval_type_text',
'application_type_dict',
'proposal_type',
'approval_level',
'title',
'customer_status',
'processing_status',
'applicant_type',
'applicant',
'submitter',
'assigned_officer',
'get_history',
'lodgement_date',
'modified_date',
'documents',
'requirements',
'readonly',
'can_user_edit',
'can_user_view',
'documents_url',
'lodgement_number',
'lodgement_sequence',
'can_officer_process',
'allowed_assessors',
'pending_amendment_request',
'is_amendment_proposal',
'applicant_details',
# 'fee_paid',
# 'invoices',
## vessel fields
# 'rego_no',
# 'vessel_id',
# 'vessel_details_id',
# 'vessel_ownership_id',
'vessel_type',
# 'vessel_name',
'vessel_length',
'vessel_draft',
'vessel_beam',
'vessel_weight',
'berth_mooring',
'dot_name',
# 'percentage',
# 'editable_vessel_details',
# 'individual_owner',
'insurance_choice',
# 'preferred_bay_id',
# 'silent_elector',
# 'bay_preferences_numbered',
'site_licensee_email',
'mooring_id',
'mooring_authorisation_preference',
'company_ownership_name',
'company_ownership_percentage',
'previous_application_id',
'previous_application_vessel_details_id',
'previous_application_preferred_bay_id',
'current_vessels_rego_list',
'approval_lodgement_number',
'approval_vessel_rego_no',
'waiting_list_application_id',
'authorised_user_moorings_str',
# 'temporary_document_collection_id',
'previous_application_vessel_details_obj',
'previous_application_vessel_ownership_obj',
# 'max_vessel_length_with_no_payment',
'keep_existing_mooring',
'keep_existing_vessel',
'approval_reissued',
'vessel_on_proposal',
'null_vessel_on_create',
'proposal_applicant',
'uuid',
'amendment_requests',
)

def get_readonly(self, obj):
return True

def get_for_endorser(self, obj):
return True


class ProposalSerializer(BaseProposalSerializer):
processing_status = serializers.SerializerMethodField(read_only=True)
customer_status = serializers.SerializerMethodField(read_only=True)
Expand Down
136 changes: 4 additions & 132 deletions mooringlicensing/components/proposals/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,160 +589,32 @@ def submit_vessel_data(instance, request, vessel_data):
if vessel_lookup_errors:
raise serializers.ValidationError(vessel_lookup_errors)

min_vessel_size_str = GlobalSettings.objects.get(key=GlobalSettings.KEY_MINIMUM_VESSEL_LENGTH).value
min_mooring_vessel_size_str = GlobalSettings.objects.get(key=GlobalSettings.KEY_MINUMUM_MOORING_VESSEL_LENGTH).value
min_vessel_size = float(min_vessel_size_str)
min_mooring_vessel_size = float(min_mooring_vessel_size_str)

if not vessel_data.get('rego_no'):
if instance.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT,]:
if type(instance.child_obj) in [MooringLicenceApplication, WaitingListApplication,]:
return
else:
raise serializers.ValidationError("Application cannot be submitted without a vessel listed")

## save vessel data into proposal first
# save vessel data into proposal first
save_vessel_data(instance, request, vessel_data)
vessel, vessel_details = store_vessel_data(request, vessel_data)

# associate vessel_details with proposal
instance.vessel_details = vessel_details
instance.save()

## vessel min length requirements - cannot use serializer validation due to @property vessel_applicable_length
if type(instance.child_obj) == AnnualAdmissionApplication:
if instance.vessel_details.vessel_applicable_length < min_vessel_size:
logger.error("Proposal {}: Vessel must be at least {}m in length".format(instance, min_vessel_size_str))
raise serializers.ValidationError("Vessel must be at least {}m in length".format(min_vessel_size_str))
elif type(instance.child_obj) == AuthorisedUserApplication:
if instance.vessel_details.vessel_applicable_length < min_vessel_size:
logger.error("Proposal {}: Vessel must be at least {}m in length".format(instance, min_vessel_size_str))
raise serializers.ValidationError("Vessel must be at least {}m in length".format(min_vessel_size_str))
# check new site licensee mooring
proposal_data = request.data.get('proposal') if request.data.get('proposal') else {}
mooring_id = proposal_data.get('mooring_id')
if mooring_id and proposal_data.get('site_licensee_email'):
mooring = Mooring.objects.get(id=mooring_id)
if (instance.vessel_details.vessel_applicable_length > mooring.vessel_size_limit or
instance.vessel_details.vessel_draft > mooring.vessel_draft_limit):
logger.error("Proposal {}: Vessel unsuitable for mooring".format(instance))
raise serializers.ValidationError("Vessel unsuitable for mooring")
if instance.approval:
# Amend / Renewal
if proposal_data.get('keep_existing_mooring'):
# check existing moorings against current vessel dimensions
for moa in instance.approval.mooringonapproval_set.filter(end_date__isnull=True):
if instance.vessel_details.vessel_applicable_length > moa.mooring.vessel_size_limit:
logger.error(f"Vessel applicable lentgh: [{instance.vessel_details.vessel_applicable_length}] is not suitable for the mooring: [{moa.mooring}]")
raise serializers.ValidationError(f"Vessel length: {instance.vessel_details.vessel_applicable_length}[m] is not suitable for the vessel size limit: {moa.mooring.vessel_size_limit} [m] of the mooring: [{moa.mooring}]")
if instance.vessel_details.vessel_draft > moa.mooring.vessel_draft_limit:
logger.error(f"Vessel draft: [{instance.vessel_details.vessel_draft}] is not suitable for the mooring: [{moa.mooring}]")
raise serializers.ValidationError(f"Vessel draft: {instance.vessel_details.vessel_draft} [m] is not suitable for the vessel draft limit: {moa.mooring.vessel_draft_limit} [m] of the mooring: [{moa.mooring}]")
elif type(instance.child_obj) == WaitingListApplication:
if instance.vessel_details.vessel_applicable_length < min_mooring_vessel_size:
logger.error("Proposal {}: Vessel must be at least {}m in length".format(instance, min_mooring_vessel_size_str))
raise serializers.ValidationError("Vessel must be at least {}m in length".format(min_mooring_vessel_size_str))
else:
## Mooring Licence Application
if instance.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT] and instance.vessel_details.vessel_applicable_length < min_vessel_size:
logger.error("Proposal {}: Vessel must be at least {}m in length".format(instance, min_vessel_size_str))
raise serializers.ValidationError("Vessel must be at least {}m in length".format(min_vessel_size_str))
elif instance.vessel_details.vessel_applicable_length < min_mooring_vessel_size:
logger.error("Proposal {}: Vessel must be at least {}m in length".format(instance, min_mooring_vessel_size_str))
raise serializers.ValidationError("Vessel must be at least {}m in length".format(min_mooring_vessel_size_str))
elif instance.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT] and (
instance.vessel_details.vessel_applicable_length > instance.approval.child_obj.mooring.vessel_size_limit or
instance.vessel_details.vessel_draft > instance.approval.child_obj.mooring.vessel_draft_limit
):
logger.error("Proposal {}: Vessel unsuitable for mooring".format(instance))
raise serializers.ValidationError("Vessel unsuitable for mooring")
instance.validate_vessel_length(request)

# record ownership data
vessel_ownership = store_vessel_ownership(request, vessel, instance)

# associate vessel_ownership with proposal
instance.vessel_ownership = vessel_ownership
instance.save()

association_fail = False
proposals = [proposal.child_obj for proposal in Proposal.objects.filter(vessel_details__vessel=vessel).exclude(id=instance.id)]
proposals_wla = []
proposals_mla = []
proposals_aaa = []
proposals_aua = []
# 20220311 - add exclusion for amendment applications
approvals = [ah.approval for ah in ApprovalHistory.objects.filter(end_date=None, vessel_ownership__vessel=vessel).exclude(approval_id=instance.approval_id)]
approvals = list(dict.fromkeys(approvals)) # remove duplicates
approvals_wla = []
approvals_ml = []
approvals_ml_sus = []
approvals_aap = []
approvals_aup = []
approvals_aup_sus = []
for proposal in proposals:
# if type(proposal) == WaitingListApplication and proposal.processing_status not in ['approved', 'declined', 'discarded']:
if proposal.processing_status not in [Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED,]:
if type(proposal) == WaitingListApplication:
proposals_wla.append(proposal)
if type(proposal) == MooringLicenceApplication:
proposals_mla.append(proposal)
if type(proposal) == AnnualAdmissionApplication:
proposals_aaa.append(proposal)
if type(proposal) == AuthorisedUserApplication:
proposals_aua.append(proposal)
for approval in approvals:
if approval.status in settings.APPROVED_APPROVAL_STATUS:
if type(approval.child_obj) == WaitingListAllocation:
approvals_wla.append(approval)
if type(approval.child_obj) == MooringLicence:
approvals_ml.append(approval)
if type(approval.child_obj) == AnnualAdmissionPermit:
approvals_aap.append(approval)
if type(approval.child_obj) == AuthorisedUserPermit:
approvals_aup.append(approval)

wl_applications = WaitingListApplication.get_intermediate_proposals(instance.submitter).exclude(id=instance.id)
wl_allocations = WaitingListAllocation.get_intermediate_approvals(instance.submitter).exclude(approval=instance.approval)
ml_applications = MooringLicenceApplication.get_intermediate_proposals(instance.submitter)
ml_approvals = MooringLicence.get_valid_approvals(instance.submitter)

# apply rules
if type(instance.child_obj) == WaitingListApplication and (proposals_wla or approvals_wla or proposals_mla or approvals_ml):
raise serializers.ValidationError("The vessel in the application is already listed in " +
", ".join(['{} {} '.format(proposal.description, proposal.lodgement_number) for proposal in proposals_wla]) +
", ".join(['{} {} '.format(approval.description, approval.lodgement_number) for approval in approvals_wla])
)
# Person can have only one WLA, Waiting Liast application, Mooring Licence and Mooring Licence application
elif (type(instance.child_obj) == WaitingListApplication and (
WaitingListApplication.get_intermediate_proposals(instance.submitter).exclude(id=instance.id) or
WaitingListAllocation.get_intermediate_approvals(instance.submitter).exclude(approval=instance.approval) or
MooringLicenceApplication.get_intermediate_proposals(instance.submitter) or
MooringLicence.get_valid_approvals(instance.submitter))
):
raise serializers.ValidationError("Person can have only one WLA, Waiting List application, Mooring Site Licence and Mooring Site Licence application")
elif (type(instance.child_obj) == AnnualAdmissionApplication and (proposals_aaa or approvals_aap or
proposals_aua or approvals_aup or proposals_mla or approvals_ml)):
list_sum = proposals_aaa + proposals_aua + proposals_mla + approvals_aap + approvals_aup + approvals_ml
raise serializers.ValidationError("The vessel in the application is already listed in " +
", ".join(['{} {} '.format(item.description, item.lodgement_number) for item in list_sum]))
elif type(instance.child_obj) == AuthorisedUserApplication and (proposals_aua or approvals_aup):
#association_fail = True
raise serializers.ValidationError("The vessel in the application is already listed in " +
", ".join(['{} {} '.format(proposal.description, proposal.lodgement_number) for proposal in proposals_aua]) +
", ".join(['{} {} '.format(approval.description, approval.lodgement_number) for approval in approvals_aup])
)
elif type(instance.child_obj) == MooringLicenceApplication and (proposals_mla or approvals_ml):
#association_fail = True
raise serializers.ValidationError("The vessel in the application is already listed in " +
", ".join(['{} {} '.format(proposal.description, proposal.lodgement_number) for proposal in proposals_mla]) +
", ".join(['{} {} '.format(approval.description, approval.lodgement_number) for approval in approvals_ml])
)
#if association_fail:
# raise serializers.ValidationError("This vessel is already part of another application/permit/licence")
instance.validate_against_existing_proposals_and_approvals()

## vessel ownership cannot be greater than 100%
ownership_percentage_validation(vessel_ownership, instance)
#delete_draft_vessel_data(instance)


def store_vessel_data(request, vessel_data):
logger.info(f'store_vessel_data() is called with the vessel_data: {vessel_data}')
Expand Down
Loading

0 comments on commit f92b9d8

Please sign in to comment.