Skip to content

Commit

Permalink
Merge pull request #389 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 Feb 1, 2024
2 parents 4b7333d + 7cd25ae commit b16ebff
Show file tree
Hide file tree
Showing 11 changed files with 135 additions and 14 deletions.
49 changes: 47 additions & 2 deletions mooringlicensing/components/approvals/api.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import re
import traceback
from django.db.models import Q, Min, CharField, Value
from django.db.models.functions import Concat
from django.core.paginator import Paginator, EmptyPage
from confy import env
import datetime
Expand Down Expand Up @@ -389,6 +392,15 @@ def get_queryset(self):
if target_email_user_id:
target_user = EmailUser.objects.get(id=target_email_user_id)
all = all.filter(Q(submitter=target_user.id))

for_swap_moorings_modal = self.request.GET.get('for_swap_moorings_modal', 'false')
for_swap_moorings_modal = True if for_swap_moorings_modal.lower() in ['true', 'yes', 'y', ] else False
if for_swap_moorings_modal:
all = all.filter(
Q(current_proposal__processing_status__in=[Proposal.PROCESSING_STATUS_APPROVED,]) &
Q(status__in=[Approval.APPROVAL_STATUS_CURRENT, Approval.APPROVAL_STATUS_SUSPENDED,])
)
logger.debug(f'{all.count()}')
return all
elif is_customer(self.request):
qs = all.filter(Q(submitter=request_user.id))
Expand Down Expand Up @@ -1144,6 +1156,36 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
class StickerFilterBackend(DatatablesFilterBackend):
def filter_queryset(self, request, queryset, view):
total_count = queryset.count()
search_term = request.GET.get('search[value]', '')

# Custom fullname search
pattern = re.compile(r'\S\s+')
qs_stickers1 = Sticker.objects.none()
qs_stickers2 = Sticker.objects.none()
if pattern.search(search_term):
# Only when the search term has a space after a some text(first_name), then perform custome query because we just want to perform full_name search.

# Search sticker.approval.submitter by fullname
email_user_ids = EmailUser.objects.annotate(
custom_term=Concat(
"first_name",
Value(" "),
"last_name",
output_field=CharField(),
)
).filter(custom_term__icontains=search_term).values_list('id', flat=True)
qs_stickers1 = queryset.filter(approval__in=Approval.objects.filter(submitter__in=list(email_user_ids)))

# Search sticker.approval.current_proposal.proposalapplicant by fullname
proposal_applicants = ProposalApplicant.objects.annotate(
custom_term=Concat(
"first_name",
Value(" "),
"last_name",
output_field=CharField(),
)
).filter(custom_term__icontains=search_term).values_list('id', flat=True)
qs_stickers2 = queryset.filter(approval__current_proposal__proposalapplicant__in=proposal_applicants)

# Filter by approval types (wla, aap, aup, ml)
filter_approval_type = request.GET.get('filter_approval_type')
Expand All @@ -1164,7 +1206,6 @@ def filter_queryset(self, request, queryset, view):
# fee_season = FeeSeason.objects.get(id=filter_fee_season_id)
# queryset = queryset.filter(fee_constructor__fee_season=fee_season)
filter_year = request.GET.get('filter_year')
logger.debug(f'filter_year: {filter_year}')
if filter_year and not filter_year.lower() == 'all':
filter_year = datetime.strptime(filter_year, '%Y-%m-%d').date()
fee_seasons = FeePeriod.objects.filter(start_date=filter_year).values_list('fee_season')
Expand All @@ -1189,7 +1230,11 @@ def filter_queryset(self, request, queryset, view):
except Exception as e:
print(e)
setattr(view, '_datatables_total_count', total_count)
logger.debug(f'queryset count(): [{queryset.count()}]')

# Merge with the custom search
queryset = queryset.union(qs_stickers1)
queryset = queryset.union(qs_stickers2)

return queryset


Expand Down
28 changes: 28 additions & 0 deletions mooringlicensing/components/approvals/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,34 @@ def send_approval_surrender_email_notification(approval, request=None, already_s
_log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user)


def send_swap_moorings_application_created_notification(mooring_licence, request):
email = TemplateEmailBase(
subject=f'Swap moorings application created',
html_template='mooringlicensing/emails_2/swap_moorings_application_created.html',
txt_template='mooringlicensing/emails_2/swap_moorings_application_created.txt',
)
proposal = mooring_licence.current_proposal

context = {
'dashboard_external_url': get_public_url(request),
'recipient': mooring_licence.submitter_obj,
}
all_ccs = []
all_bccs = []
attachments = []

all_bccs = proposal.assessor_recipients

msg = email.send(proposal.submitter_obj.email, cc=all_ccs, bcc=all_bccs, context=context, attachments=attachments)
if msg:
sender = request.user if request else settings.DEFAULT_FROM_EMAIL
_log_approval_email(msg, mooring_licence, sender=sender, attachments=attachments)
if mooring_licence.org_applicant:
_log_org_email(msg, mooring_licence.org_applicant, proposal.submitter_obj, sender=sender)
else:
_log_user_email(msg, mooring_licence.submitter_obj, proposal.submitter_obj, sender=sender)


def send_approval_reinstate_email_notification(approval, request):
# 31 Reinstated
# email to licence/permit holder when licence/permit is reinstated or when suspension ends
Expand Down
25 changes: 21 additions & 4 deletions mooringlicensing/components/approvals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
send_aup_revoked_due_to_mooring_swap_email,
# send_auth_user_no_moorings_notification,
send_auth_user_mooring_removed_notification,
send_swap_moorings_application_created_notification,
)
from mooringlicensing.helpers import is_customer
from mooringlicensing.settings import PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_NEW
Expand Down Expand Up @@ -735,7 +736,7 @@ def amend_or_renew(self):
elif type(self.child_obj) == MooringLicence:
customer_status_choices = [Proposal.CUSTOMER_STATUS_WITH_ASSESSOR, Proposal.CUSTOMER_STATUS_DRAFT, Proposal.CUSTOMER_STATUS_AWAITING_ENDORSEMENT, Proposal.CUSTOMER_STATUS_PRINTING_STICKER, Proposal.CUSTOMER_STATUS_AWAITING_PAYMENT, Proposal.CUSTOMER_STATUS_AWAITING_DOCUMENTS]
existing_proposal_qs=self.proposal_set.filter(customer_status__in=customer_status_choices,
proposal_type__in=ProposalType.objects.filter(code__in=[PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL]))
proposal_type__in=ProposalType.objects.filter(code__in=[PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_SWAP_MOORINGS,]))
## cannot amend or renew
if existing_proposal_qs or ria_generated_proposal_qs:
amend_renew = None
Expand All @@ -746,6 +747,17 @@ def amend_or_renew(self):
except Exception as e:
raise e

@property
def mooring_swappable(self):
logger.debug(f'approval: [{self}]')
logger.debug(f'amend_or_renew: [{self.amend_or_renew}]')
try:
if self.amend_or_renew:
return True # if it is amendable/renewable, it is also swappable.
return False
except Exception as e:
raise e

def generate_doc(self, preview=False):
if preview:
from mooringlicensing.doctopdf import create_approval_doc_bytes
Expand Down Expand Up @@ -1777,14 +1789,19 @@ def _create_new_swap_moorings_application(self, request, new_mooring):
new_proposal.processing_status = Proposal.PROCESSING_STATUS_AWAITING_DOCUMENTS
new_proposal.vessel_ownership = self.current_proposal.vessel_ownership

new_proposal.save(version_comment=f'New Swap moorings Application: [{new_proposal}] created with the new mooring: [{new_mooring}] from the origin {new_proposal.previous_application}')
new_proposal.add_vessels_and_moorings_from_licence()

self.current_proposal = new_proposal # current_proposal of this ML should be new_proposal
self.save()

# Create a log entry for the proposal
self.current_proposal.log_user_action(ProposalUserAction.ACTION_SWAP_MOORINGS_PROPOSAL.format(self.current_proposal.id), request)

# Create a log entry for the approval
self.log_user_action(ApprovalUserAction.ACTION_SWAP_MOORINGS.format(self.id), request)

new_proposal.save(version_comment=f'New Swap moorings Application: [{new_proposal}] created with the new mooring: [{new_mooring}] from the origin {new_proposal.previous_application}')
new_proposal.add_vessels_and_moorings_from_licence()

send_swap_moorings_application_created_notification(self, request)

def swap_moorings(self, request, target_mooring_licence):
logger.info(f'Swapping moorings between an approval: [{self} ({self.mooring})] and an approval: [{target_mooring_licence} ({target_mooring_licence.mooring})]...')
Expand Down
6 changes: 6 additions & 0 deletions mooringlicensing/components/approvals/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ class ListApprovalSerializer(serializers.ModelSerializer):
can_action = serializers.SerializerMethodField()
can_reinstate = serializers.SerializerMethodField()
amend_or_renew = serializers.SerializerMethodField()
mooring_swappable = serializers.SerializerMethodField()
allowed_assessors_user = serializers.SerializerMethodField()
stickers = serializers.SerializerMethodField()
stickers_historical = serializers.SerializerMethodField()
Expand Down Expand Up @@ -704,6 +705,7 @@ class Meta:
'can_action',
'can_reinstate',
'amend_or_renew',
'mooring_swappable',
'renewal_document',
'allowed_assessors_user',
'stickers',
Expand Down Expand Up @@ -748,6 +750,7 @@ class Meta:
'can_action',
'can_reinstate',
'amend_or_renew',
'mooring_swappable',
'renewal_document',
'allowed_assessors_user',
'stickers',
Expand Down Expand Up @@ -895,6 +898,9 @@ def get_can_action(self,obj):
def get_amend_or_renew(self,obj):
return obj.amend_or_renew

def get_mooring_swappable(self,obj):
return obj.mooring_swappable

def get_mooring_licence_vessels(self, obj):
links = ''
request = self.context.get('request')
Expand Down
4 changes: 2 additions & 2 deletions mooringlicensing/components/proposals/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ def preview_approval(self,request,details):
if not self.applicant_address:
raise ValidationError('The applicant needs to have set their postal address before approving this proposal.')

lodgement_number = self.previous_application.approval.lodgement_number if self.proposal_type in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT] else None # renewals/amendments keep same licence number
lodgement_number = self.previous_application.approval.lodgement_number if self.proposal_type in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_SWAP_MOORINGS,] else None # renewals/amendments keep same licence number
preview_approval = PreviewTempApproval.objects.create(
current_proposal = self,
issue_date = timezone.now(),
Expand Down Expand Up @@ -4030,7 +4030,7 @@ def update_or_create_approval(self, current_datetime, request=None):

@property
def does_accept_null_vessel(self):
if self.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT,]:
if self.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_SWAP_MOORINGS,]:
return True
return False

Expand Down
6 changes: 3 additions & 3 deletions mooringlicensing/components/proposals/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
)
from mooringlicensing.components.users.serializers import UserSerializer
from mooringlicensing.ledger_api_utils import get_invoice_payment_status
from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_NEW
from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_NEW, PROPOSAL_TYPE_SWAP_MOORINGS
import traceback
from copy import deepcopy
from rest_framework import serializers
Expand Down Expand Up @@ -579,7 +579,7 @@ def submit_vessel_data(instance, request, vessel_data):
raise serializers.ValidationError(vessel_lookup_errors)

if not vessel_data.get('rego_no'):
if instance.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT,]:
if instance.proposal_type.code in [PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_SWAP_MOORINGS,]:
if type(instance.child_obj) in [MooringLicenceApplication, WaitingListApplication,]:
return
else:
Expand Down Expand Up @@ -764,7 +764,7 @@ def store_vessel_ownership(request, vessel, instance=None):
vessel_ownership.company_ownerships.add(company_ownership)
logger.info(f'CompanyOwnership: [{company_ownership}] has been added to the company_ownerships field of the VesselOwnership: [{vessel_ownership}].')
vo_created = True
elif instance.proposal_type.code in [PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL,]:
elif instance.proposal_type.code in [PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_SWAP_MOORINGS,]:
# Retrieve a vessel_ownership from the previous proposal
# vessel_ownership = instance.previous_application.vessel_ownership # !!! This is not always true when ML !!!
vessel_ownership = instance.get_latest_vessel_ownership_by_vessel(vessel)
Expand Down
10 changes: 9 additions & 1 deletion mooringlicensing/components/users/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,17 @@ class UserListFilterView(generics.ListAPIView):


class UserViewSet(viewsets.ModelViewSet):
queryset = EmailUser.objects.all()
queryset = EmailUser.objects.none()
serializer_class = UserSerializer

def get_queryset(self):
if is_internal(self.request):
return EmailUser.objects.all()
elif is_customer(self.request):
user = self.request.user
return EmailUser.objects.filter(Q(id=user.id))
return EmailUser.objects.none()

@detail_route(methods=['POST',], detail=True)
@basic_exception_handler
def update_personal(self, request, *args, **kwargs):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export default {
//searching: false,
searching: true,
ajax: {
"url": api_endpoints.approvals_paginated_list + '/list2/?format=datatables&target_email_user_id=' + vm.target_email_user_id,
"url": api_endpoints.approvals_paginated_list + '/list2/?format=datatables&target_email_user_id=' + vm.target_email_user_id + '&for_swap_moorings_modal=True',
"dataSrc": 'data',
"type": 'POST',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ export default {
links += `<a href='${full.renewal_document}' target='_blank'>Renewal Notice</a><br/>`
}
if(full.approval_type_dict && full.approval_type_dict.code == 'ml'){
links += `<a href='#${full.id}' data-swap-moorings-approval='${full.id}' data-swap-moorings-approval-lodgement-number='${full.lodgement_number}'>Swap moorings</a><br/>`
if(full.mooring_swappable)
links += `<a href='#${full.id}' data-swap-moorings-approval='${full.id}' data-swap-moorings-approval-lodgement-number='${full.lodgement_number}'>Swap moorings</a><br/>`
}
}
if (full.approval_type_dict.code != 'wla') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends 'mooringlicensing/emails/base_email-rottnest.html' %}

{% block content_body %}
{% include "mooringlicensing/emails_2/salutation.html" %}
<p>New swap moorings application has been created. Please click <a href="{{ dashboard_external_url }}">here</a> to access the application.</p>
{% include "mooringlicensing/emails/signature-rottnest.html" %}
{% endblock %}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends 'mooringlicensing/emails/base_email-rottnest.txt' %}

{% block content_body %}
{% include "mooringlicensing/emails_2/salutation.txt" %}
New swap moorings application has been created. Please access {{ dashboard_external_url }} to access the application.
{% include "mooringlicensing/emails/signature-rottnest.txt" %}
{% endblock %}

0 comments on commit b16ebff

Please sign in to comment.