diff --git a/mooringlicensing/components/approvals/api.py b/mooringlicensing/components/approvals/api.py
index c3997b923..1f1cbd3e0 100755
--- a/mooringlicensing/components/approvals/api.py
+++ b/mooringlicensing/components/approvals/api.py
@@ -1302,6 +1302,8 @@ def create_mooring_licence_application(self, request, *args, **kwargs):
waiting_list_allocation.current_proposal.copy_vessel_details(new_proposal)
logger.info(f'Vessel details have been copied from the proposal: [{waiting_list_allocation.current_proposal}] to the mooring site licence application: [{new_proposal}].')
+ waiting_list_allocation.log_user_action(f'Offer new Mooring Site Licence application: {new_proposal.lodgement_number}.', request)
+
if new_proposal:
# send email
send_create_mooring_licence_application_email_notification(request, waiting_list_allocation, new_proposal)
diff --git a/mooringlicensing/components/approvals/models.py b/mooringlicensing/components/approvals/models.py
index b98febb19..14dd133fb 100755
--- a/mooringlicensing/components/approvals/models.py
+++ b/mooringlicensing/components/approvals/models.py
@@ -635,17 +635,31 @@ def allowed_assessors(self):
return self.current_proposal.allowed_assessors
def allowed_assessors_user(self, request):
- return self.current_proposal.allowed_assessors_user(request)
+ if self.current_proposal:
+ return self.current_proposal.allowed_assessors_user(request)
+ else:
+ logger.warning(f'Current proposal of the approval: [{self}] not found.')
+ return None
- def is_assessor(self,user):
+ def is_assessor(self, user):
if isinstance(user, EmailUserRO):
user = user.id
- return self.current_proposal.is_assessor(user)
+
+ if self.current_proposal:
+ return self.current_proposal.is_assessor(user)
+ else:
+ logger.warning(f'Current proposal of the approval: [{self}] not found.')
+ return False
def is_approver(self,user):
if isinstance(user, EmailUserRO):
user = user.id
- return self.current_proposal.is_approver(user)
+
+ if self.current_proposal:
+ return self.current_proposal.is_approver(user)
+ else:
+ logger.warning(f'Current proposal of the approval: [{self}] not found.')
+ return False
@property
def is_issued(self):
@@ -654,7 +668,7 @@ def is_issued(self):
@property
def can_action(self):
if not (self.set_to_cancel or self.set_to_suspend or self.set_to_surrender):
- return True
+ return True
else:
return False
@@ -848,8 +862,6 @@ def reinstate_approval(self,request):
if type(self.child_obj) == WaitingListAllocation and previous_status in [Approval.APPROVAL_STATUS_CANCELLED, Approval.APPROVAL_STATUS_SURRENDERED]:
wla = self.child_obj
wla.internal_status = Approval.INTERNAL_STATUS_WAITING
- current_datetime = datetime.datetime.now(pytz.timezone(TIME_ZONE))
- # wla.wla_queue_date = current_datetime # Comment out this line because we never want to lost the original queue_date.
wla.save()
wla.set_wla_order()
send_approval_reinstate_email_notification(self, request)
@@ -1139,7 +1151,7 @@ def process_after_discarded(self):
logger.info(f'Set attributes as follows: [status=fulfilled, wla_order=None] of the WL Allocation: [{self}].')
self.set_wla_order()
- def reinstate_wla_order(self, request):
+ def reinstate_wla_order(self):
"""
This function makes this WL allocation back to the 'waiting' status
"""
@@ -1147,8 +1159,9 @@ def reinstate_wla_order(self, request):
self.status = Approval.APPROVAL_STATUS_CURRENT
self.internal_status = Approval.INTERNAL_STATUS_WAITING
self.save()
- logger.info(f'Set attributes as follows: [status=current, internal_status=waiting, wla_order=None] of the WL Allocation: [{self}].')
+ logger.info(f'Set attributes as follows: [status=current, internal_status=waiting, wla_order=None] of the WL Allocation: [{self}]. These changes make this WL allocation back to the waiting list queue.')
self.set_wla_order()
+ return self
class AnnualAdmissionPermit(Approval):
diff --git a/mooringlicensing/components/approvals/serializers.py b/mooringlicensing/components/approvals/serializers.py
index 0b0f5517e..5d01fc752 100755
--- a/mooringlicensing/components/approvals/serializers.py
+++ b/mooringlicensing/components/approvals/serializers.py
@@ -840,7 +840,11 @@ def get_allowed_assessors_user(self, obj):
def get_current_proposal_approved(self, obj):
from mooringlicensing.components.proposals.models import Proposal
- return obj.current_proposal.processing_status == Proposal.PROCESSING_STATUS_APPROVED
+ if obj.current_proposal:
+ return obj.current_proposal.processing_status == Proposal.PROCESSING_STATUS_APPROVED
+ else:
+ logger.warning(f'Current proposal of the approval: [{obj}] not found.')
+ return ''
def get_is_assessor(self, obj):
request = self.context.get('request')
@@ -972,10 +976,15 @@ def get_vessel_regos(self, obj):
# regos += '{}\n'.format(obj.current_proposal.vessel_details.vessel.rego_no) if obj.current_proposal.vessel_details else ''
# if obj.current_proposal.vessel_details:
# regos.append(obj.current_proposal.vessel_details.vessel.rego_no)
- if obj.current_proposal.vessel_ownership:
- if obj.current_proposal.vessel_ownership.end_date is None or obj.current_proposal.vessel_ownership.end_date >= today:
- # We don't want to include the sold vessel
- regos.append(obj.current_proposal.vessel_ownership.vessel.rego_no)
+ if obj.current_proposal:
+ if obj.current_proposal.vessel_ownership:
+ if obj.current_proposal.vessel_ownership.end_date is None or obj.current_proposal.vessel_ownership.end_date >= today:
+ # We don't want to include the sold vessel
+ regos.append(obj.current_proposal.vessel_ownership.vessel.rego_no)
+ else:
+ logger.warning(f'Current proposal of the approval: [{obj}] not found.')
+ return ''
+
return regos
diff --git a/mooringlicensing/components/payments_ml/views.py b/mooringlicensing/components/payments_ml/views.py
index c629a2bc6..07f84d60d 100644
--- a/mooringlicensing/components/payments_ml/views.py
+++ b/mooringlicensing/components/payments_ml/views.py
@@ -777,7 +777,7 @@ def get(self, request, uuid, format=None):
# When WLA / AAA
if proposal.application_type.code in [WaitingListApplication.code, AnnualAdmissionApplication.code]:
proposal.lodgement_date = datetime.datetime.now(pytz.timezone(TIME_ZONE))
- proposal.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(proposal.id), request)
+ proposal.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(proposal.lodgement_number), request)
proposal.child_obj.send_emails_after_payment_success(request)
proposal.save()
@@ -834,8 +834,9 @@ def get(self, request, uuid, *args, **kwargs):
except Exception as e:
# Should not reach here
- msg = 'Failed to process the payment. {}'.format(str(e))
+ msg = f'Failed to process the payment. {str(e)}'
logger.error(msg)
+ logger.error('Check if the preload_url is configured correctly.')
raise Exception(msg)
diff --git a/mooringlicensing/components/proposals/api.py b/mooringlicensing/components/proposals/api.py
index 64968470f..76d09ae9f 100755
--- a/mooringlicensing/components/proposals/api.py
+++ b/mooringlicensing/components/proposals/api.py
@@ -617,8 +617,7 @@ def create(self, request, *args, **kwargs):
proposal_type=proposal_type
)
logger.info(f'Annual Admission Application: [{obj}] has been created by the user: [{request.user}].')
-
- # make_proposal_applicant_ready(obj, request)
+ obj.log_user_action(f'Annual Admission Application: {obj.lodgement_number} has been created.', request)
serialized_obj = ProposalSerializer(obj.proposal)
return Response(serialized_obj.data)
@@ -647,9 +646,8 @@ def create(self, request, *args, **kwargs):
submitter=request.user.id,
proposal_type=proposal_type
)
- logger.info(f'Authorised User Application: [{obj}] has been created by the user: [{request.user}].')
-
- # make_proposal_applicant_ready(obj, request)
+ logger.info(f'Authorised User Permit Application: [{obj}] has been created by the user: [{request.user}].')
+ obj.log_user_action(f'Authorised User Permit Application: {obj.lodgement_number} has been created.', request)
serialized_obj = ProposalSerializer(obj.proposal)
return Response(serialized_obj.data)
@@ -684,8 +682,7 @@ def create(self, request, *args, **kwargs):
allocated_mooring=mooring,
)
logger.info(f'Mooring Licence Application: [{obj}] has been created by the user: [{request.user}].')
-
- # make_proposal_applicant_ready(obj, request)
+ obj.log_user_action(f'Mooring Licence Application: {obj.lodgement_number} has been created.', request)
serialized_obj = ProposalSerializer(obj.proposal)
return Response(serialized_obj.data)
@@ -717,16 +714,12 @@ def create(self, request, *args, **kwargs):
)
logger.info(f'Waiting List Application: [{obj}] has been created by the user: [{request.user}].')
-
- # make_proposal_applicant_ready(obj, request)
-
- # make_ownership_ready(obj, request)
+ obj.log_user_action(f'Waiting List Application: {obj.lodgement_number} has been created.', request)
serialized_obj = ProposalSerializer(obj.proposal)
return Response(serialized_obj.data)
-
class ProposalByUuidViewSet(viewsets.ModelViewSet):
queryset = Proposal.objects.none()
@@ -874,6 +867,21 @@ def internal_serializer_class(self):
print(traceback.print_exc())
raise serializers.ValidationError(str(e))
+ @detail_route(methods=['PUT'], detail=True)
+ @renderer_classes((JSONRenderer,))
+ @basic_exception_handler
+ def reinstate_wl_allocation(self, request, *args, **kwargs):
+ instance = self.get_object()
+ if is_internal(request) and instance.child_obj.code == MooringLicenceApplication.code and instance.processing_status in [Proposal.PROCESSING_STATUS_DISCARDED,]:
+ # Internal user is accessing
+ # Proposal is ML application and the status of it is 'discarded'
+ wlallocation = instance.child_obj.reinstate_wl_allocation(request)
+ return Response({'lodgement_number': wlallocation.lodgement_number}) # TODO
+ else:
+ msg = f'This application: [{instance}] does not meet the conditions to put the original WLAllocation to the waiting list queue.'
+ logger.warn(msg)
+ raise serializers.ValidationError(msg)
+
@detail_route(methods=['POST'], detail=True)
@renderer_classes((JSONRenderer,))
@basic_exception_handler
@@ -1075,7 +1083,6 @@ def assign_request_user(self, request, *args, **kwargs):
serializer_class = self.internal_serializer_class()
serializer = serializer_class(instance,context={'request':request})
return Response(serializer.data)
- raise serializers.ValidationError(str(e))
@detail_route(methods=['POST',], detail=True)
@basic_exception_handler
@@ -1388,14 +1395,6 @@ def destroy(self, request, *args, **kwargs):
else:
instance.destroy(request, *args, **kwargs)
- ## ML
- # if type(instance.child_obj) == MooringLicenceApplication and instance.waiting_list_allocation:
- # pass
- # instance.waiting_list_allocation.internal_status = 'waiting'
- # current_datetime = datetime.now(pytz.timezone(TIME_ZONE))
- # instance.waiting_list_allocation.wla_queue_date = current_datetime
- # instance.waiting_list_allocation.save()
- # instance.waiting_list_allocation.set_wla_order()
return Response()
@detail_route(methods=['POST',], detail=True)
diff --git a/mooringlicensing/components/proposals/models.py b/mooringlicensing/components/proposals/models.py
index d513d48a2..8e048458d 100644
--- a/mooringlicensing/components/proposals/models.py
+++ b/mooringlicensing/components/proposals/models.py
@@ -415,6 +415,7 @@ def get_latest_vessel_ownership_by_vessel(self, vessel):
if self.previous_application.vessel_ownership:
if self.previous_application.vessel_ownership.vessel == vessel:
# Same vessel is found.
+ # TODO: Check if vessel_ownership is valid
return self.previous_application.vessel_ownership
else:
# vessel of the previous application is differenct vessel. Search further back.
@@ -461,7 +462,7 @@ def withdraw(self, request, *args, **kwargs):
self.processing_status = Proposal.PROCESSING_STATUS_DISCARDED
self.save()
logger.info(f'Status: [{self.processing_status}] has been set to the proposal: [{self}].')
- self.log_user_action(ProposalUserAction.ACTION_WITHDRAW_PROPOSAL.format(self.lodgement_number, request))
+ self.log_user_action(ProposalUserAction.ACTION_WITHDRAW_PROPOSAL.format(self.lodgement_number), request)
# Perform post-processing for each application type after discarding.
self.child_obj.process_after_withdrawn()
@@ -470,7 +471,7 @@ def destroy(self, request, *args, **kwargs):
self.processing_status = Proposal.PROCESSING_STATUS_DISCARDED
self.save()
logger.info(f'Status: [{self.processing_status}] has been set to the proposal: [{self}].')
- self.log_user_action(ProposalUserAction.ACTION_DISCARD_PROPOSAL.format(self.lodgement_number, request))
+ self.log_user_action(ProposalUserAction.ACTION_DISCARD_PROPOSAL.format(self.lodgement_number), request)
# Perform post-processing for each application type after discarding.
self.child_obj.process_after_discarded()
@@ -1373,9 +1374,9 @@ def move_to_status(self, request, status, approver_comment):
# Create a log entry for the proposal
if self.processing_status == self.PROCESSING_STATUS_WITH_ASSESSOR:
- self.log_user_action(ProposalUserAction.ACTION_BACK_TO_PROCESSING.format(self.id), request)
+ self.log_user_action(ProposalUserAction.ACTION_BACK_TO_PROCESSING.format(self.lodgement_number), request)
elif self.processing_status == self.PROCESSING_STATUS_WITH_ASSESSOR_REQUIREMENTS:
- self.log_user_action(ProposalUserAction.ACTION_ENTER_REQUIREMENTS.format(self.id), request)
+ self.log_user_action(ProposalUserAction.ACTION_ENTER_REQUIREMENTS.format(self.lodgement_number), request)
def reissue_approval(self, request):
with transaction.atomic():
@@ -1671,6 +1672,7 @@ def final_approval_for_WLA_AAA(self, request, details=None):
)
if created:
logger.info(f'New approval: [{approval}] has been created.')
+ approval.log_user_action(f'New approval: {approval} has been created.', request)
self.approval = approval
self.save()
@@ -1708,11 +1710,11 @@ def final_approval_for_WLA_AAA(self, request, details=None):
# Log proposal action
if details:
# When not auto-approve
- self.log_user_action(ProposalUserAction.ACTION_APPROVED.format(self.id), request)
+ self.log_user_action(ProposalUserAction.ACTION_APPROVED.format(self.lodgement_number), request)
# applicant_field.log_user_action(ProposalUserAction.ACTION_APPROVED.format(self.id), request)
else:
# When auto approve
- self.log_user_action(ProposalUserAction.ACTION_AUTO_APPROVED.format(self.id),)
+ self.log_user_action(ProposalUserAction.ACTION_AUTO_APPROVED.format(self.lodgement_number),)
# applicant_field.log_user_action(ProposalUserAction.ACTION_AUTO_APPROVED.format(self.id),)
# set proposal status to approved - can change later after manage_stickers
@@ -3261,7 +3263,7 @@ def get_mooring_authorisation_preference(self):
def process_after_submit(self, request):
self.lodgement_date = datetime.datetime.now(pytz.timezone(TIME_ZONE))
self.save()
- self.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(self.id), request)
+ self.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(self.lodgement_number), request)
mooring_preference = self.get_mooring_authorisation_preference()
# if mooring_preference.lower() != 'ria' and self.proposal_type.code in [PROPOSAL_TYPE_NEW,]:
@@ -3507,6 +3509,11 @@ class MooringLicenceApplication(Proposal):
# This uuid is used to generate the URL for the ML document upload page
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
+ def reinstate_wl_allocation(self, request):
+ wlallocation = self.waiting_list_allocation.reinstate_wla_order()
+ self.log_user_action(f'Reinstate Waiting List Alocation: {wlallocation.lodgement_number} back to the waiting list queue.', request)
+ return wlallocation
+
def validate_against_existing_proposals_and_approvals(self):
from mooringlicensing.components.approvals.models import Approval, ApprovalHistory, WaitingListAllocation, MooringLicence
today = datetime.datetime.now(pytz.timezone(TIME_ZONE)).date()
@@ -3764,7 +3771,7 @@ def send_emails_after_payment_success(self, request):
def process_after_submit(self, request):
self.lodgement_date = datetime.datetime.now(pytz.timezone(TIME_ZONE))
- self.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(self.id), request)
+ self.log_user_action(ProposalUserAction.ACTION_LODGE_APPLICATION.format(self.lodgement_number), request)
if self.proposal_type in (ProposalType.objects.filter(code__in=[PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT,])):
# Renewal
diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue b/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue
index caefeead5..2bc8b2e35 100755
--- a/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue
+++ b/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue
@@ -431,112 +431,112 @@ export default {
// },
computed: {
applicant_first_name: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.first_name
} else {
return this.email_user.first_name
}
},
applicant_last_name: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.last_name
} else {
return this.email_user.last_name
}
},
applicant_dob: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.dob
} else {
return this.email_user.dob
}
},
contact_mobile_number: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.mobile_number
} else {
return this.email_user.mobile_number
}
},
contact_phone_number: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.phone_number
} else {
return this.email_user.phone_number
}
},
residential_line1: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.residential_line1
} else {
return this.email_user.residential_address.line1
}
},
residential_locality: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.residential_locality
} else {
return this.email_user.residential_address.locality
}
},
residential_state: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.residential_state
} else {
return this.email_user.residential_address.state
}
},
residential_postcode: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.residential_postcode
} else {
return this.email_user.residential_address.postcode
}
},
residential_country: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.residential_country
} else {
return this.email_user.residential_address.country
}
},
postal_same_as_residential: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_same_as_residential
} else {
return this.email_user.postal_same_as_residential
}
},
postal_line1: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_line1
} else {
return this.email_user.postal_address.line1
}
},
postal_locality: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_locality
} else {
return this.email_user.postal_address.locality
}
},
postal_state: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_state
} else {
return this.email_user.postal_address.state
}
},
postal_postcode: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_postcode
} else {
return this.email_user.postal_address.postcode
}
},
postal_country: function(){
- if (this.proposal){
+ if (this.proposal && this.proposal.proposal_applicant){
return this.proposal.proposal_applicant.postal_country
} else {
return this.email_user.postal_address.country
diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/common/table_approvals.vue b/mooringlicensing/frontend/mooringlicensing/src/components/common/table_approvals.vue
index 5358abcd4..bdb868eb4 100644
--- a/mooringlicensing/frontend/mooringlicensing/src/components/common/table_approvals.vue
+++ b/mooringlicensing/frontend/mooringlicensing/src/components/common/table_approvals.vue
@@ -945,8 +945,6 @@ export default {
*/
offerMooringLicence: function(id){
- console.log('offerMooringLicence')
- console.log(id)
this.selectedWaitingListAllocationId = parseInt(id);
this.uuid++;
this.$nextTick(() => {
diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/common/table_proposals.vue b/mooringlicensing/frontend/mooringlicensing/src/components/common/table_proposals.vue
index 0b47534ea..6b8f7b42d 100644
--- a/mooringlicensing/frontend/mooringlicensing/src/components/common/table_proposals.vue
+++ b/mooringlicensing/frontend/mooringlicensing/src/components/common/table_proposals.vue
@@ -285,9 +285,13 @@ export default {
}
}
if (full.application_type_dict.code === 'mla' && full.processing_status === 'Draft'){
- // Only mooring licensing draft application can be withdrawn
+ // Only ML draft application can be withdrawn
links += `Withdraw
`
}
+ if (full.application_type_dict.code === 'mla' && full.processing_status === 'Discarded'){
+ // Only ML discarded application can make originated WL allocation reinstated
+ links += `Reinstate WL allocation
`
+ }
}
if (vm.is_external){
if (full.can_user_edit) {
@@ -453,6 +457,33 @@ export default {
name: 'apply_proposal'
})
},
+ reinstateWLAllocation: function(proposal_id){
+ let vm = this;
+ swal({
+ title: "Reinstate Waiting List Allocation",
+ text: "Are you sure you want to put the originated waiting list allocation back on the waiting list?",
+ type: "warning",
+ showCancelButton: true,
+ confirmButtonText: 'Reinstate WL Allocation',
+ confirmButtonColor:'#dc3545'
+ }).then(() => {
+ vm.$http.put('/api/proposal/' + proposal_id + '/reinstate_wl_allocation/')
+ .then((response) => {
+ console.log({response})
+ swal(
+ 'Reinstated',
+ 'Originated waiting list allocation has been reinstated',
+ 'success'
+ )
+ vm.$refs.application_datatable.vmDataTable.draw();
+ }, (error) => {
+ helpers.processError(error)
+ });
+ },(error) => {
+
+ });
+
+ },
withdrawProposal: function(proposal_id){
let vm = this;
swal({
@@ -574,6 +605,11 @@ export default {
let id = $(this).attr('data-withdraw-proposal');
vm.withdrawProposal(id)
})
+ vm.$refs.application_datatable.vmDataTable.on('click', 'a[data-reinstate-wl-allocation]', function(e) {
+ e.preventDefault();
+ let id = $(this).attr('data-reinstate-wl-allocation');
+ vm.reinstateWLAllocation(id)
+ })
},
},
created: function(){