diff --git a/mooringlicensing/components/proposals/api.py b/mooringlicensing/components/proposals/api.py index d05285e83..d4856b779 100755 --- a/mooringlicensing/components/proposals/api.py +++ b/mooringlicensing/components/proposals/api.py @@ -2067,13 +2067,13 @@ def record_sale(self, request, *args, **kwargs): if not instance.end_date: # proposals with instance copied to listed_vessels for proposal in instance.listed_on_proposals.all(): - if proposal.processing_status not in [Proposal.PROCESSING_STATUS_DISCARDED, Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED,]: + if proposal.processing_status not in [Proposal.PROCESSING_STATUS_DISCARDED, Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_EXPIRED]: raise serializers.ValidationError( "You cannot record the sale of this vessel at this time as application {} that lists this vessel is still in progress.".format(proposal.lodgement_number) ) # submitted proposals with instance == proposal.vessel_ownership for proposal in instance.proposal_set.all(): - if proposal.processing_status not in [Proposal.PROCESSING_STATUS_DISCARDED, Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED,]: + if proposal.processing_status not in [Proposal.PROCESSING_STATUS_DISCARDED, Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_EXPIRED]: raise serializers.ValidationError( "You cannot record the sale of this vessel at this time as application {} that lists this vessel is still in progress.".format(proposal.lodgement_number) ) diff --git a/mooringlicensing/components/proposals/email.py b/mooringlicensing/components/proposals/email.py index 338716d09..fb0c6ac7c 100755 --- a/mooringlicensing/components/proposals/email.py +++ b/mooringlicensing/components/proposals/email.py @@ -393,6 +393,95 @@ def send_invitee_reminder_email(approval, due_date, request=None): _log_approval_email(msg, approval, sender=sender_user) _log_user_email(msg, approval.applicant_obj, proposal.applicant_obj, sender=sender_user) +def send_expire_application_email(proposal, due_date,): + + html_template = 'mooringlicensing/emails_2/application_expire_notification.html' + txt_template = 'mooringlicensing/emails_2/application_expire_notification.txt' + + email = TemplateEmailBase( + subject='Application {} Expired - Rottnest Island Authority'.format(proposal.lodgement_number), + html_template=html_template, + txt_template=txt_template, + ) + url = settings.SITE_URL if settings.SITE_URL else '' + dashboard_url = url + reverse('external') + + # Configure recipients, contents, etc + context = { + 'url': url, + 'proposal': proposal, + 'recipient': proposal.applicant_obj, + 'dashboard_url': make_http_https(dashboard_url), + } + to_address = proposal.applicant_obj.email + cc = [] + bcc = [] + + # Send email + msg = email.send(to_address, context=context, attachments=[], cc=cc, bcc=bcc,) + if msg: + sender = get_user_as_email_user(msg.from_email) + log_proposal_email(msg, proposal, sender) + return msg + +def send_expire_notification_to_assessor(proposal, due_date): + email = TemplateEmailBase( + subject='Expired application - not paid on time', + html_template='mooringlicensing/emails_2/assessor_expiry_notification.html', + txt_template='mooringlicensing/emails_2/assessor_expiry_notification.txt', + ) + + context = { + 'public_url': get_public_url(), + 'applicant': proposal.applicant_obj, + 'due_date': due_date, + 'recipient': proposal.applicant_obj, + 'proposal': proposal + } + + to_address = proposal.assessor_recipients + cc = [] + bcc = [] + + # Send email + msg = email.send(to_address, context=context, attachments=[], cc=cc, bcc=bcc,) + if msg: + sender = get_user_as_email_user(msg.from_email) + log_proposal_email(msg, proposal, sender) + return msg + +def send_payment_reminder_email(proposal, request=None): + + email = TemplateEmailBase( + subject='Payment reminder: Application {} - Rottnest Island Authority'.format(proposal.lodgement_number), + html_template='mooringlicensing/emails_2/application_payment_reminder.html', + txt_template='mooringlicensing/emails_2/application_payment_reminder.txt', + ) + + url = settings.SITE_URL if settings.SITE_URL else '' + + due_date = proposal.payment_due_date + + # Configure recipients, contents, etc + context = { + 'url': url, + 'proposal': proposal, + 'recipient': proposal.applicant_obj, + 'applicant': proposal.applicant_obj, + 'due_date': due_date, + } + to_address = proposal.applicant_obj.email + cc = [] + bcc = [] + + # Send email + msg = email.send(to_address, context=context, attachments=[], cc=cc, bcc=bcc,) + if msg: + sender = get_user_as_email_user(msg.from_email) + log_proposal_email(msg, proposal, sender) + + return msg + def send_expire_mooring_licence_application_email(proposal, reason, due_date,): # 12 email to mooring licence applicant when mooring licence application is not submitted within configurable diff --git a/mooringlicensing/components/proposals/models.py b/mooringlicensing/components/proposals/models.py index b5c9b02fb..a58c6772b 100644 --- a/mooringlicensing/components/proposals/models.py +++ b/mooringlicensing/components/proposals/models.py @@ -356,6 +356,8 @@ class Proposal(DirtyFieldsMixin, RevisionedMixin): # To avoid that, this fee_season field is used in order to store those data. auto_approve = models.BooleanField(default=False) null_vessel_on_create = models.BooleanField(default=True) + payment_reminder_sent = models.BooleanField(default=False) + payment_due_date = models.DateField(blank=True, null=True) #date when payment is due for future invoices class Meta: app_label = 'mooringlicensing' @@ -1053,6 +1055,7 @@ def can_officer_process(self): Proposal.PROCESSING_STATUS_AWAITING_DOCUMENTS, Proposal.PROCESSING_STATUS_PRINTING_STICKER, Proposal.PROCESSING_STATUS_STICKER_TO_BE_RETURNED, + Proposal.PROCESSING_STATUS_EXPIRED, ] return False if self.processing_status in officer_view_state else True @@ -1113,7 +1116,8 @@ def has_approver_mode(self,user): Proposal.PROCESSING_STATUS_AWAITING_PAYMENT, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DRAFT, - Proposal.PROCESSING_STATUS_PRINTING_STICKER + Proposal.PROCESSING_STATUS_PRINTING_STICKER, + Proposal.PROCESSING_STATUS_EXPIRED, ] if self.processing_status in status_without_approver: return False @@ -1135,7 +1139,8 @@ def has_assessor_mode(self,user): Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_AWAITING_PAYMENT, Proposal.PROCESSING_STATUS_DECLINED, - Proposal.PROCESSING_STATUS_PRINTING_STICKER + Proposal.PROCESSING_STATUS_PRINTING_STICKER, + Proposal.PROCESSING_STATUS_EXPIRED, ] if self.processing_status in status_without_assessor: return False @@ -1758,7 +1763,15 @@ def final_approval_for_AUA_MLA(self, request=None): return_preload_url = settings.MOORING_LICENSING_EXTERNAL_URL + reverse("ledger-api-success-callback", kwargs={"uuid": application_fee.uuid}) basket_hash_split = basket_hash.split("|") - pcfi = process_create_future_invoice(basket_hash_split[0], invoice_text, return_preload_url) + + invoice_name = self.proposal_applicant.get_full_name() + today = timezone.localtime(timezone.now()).date() + days_type = NumberOfDaysType.objects.get(code=settings.CODE_DAYS_BEFORE_DUE_PAYMENT) + days_setting = NumberOfDaysSetting.get_setting_by_date(days_type, today) + self.payment_due_date = today + datetime.timedelta(days=days_setting.number_of_days) + self.save() + + pcfi = process_create_future_invoice(basket_hash_split[0], invoice_text, return_preload_url, invoice_name, self.payment_due_date.strftime("%d/%m/%Y")) application_fee.invoice_reference = pcfi['data']['invoice'] application_fee.save() @@ -1778,9 +1791,10 @@ def final_approval_for_AUA_MLA(self, request=None): amount_to_be_paid=amount_to_be_paid, ) logger.info(f'FeeItemApplicationFee: [{fiaf}] has been created.') - + if not self.payment_required(): self.approval.generate_doc() + send_application_approved_or_declined_email(self, 'approved', request) self.log_user_action(ProposalUserAction.ACTION_APPROVE_APPLICATION.format(self.lodgement_number), request) @@ -2614,6 +2628,7 @@ def get_intermediate_proposals(email_user_id): Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, ]) return proposals @@ -2788,6 +2803,7 @@ def validate_against_existing_proposals_and_approvals(self): Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, ]: if type(proposal) == MooringLicenceApplication: proposals_mla.append(proposal) @@ -3008,6 +3024,7 @@ def validate_against_existing_proposals_and_approvals(self): Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, ]: if type(proposal) == AuthorisedUserApplication: proposals_aua.append(proposal) @@ -3510,6 +3527,7 @@ def validate_against_existing_proposals_and_approvals(self): Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, ]: if type(proposal) == MooringLicenceApplication: proposals_mla.append(proposal) @@ -3582,7 +3600,9 @@ def get_intermediate_proposals(email_user_id): proposals = MooringLicenceApplication.objects.filter(proposal_applicant__email_user_id=email_user_id).exclude(processing_status__in=[ Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, - Proposal.PROCESSING_STATUS_DISCARDED,]) + Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, + ]) return proposals def create_fee_lines(self): @@ -4054,7 +4074,12 @@ def get_queryset(self): # now check whether there are any blocking proposals blocking_proposal = False for proposal in mooring.ria_generated_proposal.all(): - if proposal.processing_status not in [Proposal.PROCESSING_STATUS_APPROVED, Proposal.PROCESSING_STATUS_DECLINED, Proposal.PROCESSING_STATUS_DISCARDED,]: + if proposal.processing_status not in [ + Proposal.PROCESSING_STATUS_APPROVED, + Proposal.PROCESSING_STATUS_DECLINED, + Proposal.PROCESSING_STATUS_DISCARDED, + Proposal.PROCESSING_STATUS_EXPIRED, + ]: blocking_proposal = True if not blocking_proposal: available_ids.append(mooring.id) diff --git a/mooringlicensing/components/proposals/templates/mooringlicensing/mgt-commands.html b/mooringlicensing/components/proposals/templates/mooringlicensing/mgt-commands.html index 8a29a604f..6fdc06c0d 100755 --- a/mooringlicensing/components/proposals/templates/mooringlicensing/mgt-commands.html +++ b/mooringlicensing/components/proposals/templates/mooringlicensing/mgt-commands.html @@ -41,14 +41,6 @@
Please be aware that your {{ proposal.child_obj.description }}: {{ proposal.lodgement_number }} expired on {{ proposal.payment_due_date }} due to payment not being made
+To apply for a new {{ proposal.child_obj.description }} please click here
+{% include "mooringlicensing/emails/signature-rottnest.html" %} +{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/application_expire_notification.txt b/mooringlicensing/templates/mooringlicensing/emails_2/application_expire_notification.txt new file mode 100755 index 000000000..88436ac7f --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/emails_2/application_expire_notification.txt @@ -0,0 +1,6 @@ +{% extends 'mooringlicensing/emails/base_email.txt' %} + +{% block content %} + Please be aware that your {{ proposal.child_obj.description }}: {{ proposal.lodgement_number }} expired on {{ proposal.payment_due_date }} due to payment not being made + To apply for a new {{ proposal.child_obj.description }} please access {{ url }} +{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.html b/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.html new file mode 100755 index 000000000..d71aba380 --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.html @@ -0,0 +1,9 @@ +{% extends 'mooringlicensing/emails/base_email-rottnest.html' %} + +{% block content_body %} +Dear {{ applicant.first_name }} {{ applicant.last_name}},
+ +Payment for application {{ proposal.lodgement_number }} is due by {{ due_date }}.
+Please click here to login and check the Applications dashboard.
+{% include "mooringlicensing/emails/signature-rottnest.html" %} +{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.txt b/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.txt new file mode 100755 index 000000000..941db2220 --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/emails_2/application_payment_reminder.txt @@ -0,0 +1,9 @@ +{% extends 'mooringlicensing/emails/base_email-rottnest.txt' %} + +{% block content_body %} +Dear {{ applicant.first_name }} {{ applicant.last_name}}, + +Payment for application {{ proposal.lodgement_number }} is due by {{ due_date }}. +Please access {{ url }} to login and check the Applications dashboard. +{% include "mooringlicensing/emails/signature-rottnest.html" %} +{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.html b/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.html new file mode 100755 index 000000000..ffaf3dcd5 --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.html @@ -0,0 +1,5 @@ +{% extends 'mooringlicensing/emails/base_email-rottnest.html' %} + +{% block content_body %} +{{ applicant.first_name }} {{ applicant.last_name }} has not paid for application {{ proposal.lodgement_number }} before the due date of {{ due_date }}. The application has expired.
+{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.txt b/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.txt new file mode 100755 index 000000000..937b39fd0 --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/emails_2/assessor_expiry_notification.txt @@ -0,0 +1,5 @@ +{% extends 'mooringlicensing/emails/base_email-rottnest.txt' %} + +{% block content_body %} +{{ applicant.first_name }} {{ applicant.last_name }} has not paid for application {{ proposal.lodgement_number }} before the due date of {{ due_date }}. The application has expired. +{% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.html b/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.html index 6daf16007..2bd08d137 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.html +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.html @@ -4,6 +4,7 @@ {% include "mooringlicensing/emails_2/salutation.html" %}Your authorised user permit application {{ proposal.lodgement_number }} has been conditionally approved pending payment of your fees. Please see attached document for more details.
The sticker(s) for your vessel will be mailed to you once payment has been made. Please click here to pay the fees for your authorised user permit.
+Payment is due by {{proposal.payment_due_date}}.
Vessel stickers remain the property of RIA and must be returned or surrendered upon request, or prior to the sale or disposal of your vessel. Penalties apply.
Entry to the Reserve, access to the Shared Mooring System (SMS), and authorised use of mooring sites, is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply.
{% if details %}Details: {{ details }}
{% endif %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.txt b/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.txt index 953c188cf..82e032866 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.txt +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_20a.txt @@ -4,6 +4,7 @@ {% include "mooringlicensing/emails_2/salutation.txt" %} Your authorised user permit application {{ proposal.lodgement_number }} has been conditionally approved pending payment of your fees. Please see attached document for more details. The sticker(s) for your vessel will be mailed to you once payment has been made. Please access {{ payment_url }} to pay the fees for your authorised user permit. +Payment is due by {{proposal.payment_due_date}}. Vessel stickers remain the property of RIA and must be returned or surrendered upon request, or prior to the sale or disposal of your vessel. Penalties apply. Entry to the Reserve, access to the Shared Mooring System (SMS), and authorised use of mooring sites, is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply. {% if details %}Details: {{ details }}{% endif %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.html b/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.html index 001fac4e3..6ec9faf24 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.html +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.html @@ -12,6 +12,7 @@ {% endif %}Entry to the Reserve and access to the Shared Mooring System (SMS) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply.
Please click here to pay the fees for your authorised user permit.
+Payment is due by {{proposal.payment_due_date}}.
{% if details %}Details: {{ details }}
{% endif %} {% include "mooringlicensing/emails/signature-rottnest.html" %} {% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.txt b/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.txt index cf775f8c4..5a10af7ca 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.txt +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_22a.txt @@ -12,6 +12,7 @@ to the Rottnest Island Authority and the new sticker for your vessel will be mai {% endif %} Entry to the Reserve and access to the Shared Mooring System (SMS) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply. Please access {{ payment_url }} to pay the fees for your authorised user permit. +Payment is due by {{proposal.payment_due_date}}. {% if details %}Details: {{ details }}{% endif %} {% include "mooringlicensing/emails/signature-rottnest.txt" %} {% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.html b/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.html index d8df57a2f..0eb50b1e5 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.html +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.html @@ -7,6 +7,7 @@Entry to the Reserve and access to the Shared Mooring System (including use of your own mooring site) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply.
Remember that vessel stickers remain the property of RIA and must be returned or surrendered upon request, or prior to the sale or disposal of your vessel. Penalties apply.
Please click here to pay the fees for your mooring site licence.
+Payment is due by {{proposal.payment_due_date}}.
{% if details %}Details: {{ details }}
{% endif %} {% include "mooringlicensing/emails/signature-rottnest.html" %} {% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.txt b/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.txt index f4ea6a9b5..946779cb0 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.txt +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_23a.txt @@ -7,6 +7,7 @@ Once payment has been received the sticker(s) for your vessel(s) will be mailed Entry to the Reserve and access to the Shared Mooring System (including use of your own mooring site) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply. Remember that vessel stickers remain the property of RIA and must be returned or surrendered upon request, or prior to the sale or disposal of your vessel. Penalties apply. Please access {{ payment_url }} to pay the fees for your mooring site licence. +Payment is due by {{proposal.payment_due_date}}. {% if details %}Details: {{ details }}{% endif %} {% include "mooringlicensing/emails/signature-rottnest.txt" %} {% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.html b/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.html index 1c341042c..2c921f803 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.html +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.html @@ -12,6 +12,7 @@ {% endif %}Entry to the Reserve and access to the Shared Mooring System (including use of your own mooring site) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply.
Please click here to pay the fees for your mooring site licence.
+Payment is due by {{proposal.payment_due_date}}.
{% if details %}Details: {{ details }}
{% endif %} {% include "mooringlicensing/emails/signature-rottnest.html" %} {% endblock %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.txt b/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.txt index 1b2f008fe..105e1fbfa 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.txt +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_25a.txt @@ -12,6 +12,7 @@ to the Rottnest Island Authority and the new sticker for your vessel will be mai {% endif %} Entry to the Reserve and access to the Shared Mooring System (including use of your own mooring site) is NOT approved until your current sticker is clearly displayed on the port side of your vessel. Penalties apply. Please click here {{ payment_url }} to pay the fees for your mooring site licence. +Payment is due by {{proposal.payment_due_date}}. {% if details %}Details: {{ details }}{% endif %} {% include "mooringlicensing/emails/signature-rottnest.txt" %} {% endblock %}