diff --git a/.gitignore b/.gitignore index 1f243e346..04e077df3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ .settings/ .env -venv/ -venv2/ -venv3.8/ +venv*/ +#venv2/ +#venv3.8/ node_modules/ #cronjobs .ropeproject/ diff --git a/Dockerfile b/Dockerfile index 603da9e11..d25f009bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,9 +39,9 @@ COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt \ && rm -rf /var/lib/{apt,dpkg,cache,log}/ /tmp/* /var/tmp/* -COPY libgeos.py.patch /app/ -RUN patch /usr/local/lib/python3.8/dist-packages/django/contrib/gis/geos/libgeos.py /app/libgeos.py.patch -RUN rm /app/libgeos.py.patch +#COPY libgeos.py.patch /app/ +#RUN patch /usr/local/lib/python3.8/dist-packages/django/contrib/gis/geos/libgeos.py /app/libgeos.py.patch +#RUN rm /app/libgeos.py.patch # Install the project (ensure that frontend projects have been built prior to this step). FROM python_libs_ml @@ -53,6 +53,10 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN touch /app/.env COPY .git ./.git COPY mooringlicensing ./mooringlicensing +COPY patch_for_admin_0001_initial.patch ./patch_for_admin_0001_initial.patch +COPY patch_for_admin_0001_initial.patch_revert ./patch_for_admin_0001_initial.patch_revert +COPY patch_for_reversion_0001.patch ./patch_for_reversion_0001.patch +COPY patch_for_reversion_0001.patch_revert ./patch_for_reversion_0001.patch_revert RUN python manage_ml.py collectstatic --noinput RUN mkdir /app/tmp/ diff --git a/mooringlicensing/admin.py b/mooringlicensing/admin.py index 8f1e3e83f..d3b71b3d2 100644 --- a/mooringlicensing/admin.py +++ b/mooringlicensing/admin.py @@ -1,14 +1,18 @@ from django.contrib.admin import AdminSite from django.contrib import admin +# from mooringlicensing.components.main import models from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import Group from django.conf import settings -from ledger.accounts import admin as ledger_admin +# from ledger.accounts import admin as ledger_admin #from ledger.accounts.models import EmailUser, Document, Address, Profile -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO from copy import deepcopy +admin.site.index_template = 'admin-index.html' +admin.autodiscover() class MooringLicensingAdminSite(AdminSite): site_header = 'Mooring Licensing Administration' @@ -17,31 +21,51 @@ class MooringLicensingAdminSite(AdminSite): mooringlicensing_admin_site = MooringLicensingAdminSite(name='mooringlicensingadmin') -admin.site.unregister(EmailUser) # because this base classAdmin alsready registered in ledger.accounts.admin -@admin.register(EmailUser) -class EmailUserAdmin(ledger_admin.EmailUserAdmin): - """ - Overriding the EmailUserAdmin from ledger.accounts.admin, to remove is_superuser checkbox field on Admin page - """ - - def get_fieldsets(self, request, obj=None): - """ Remove the is_superuser checkbox from the Admin page, if user is MooringLicensingAdmin and NOT superuser """ - fieldsets = super(UserAdmin, self).get_fieldsets(request, obj) - #if not obj: - # return fieldsets - - if request.user.is_superuser: - return fieldsets - - # User is not a superuser, remove is_superuser checkbox - fieldsets = deepcopy(fieldsets) - for fieldset in fieldsets: - if 'is_superuser' in fieldset[1]['fields']: - if type(fieldset[1]['fields']) == tuple : - fieldset[1]['fields'] = list(fieldset[1]['fields']) - fieldset[1]['fields'].remove('is_superuser') - break - - return fieldsets +# admin.site.unregister(EmailUser) # because this base classAdmin alsready registered in ledger.accounts.admin +# @admin.register(EmailUser) +# class EmailUserAdmin(admin.ModelAdmin): +# # class EmailUserAdmin(ledger_admin.EmailUserAdmin): +# """ +# Overriding the EmailUserAdmin from ledger.accounts.admin, to remove is_superuser checkbox field on Admin page +# """ +# +# def get_fieldsets(self, request, obj=None): +# """ Remove the is_superuser checkbox from the Admin page, if user is MooringLicensingAdmin and NOT superuser """ +# fieldsets = super(UserAdmin, self).get_fieldsets(request, obj) +# #if not obj: +# # return fieldsets +# +# if request.user.is_superuser: +# return fieldsets +# +# # User is not a superuser, remove is_superuser checkbox +# fieldsets = deepcopy(fieldsets) +# for fieldset in fieldsets: +# if 'is_superuser' in fieldset[1]['fields']: +# if type(fieldset[1]['fields']) == tuple : +# fieldset[1]['fields'] = list(fieldset[1]['fields']) +# fieldset[1]['fields'].remove('is_superuser') +# break +# +# return fieldsets +# +# +# @admin.register(EmailUserRO) +class EmailUserAdmin(admin.ModelAdmin): + list_display = ( + "email", + "first_name", + "last_name", + "is_staff", + "is_active", + ) + ordering = ("email",) + search_fields = ("id", "email", "first_name", "last_name") + def has_change_permission(self, request, obj=None): + if obj is None: # and obj.status > 1: + return True + return None + def has_delete_permission(self, request, obj=None): + return None diff --git a/mooringlicensing/components/approvals/api.py b/mooringlicensing/components/approvals/api.py index 5a153338a..3409b1177 100755 --- a/mooringlicensing/components/approvals/api.py +++ b/mooringlicensing/components/approvals/api.py @@ -9,11 +9,16 @@ from django.core.exceptions import ValidationError from django.conf import settings from rest_framework import viewsets, serializers, generics, views, status -from rest_framework.decorators import detail_route, list_route, renderer_classes +# from rest_framework.decorators import detail_route, list_route, renderer_classes +from rest_framework.decorators import action as detail_route +from rest_framework.decorators import action as list_route +from rest_framework.decorators import renderer_classes from rest_framework.response import Response from rest_framework.renderers import JSONRenderer -from ledger.accounts.models import EmailUser -from ledger.settings_base import TIME_ZONE +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO as EmailUser +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE from datetime import datetime from collections import OrderedDict @@ -166,10 +171,10 @@ def get(self, request, format=None): from mooringlicensing.components.proposals.models import WaitingListApplication wla_allowed = True # Person can have only one WLA, Waiting Liast application, Mooring Licence and Mooring Licence application - if (WaitingListApplication.objects.filter(submitter=request.user).exclude(processing_status__in=['approved', 'declined', 'discarded']) or - WaitingListAllocation.objects.filter(submitter=request.user).exclude(status__in=['cancelled', 'expired', 'surrendered']) or - MooringLicenceApplication.objects.filter(submitter=request.user).exclude(processing_status__in=['approved', 'declined', 'discarded']) or - MooringLicence.objects.filter(submitter=request.user).filter(status__in=['current', 'suspended'])): + if (WaitingListApplication.objects.filter(submitter=request.user.id).exclude(processing_status__in=['approved', 'declined', 'discarded']) or + WaitingListAllocation.objects.filter(submitter=request.user.id).exclude(status__in=['cancelled', 'expired', 'surrendered']) or + MooringLicenceApplication.objects.filter(submitter=request.user.id).exclude(processing_status__in=['approved', 'declined', 'discarded']) or + MooringLicence.objects.filter(submitter=request.user.id).filter(status__in=['current', 'suspended'])): wla_allowed = False return Response({"wla_allowed": wla_allowed}) @@ -198,7 +203,7 @@ def get_queryset(self): approval_qs = approval_qs.exclude(replaced_by__isnull=False) # get lastest licence, ignore the amended return approval_qs - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def _list(self, request, *args, **kwargs): data = [] for approval in self.get_queryset(): @@ -269,9 +274,11 @@ def filter_queryset(self, request, queryset, view): filter_query &= Q(id__in=wla_list) queryset = queryset.filter(filter_query) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -314,7 +321,7 @@ def get_queryset(self): all = all.filter(Q(submitter=target_user)) return all elif is_customer(self.request): - qs = all.filter(Q(submitter=request_user)) + qs = all.filter(Q(submitter=request_user.id)) return qs return Approval.objects.none() @@ -339,8 +346,10 @@ def get_queryset(self): if is_internal(self.request): return Approval.objects.all() elif is_customer(self.request): - user_orgs = [org.id for org in self.request.user.mooringlicensing_organisations.all()] - queryset = Approval.objects.filter(Q(org_applicant_id__in = user_orgs) | Q(submitter = self.request.user)) + # user_orgs = [org.id for org in self.request.user.mooringlicensing_organisations.all()] + # queryset = Approval.objects.filter(Q(org_applicant_id__in = user_orgs) | Q(submitter = self.request.user)) + user_orgs = Organisation.objects.filter(delegates__contains=[self.request.user.id]) + queryset = Approval.objects.filter(Q(org_applicant__in=user_orgs) | Q(submitter = self.request.user.id)) return queryset return Approval.objects.none() @@ -362,12 +371,12 @@ def list(self, request, *args, **kwargs): ('data',serializer.data) ]),status=status.HTTP_200_OK) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) @basic_exception_handler def existing_licences(self, request, *args, **kwargs): existing_licences = [] l_list = Approval.objects.filter( - submitter=request.user, + submitter=request.user.id, #status__in=['current', 'fulfilled'], status__in=['current'], ) @@ -397,7 +406,7 @@ def existing_licences(self, request, *args, **kwargs): }) return Response(existing_licences) - @list_route(methods=['GET']) + @list_route(methods=['GET'], detail=False) def holder_list(self, request, *args, **kwargs): holder_list = self.get_queryset().values_list('submitter__id', flat=True) print(holder_list) @@ -406,7 +415,7 @@ def holder_list(self, request, *args, **kwargs): serializer = EmailUserSerializer(EmailUser.objects.filter(id__in=distinct_holder_list), many=True) return Response(serializer.data) - @detail_route(methods=['GET']) + @detail_route(methods=['GET'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def get_moorings(self, request, *args, **kwargs): @@ -425,7 +434,7 @@ def get_moorings(self, request, *args, **kwargs): }) return Response(moorings) - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def request_new_stickers(self, request, *args, **kwargs): @@ -458,7 +467,7 @@ def request_new_stickers(self, request, *args, **kwargs): return Response({'sticker_action_detail_ids': sticker_action_details}) - @detail_route(methods=['GET']) + @detail_route(methods=['GET'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def stickers(self, request, *args, **kwargs): @@ -467,7 +476,7 @@ def stickers(self, request, *args, **kwargs): serializer = StickerSerializer(stickers, many=True) return Response({'stickers': serializer.data}) - @detail_route(methods=['GET']) + @detail_route(methods=['GET'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def approval_history(self, request, *args, **kwargs): @@ -475,7 +484,7 @@ def approval_history(self, request, *args, **kwargs): serializer = ApprovalHistorySerializer(instance.approvalhistory_set.all(), many=True) return Response(serializer.data) - @detail_route(methods=['GET']) + @detail_route(methods=['GET'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def lookup_approval(self, request, *args, **kwargs): @@ -486,7 +495,7 @@ def lookup_approval(self, request, *args, **kwargs): } return Response(approval_details) - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_waiting_list_offer_document(self, request, *args, **kwargs): @@ -497,7 +506,7 @@ def process_waiting_list_offer_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) def process_document(self, request, *args, **kwargs): instance = self.get_object() @@ -530,7 +539,7 @@ def process_document(self, request, *args, **kwargs): return Response( [dict(input_name=d.input_name, name=d.name,file=d._file.url, id=d.id, can_delete=d.can_delete) for d in instance.qaofficer_documents.filter(input_name=section, visible=True) if d._file] ) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def approval_cancellation(self, request, *args, **kwargs): try: instance = self.get_object() @@ -551,7 +560,7 @@ def approval_cancellation(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def approval_suspension(self, request, *args, **kwargs): try: instance = self.get_object() @@ -573,7 +582,7 @@ def approval_suspension(self, request, *args, **kwargs): raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def approval_reinstate(self, request, *args, **kwargs): try: instance = self.get_object() @@ -592,7 +601,7 @@ def approval_reinstate(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def approval_surrender(self, request, *args, **kwargs): try: instance = self.get_object() @@ -613,7 +622,7 @@ def approval_surrender(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def action_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -630,7 +639,7 @@ def action_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def comms_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -647,7 +656,7 @@ def comms_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def add_comms_log(self, request, *args, **kwargs): try: @@ -708,7 +717,7 @@ def create(self, request, *args, **kwargs): data = request.data dcv_vessel = self._handle_dcv_vessel(request.data.get('dcv_vessel'), None) - if request.user.is_authenticated(): + if request.user.is_authenticated: # Logged in user # 1. DcvPermit exists # 2. DcvPermit doesn't exist @@ -844,7 +853,7 @@ def _handle_dcv_vessel(request, org_id=None): return dcv_vessel - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def create_new_sticker(self, request, *args, **kwargs): instance = self.get_object() @@ -931,9 +940,11 @@ def filter_queryset(self, request, queryset, view): common_search_criteria ) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -974,7 +985,7 @@ def get_queryset(self): return qs - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_external(self, request, *args, **kwargs): """ User is accessing /external/ page @@ -992,7 +1003,7 @@ class DcvVesselViewSet(viewsets.ModelViewSet): queryset = DcvVessel.objects.all().order_by('id') serializer_class = DcvVesselSerializer - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def lookup_dcv_vessel(self, request, *args, **kwargs): dcv_vessel = self.get_object() @@ -1005,7 +1016,7 @@ def lookup_dcv_vessel(self, request, *args, **kwargs): return Response(dcv_vessel_data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_admissions(self, request, *args, **kwargs): vessel = self.get_object() @@ -1017,7 +1028,7 @@ def find_related_admissions(self, request, *args, **kwargs): serializer = LookupDcvAdmissionSerializer(admissions, many=True) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_permits(self, request, *args, **kwargs): vessel = self.get_object() @@ -1052,9 +1063,11 @@ def filter_queryset(self, request, queryset, view): queries &= Q(dcv_admission_arrivals__arrival_date__lte=filter_date_to) queryset = queryset.filter(queries) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -1110,9 +1123,11 @@ def filter_queryset(self, request, queryset, view): if filter_sticker_status_id and not filter_sticker_status_id.lower() == 'all': queryset = queryset.filter(status=filter_sticker_status_id) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -1135,7 +1150,7 @@ def get_queryset(self): qs = Sticker.objects.all() return qs - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def record_returned(self, request, *args, **kwargs): sticker = self.get_object() @@ -1154,7 +1169,7 @@ def record_returned(self, request, *args, **kwargs): serializer = StickerSerializer(sticker) return Response({'sticker': serializer.data}) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def record_lost(self, request, *args, **kwargs): sticker = self.get_object() @@ -1177,7 +1192,7 @@ def record_lost(self, request, *args, **kwargs): return Response({'sticker': serializer.data}) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def request_replacement(self, request, *args, **kwargs): # internal @@ -1238,7 +1253,7 @@ def get_queryset(self): return qs - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_external(self, request, *args, **kwargs): """ User is accessing /external/ page @@ -1256,7 +1271,7 @@ class WaitingListAllocationViewSet(viewsets.ModelViewSet): queryset = WaitingListAllocation.objects.all().order_by('id') serializer_class = WaitingListAllocationSerializer - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def create_mooring_licence_application(self, request, *args, **kwargs): with transaction.atomic(): diff --git a/mooringlicensing/components/approvals/email.py b/mooringlicensing/components/approvals/email.py index 386e5e371..6f45e8ca9 100755 --- a/mooringlicensing/components/approvals/email.py +++ b/mooringlicensing/components/approvals/email.py @@ -1,21 +1,28 @@ import logging import mimetypes +import requests from dateutil.relativedelta import relativedelta from django.contrib.auth.models import Group from django.core.mail import EmailMultiAlternatives, EmailMessage from django.utils.encoding import smart_text -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse +from django.urls import reverse from django.conf import settings from datetime import timedelta -from ledger.payments.pdf import create_invoice_pdf_bytes +# from ledger.payments.pdf import create_invoice_pdf_bytes from mooringlicensing import settings from mooringlicensing.components.emails.emails import TemplateEmailBase, _extract_email_headers -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO as EmailUser from mooringlicensing.components.emails.utils import get_user_as_email_user, get_public_url, make_http_https +from mooringlicensing.components.users.models import EmailUserLogEntry +# from mooringlicensing.components.main.utils import _log_user_email from mooringlicensing.components.organisations.models import OrganisationLogEntry, Organisation from django.core.files.storage import default_storage from django.core.files.base import ContentFile +from mooringlicensing.components.users.utils import _log_user_email +from mooringlicensing.ledger_api_utils import get_invoice_url logger = logging.getLogger(__name__) SYSTEM_NAME = settings.SYSTEM_NAME_SHORT + ' Automated Message' @@ -80,7 +87,7 @@ def send_auth_user_no_moorings_notification(approval): url = ''.join(url.split('-internal')) context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(), 'approval': approval, 'proposal': proposal, @@ -91,7 +98,7 @@ def send_auth_user_no_moorings_notification(approval): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL try: sender_user = EmailUser.objects.get(email__icontains=sender) @@ -102,9 +109,9 @@ def send_auth_user_no_moorings_notification(approval): _log_approval_email(msg, approval, sender=sender_user) #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_auth_user_mooring_removed_notification(approval, mooring_licence): @@ -119,7 +126,7 @@ def send_auth_user_mooring_removed_notification(approval, mooring_licence): url = ''.join(url.split('-internal')) context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(), 'approval': approval, 'proposal': proposal, @@ -131,7 +138,7 @@ def send_auth_user_mooring_removed_notification(approval, mooring_licence): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL try: sender_user = EmailUser.objects.get(email__icontains=sender) @@ -142,9 +149,9 @@ def send_auth_user_mooring_removed_notification(approval, mooring_licence): _log_approval_email(msg, approval, sender=sender_user) #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_approval_expire_email_notification(approval): @@ -165,7 +172,7 @@ def send_approval_expire_email_notification(approval): url = ''.join(url.split('-internal')) context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(), 'approval': approval, 'proposal': proposal, @@ -176,7 +183,7 @@ def send_approval_expire_email_notification(approval): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL try: sender_user = EmailUser.objects.get(email__icontains=sender) @@ -187,9 +194,9 @@ def send_approval_expire_email_notification(approval): _log_approval_email(msg, approval, sender=sender_user) #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_approval_cancelled_due_to_no_vessels_nominated_mail(approval, request=None): @@ -206,7 +213,7 @@ def send_approval_cancelled_due_to_no_vessels_nominated_mail(approval, request=N due_date = approval.current_proposal.vessel_ownership.end_date + relativedelta(months=+6) context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(request), 'approval': approval, 'due_date': due_date, @@ -221,7 +228,7 @@ def send_approval_cancelled_due_to_no_vessels_nominated_mail(approval, request=N #approver_group = ProposalApproverGroup.objects.all().first() - to_address = approval.submitter.email + to_address = approval.submitter_obj.email all_ccs = [] #bcc = approver_group.members_email # TODO: fix bcc with correct security group @@ -230,9 +237,9 @@ def send_approval_cancelled_due_to_no_vessels_nominated_mail(approval, request=N _log_approval_email(msg, approval, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) return msg @@ -250,7 +257,7 @@ def send_vessel_nomination_reminder_mail(approval, request=None): proposal = approval.current_proposal context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(request), 'approval': approval, 'date_to_nominate_new_vessel': approval.current_proposal.vessel_ownership.end_date + relativedelta(months=+6), @@ -264,7 +271,7 @@ def send_vessel_nomination_reminder_mail(approval, request=None): EmailUser.objects.create(email=sender, password='') sender_user = EmailUser.objects.get(email__icontains=sender) - to_address = approval.submitter.email + to_address = approval.submitter_obj.email all_ccs = [] bcc = [] @@ -272,9 +279,9 @@ def send_vessel_nomination_reminder_mail(approval, request=None): _log_approval_email(msg, approval, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) return msg @@ -302,7 +309,7 @@ def _log_approval_email(email_message, approval, sender=None, attachments=[]): else: text = smart_text(email_message) subject = '' - to = approval.current_proposal.submitter.email + to = approval.current_proposal.submitter_obj.email fromm = smart_text(sender) if sender else SYSTEM_NAME all_ccs = '' @@ -315,7 +322,7 @@ def _log_approval_email(email_message, approval, sender=None, attachments=[]): 'text': text, 'approval': approval, 'customer': customer, - 'staff': staff, + 'staff': staff.id, 'to': to, 'fromm': fromm, 'cc': all_ccs @@ -370,7 +377,7 @@ def _log_org_email(email_message, organisation, customer ,sender=None): 'text': text, 'organisation': organisation, 'customer': customer, - 'staff': staff, + 'staff': staff.id, 'to': to, 'fromm': fromm, 'cc': all_ccs @@ -381,58 +388,6 @@ def _log_org_email(email_message, organisation, customer ,sender=None): return email_entry -def _log_user_email(email_message, target_email_user, customer, sender=None, attachments=[]): - from ledger.accounts.models import EmailUserLogEntry - if isinstance(email_message, (EmailMultiAlternatives, EmailMessage,)): - # TODO this will log the plain text body, should we log the html instead - text = email_message.body - subject = email_message.subject - fromm = smart_text(sender) if sender else smart_text(email_message.from_email) - # the to email is normally a list - if isinstance(email_message.to, list): - to = ','.join(email_message.to) - else: - to = smart_text(email_message.to) - # we log the cc and bcc in the same cc field of the log entry as a ',' comma separated string - all_ccs = [] - if email_message.cc: - all_ccs += list(email_message.cc) - if email_message.bcc: - all_ccs += list(email_message.bcc) - all_ccs = ','.join(all_ccs) - - else: - text = smart_text(email_message) - subject = '' - to = customer - fromm = smart_text(sender) if sender else SYSTEM_NAME - all_ccs = '' - - customer = customer - - staff = sender - - kwargs = { - 'subject': subject, - 'text': text, - 'emailuser': target_email_user if target_email_user else customer, - 'customer': customer, - 'staff': staff, - 'to': to, - 'fromm': fromm, - 'cc': all_ccs - } - - email_entry = EmailUserLogEntry.objects.create(**kwargs) - - for attachment in attachments: - path_to_file = '{}/emailuser/{}/communications/{}'.format(settings.MEDIA_APP_DIR, target_email_user.id, attachment[0]) - path = default_storage.save(path_to_file, ContentFile(attachment[1])) - email_entry.documents.get_or_create(_file=path_to_file, name=attachment[0]) - - return email_entry - - def log_mla_created_proposal_email(email_message, proposal, sender=None): from mooringlicensing.components.proposals.models import ProposalLogEntry, ProposalLogDocument if isinstance(email_message, (EmailMultiAlternatives, EmailMessage,)): @@ -456,11 +411,11 @@ def log_mla_created_proposal_email(email_message, proposal, sender=None): else: text = smart_text(email_message) subject = '' - to = proposal.submitter.email + to = proposal.submitter_obj.email fromm = smart_text(sender) if sender else SYSTEM_NAME all_ccs = '' - customer = proposal.submitter + customer = proposal.submitter_obj staff = sender @@ -469,7 +424,7 @@ def log_mla_created_proposal_email(email_message, proposal, sender=None): 'text': text, 'proposal': proposal, 'customer': customer, - 'staff': staff, + 'staff': staff.id, 'to': to, 'fromm': fromm, 'cc': all_ccs @@ -512,8 +467,13 @@ def send_dcv_permit_mail(dcv_permit, invoice, request): attachments = [] # attach invoice - contents = create_invoice_pdf_bytes('invoice.pdf', invoice,) - attachments.append(('invoice#{}.pdf'.format(invoice.reference), contents, 'application/pdf')) + # contents = create_invoice_pdf_bytes('invoice.pdf', invoice,) + # attachments.append(('invoice#{}.pdf'.format(invoice.reference), contents, 'application/pdf')) + url = get_invoice_url(invoice.reference, request) + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = (f'invoice#{invoice.reference}', invoice_pdf.content, 'application/pdf') + attachments.append(attachment) # attach DcvPermit dcv_permit_doc = dcv_permit.permits.first() @@ -522,7 +482,7 @@ def send_dcv_permit_mail(dcv_permit, invoice, request): mime = mimetypes.guess_type(dcv_permit_doc.filename)[0] attachments.append((filename, content, mime)) - to = dcv_permit.submitter.email + to = dcv_permit.submitter_obj.email cc = [] bcc = [] @@ -568,8 +528,13 @@ def send_dcv_admission_mail(dcv_admission, invoice, request): # attach invoice if invoice: - contents = create_invoice_pdf_bytes('invoice.pdf', invoice,) - attachments.append(('invoice#{}.pdf'.format(invoice.reference), contents, 'application/pdf')) + # contents = create_invoice_pdf_bytes('invoice.pdf', invoice,) + # attachments.append(('invoice#{}.pdf'.format(invoice.reference), contents, 'application/pdf')) + url = get_invoice_url(invoice.reference, request) + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = (f'invoice#{invoice.reference}', invoice_pdf.content, 'application/pdf') + attachments.append(attachment) # attach DcvPermit if dcv_admission.admissions: @@ -580,7 +545,7 @@ def send_dcv_admission_mail(dcv_admission, invoice, request): mime = mimetypes.guess_type(dcv_admission_doc.filename)[0] attachments.append((filename, content, mime)) - to = dcv_admission.submitter.email + to = dcv_admission.submitter_obj.email cc = [] bcc = [] @@ -612,7 +577,7 @@ def send_approval_cancel_email_notification(approval): context = { 'public_url': get_public_url(), 'approval': approval, - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'cancel_start_date': approval.cancellation_date, 'details': approval.cancellation_details, } @@ -627,14 +592,14 @@ def send_approval_cancel_email_notification(approval): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender_user) #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_approval_suspend_email_notification(approval, request=None): @@ -659,7 +624,7 @@ def send_approval_suspend_email_notification(approval, request=None): to_date = approval.suspension_details['to_date'] if 'to_date' in approval.suspension_details else '' context = { - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'public_url': get_public_url(request), 'approval': approval, 'details': details, @@ -677,14 +642,14 @@ def send_approval_suspend_email_notification(approval, request=None): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender_user) #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_approval_surrender_email_notification(approval, request=None): @@ -709,7 +674,7 @@ def send_approval_surrender_email_notification(approval, request=None): context = { 'public_url': get_public_url(request), 'approval': approval, - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'details': details, 'surrender_date': surrender_date, } @@ -724,13 +689,13 @@ def send_approval_surrender_email_notification(approval, request=None): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user) def send_approval_reinstate_email_notification(approval, request): @@ -748,7 +713,7 @@ def send_approval_reinstate_email_notification(approval, request): context = { 'public_url': get_public_url(request), 'approval': approval, - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'details': '', # TODO } all_ccs = [] @@ -756,14 +721,14 @@ def send_approval_reinstate_email_notification(approval, request): cc_list = proposal.org_applicant.email if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender) - #_log_org_email(msg, approval.applicant, proposal.submitter, sender=sender) + #_log_org_email(msg, approval.applicant, proposal.submitter_obj, sender=sender) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_reissue_ml_after_sale_recorded_email(approval, request, vessel_ownership, stickers_to_be_returned): @@ -797,7 +762,7 @@ def send_reissue_ml_after_sale_recorded_email(approval, request, vessel_ownershi context = { 'public_url': get_public_url(request), - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'vessel_rego_no': vessel_ownership.vessel.rego_no, 'stickers_to_be_returned': stickers_to_be_returned, 'due_date': due_date, @@ -809,14 +774,14 @@ def send_reissue_ml_after_sale_recorded_email(approval, request, vessel_ownershi if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_reissue_wla_after_sale_recorded_email(approval, request, vessel_ownership, stickers_to_be_returned): @@ -841,7 +806,7 @@ def send_reissue_wla_after_sale_recorded_email(approval, request, vessel_ownersh context = { 'public_url': get_public_url(request), - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'vessel_rego_no': vessel_ownership.vessel.rego_no, 'stickers_to_be_returned': stickers_to_be_returned, 'due_date': due_date, @@ -853,14 +818,14 @@ def send_reissue_wla_after_sale_recorded_email(approval, request, vessel_ownersh if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_reissue_aup_after_sale_recorded_email(approval, request, vessel_ownership, stickers_to_be_returned): @@ -886,7 +851,7 @@ def send_reissue_aup_after_sale_recorded_email(approval, request, vessel_ownersh context = { 'public_url': get_public_url(request), - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'vessel_rego_no': vessel_ownership.vessel.rego_no, 'stickers_to_be_returned': stickers_to_be_returned, 'due_date': due_date, @@ -898,14 +863,14 @@ def send_reissue_aup_after_sale_recorded_email(approval, request, vessel_ownersh if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_reissue_aap_after_sale_recorded_email(approval, request, vessel_ownership, stickers_to_be_returned): @@ -931,7 +896,7 @@ def send_reissue_aap_after_sale_recorded_email(approval, request, vessel_ownersh context = { 'public_url': get_public_url(request), - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'vessel_rego_no': vessel_ownership.vessel.rego_no, 'stickers_to_be_returned': stickers_to_be_returned, 'due_date': due_date, @@ -943,14 +908,14 @@ def send_reissue_aap_after_sale_recorded_email(approval, request, vessel_ownersh if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_sticker_replacement_email(request, old_sticker, new_sticker, invoice): @@ -967,13 +932,18 @@ def send_sticker_replacement_email(request, old_sticker, new_sticker, invoice): # Attach invoice attachments = [] - invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', invoice, ) - attachment = ('invoice#{}.pdf'.format(invoice.reference), invoice_bytes, 'application/pdf') - attachments.append(attachment) + # invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', invoice, ) + # attachment = ('invoice#{}.pdf'.format(invoice.reference), invoice_bytes, 'application/pdf') + # attachments.append(attachment) + url = get_invoice_url(invoice.reference, request) + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = (f'invoice#{invoice.reference}', invoice_pdf.content, 'application/pdf') + attachments.append(attachment) context = { 'public_url': get_public_url(request), - 'recipient': approval.submitter, + 'recipient': approval.submitter_obj, 'sticker': old_sticker, 'dashboard_external_url': get_public_url(request), } @@ -984,14 +954,14 @@ def send_sticker_replacement_email(request, old_sticker, new_sticker, invoice): if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_aup_revoked_due_to_mooring_swap_email(request, authorised_user_permit, mooring, stickers_to_be_returned): @@ -1025,14 +995,14 @@ def send_aup_revoked_due_to_mooring_swap_email(request, authorised_user_permit, if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) def send_aup_revoked_due_to_relinquishment_email(request, authorised_user_permit, mooring, stickers_to_be_returned): @@ -1066,14 +1036,14 @@ def send_aup_revoked_due_to_relinquishment_email(request, authorised_user_permit if cc_list: all_ccs = [cc_list] - msg = email.send(proposal.submitter.email, cc=all_ccs, context=context, attachments=attachments) + msg = email.send(proposal.submitter_obj.email, cc=all_ccs, context=context, attachments=attachments) sender = request.user if request else settings.DEFAULT_FROM_EMAIL _log_approval_email(msg, approval, sender=sender, attachments=attachments) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender) # 39 # email to account holder with authentication link to complete login process diff --git a/mooringlicensing/components/approvals/models.py b/mooringlicensing/components/approvals/models.py index 6472ff3b8..44d080a70 100755 --- a/mooringlicensing/components/approvals/models.py +++ b/mooringlicensing/components/approvals/models.py @@ -1,9 +1,12 @@ -from __future__ import unicode_literals + + +import ledger_api_client.utils from django.core.files.base import ContentFile import datetime import logging import re +import uuid import pytz from django.db import models,transaction @@ -15,18 +18,22 @@ from django.utils import timezone from django.conf import settings from django.db.models import Q -from ledger.settings_base import TIME_ZONE -from ledger.accounts.models import EmailUser, RevisionedMixin -from ledger.payments.invoice.models import Invoice + +from mooringlicensing.ledger_api_utils import retrieve_email_userro, get_invoice_payment_status +# from ledger.settings_base import TIME_ZONE +from mooringlicensing.settings import TIME_ZONE +# from ledger.accounts.models import EmailUser, RevisionedMixin +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice, EmailUserRO from mooringlicensing.components.approvals.pdf import create_dcv_permit_document, create_dcv_admission_document, \ create_approval_doc, create_renewal_doc from mooringlicensing.components.emails.utils import get_public_url from mooringlicensing.components.organisations.models import Organisation -from mooringlicensing.components.payments_ml.models import StickerActionFee +from mooringlicensing.components.payments_ml.models import StickerActionFee, FeeConstructor from mooringlicensing.components.proposals.models import Proposal, ProposalUserAction, MooringBay, Mooring, \ StickerPrintingBatch, StickerPrintingResponse, Vessel, VesselOwnership, ProposalType from mooringlicensing.components.main.models import CommunicationsLogEntry, UserAction, Document, \ - GlobalSettings # , ApplicationType + GlobalSettings, RevisionedMixin, ApplicationType # , ApplicationType from mooringlicensing.components.approvals.email import ( send_approval_expire_email_notification, send_approval_cancel_email_notification, @@ -38,8 +45,10 @@ ) from mooringlicensing.helpers import is_customer from mooringlicensing.settings import PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_NEW +from ledger_api_client.utils import calculate_excl_gst -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) def update_waiting_list_offer_doc_filename(instance, filename): @@ -53,7 +62,7 @@ def update_approval_comms_log_filename(instance, filename): class WaitingListOfferDocument(Document): - approval = models.ForeignKey('Approval',related_name='waiting_list_offer_documents') + approval = models.ForeignKey('Approval',related_name='waiting_list_offer_documents', on_delete=models.CASCADE) _file = models.FileField(max_length=512) input_name = models.CharField(max_length=255,null=True,blank=True) can_delete = models.BooleanField(default=True) # after initial submit prevent document from being deleted @@ -66,8 +75,8 @@ class Meta: class RenewalDocument(Document): - approval = models.ForeignKey('Approval',related_name='renewal_documents') - _file = models.FileField(upload_to=update_approval_doc_filename) + approval = models.ForeignKey('Approval',related_name='renewal_documents', on_delete=models.CASCADE) + _file = models.FileField(upload_to=update_approval_doc_filename, max_length=512) can_delete = models.BooleanField(default=True) # after initial submit prevent document from being deleted def delete(self): @@ -80,7 +89,7 @@ class Meta: class AuthorisedUserSummaryDocument(Document): - approval = models.ForeignKey('Approval', related_name='authorised_user_summary_documents') + approval = models.ForeignKey('Approval', related_name='authorised_user_summary_documents', on_delete=models.CASCADE) _file = models.FileField(upload_to=update_approval_doc_filename, max_length=512) class Meta: @@ -88,7 +97,7 @@ class Meta: class ApprovalDocument(Document): - approval = models.ForeignKey('Approval',related_name='documents') + approval = models.ForeignKey('Approval',related_name='documents', on_delete=models.CASCADE) _file = models.FileField(upload_to=update_approval_doc_filename, max_length=512) can_delete = models.BooleanField(default=True) # after initial submit prevent document from being deleted @@ -102,9 +111,9 @@ class Meta: class MooringOnApproval(RevisionedMixin): - approval = models.ForeignKey('Approval') - mooring = models.ForeignKey(Mooring) - sticker = models.ForeignKey('Sticker', blank=True, null=True) + approval = models.ForeignKey('Approval', on_delete=models.CASCADE) + mooring = models.ForeignKey(Mooring, on_delete=models.CASCADE) + sticker = models.ForeignKey('Sticker', blank=True, null=True, on_delete=models.SET_NULL) site_licensee = models.BooleanField() end_date = models.DateField(blank=True, null=True) @@ -130,8 +139,8 @@ class Meta: class VesselOwnershipOnApproval(RevisionedMixin): - approval = models.ForeignKey('Approval') - vessel_ownership = models.ForeignKey(VesselOwnership) + approval = models.ForeignKey('Approval', on_delete=models.CASCADE) + vessel_ownership = models.ForeignKey(VesselOwnership, on_delete=models.CASCADE) end_date = models.DateField(blank=True, null=True) def __str__(self): @@ -162,14 +171,14 @@ class ApprovalHistory(RevisionedMixin): #reason = models.CharField(max_length=40, choices=REASON_CHOICES, blank=True, null=True) reason = models.CharField(max_length=100, blank=True, null=True) #default=REASON_CHOICES[0][0]) - approval = models.ForeignKey('Approval') + approval = models.ForeignKey('Approval', on_delete=models.CASCADE) # can be null due to requirement to allow null vessels on renewal/amendment applications - vessel_ownership = models.ForeignKey(VesselOwnership, blank=True, null=True) - proposal = models.ForeignKey(Proposal,related_name='approval_history_records') + vessel_ownership = models.ForeignKey(VesselOwnership, blank=True, null=True, on_delete=models.SET_NULL) + proposal = models.ForeignKey(Proposal, related_name='approval_history_records', on_delete=models.CASCADE) start_date = models.DateTimeField() end_date = models.DateTimeField(blank=True, null=True) stickers = models.ManyToManyField('Sticker') - approval_letter = models.ForeignKey(ApprovalDocument, blank=True, null=True) + approval_letter = models.ForeignKey(ApprovalDocument, blank=True, null=True, on_delete=models.SET_NULL) # derive from proposal class Meta: @@ -209,12 +218,12 @@ class Approval(RevisionedMixin): status = models.CharField(max_length=40, choices=STATUS_CHOICES, default=STATUS_CHOICES[0][0]) internal_status = models.CharField(max_length=40, choices=INTERNAL_STATUS_CHOICES, blank=True, null=True) - licence_document = models.ForeignKey(ApprovalDocument, blank=True, null=True, related_name='licence_document') - authorised_user_summary_document = models.ForeignKey(AuthorisedUserSummaryDocument, blank=True, null=True, related_name='approvals') - cover_letter_document = models.ForeignKey(ApprovalDocument, blank=True, null=True, related_name='cover_letter_document') - replaced_by = models.OneToOneField('self', blank=True, null=True, related_name='replace') - current_proposal = models.ForeignKey(Proposal,related_name='approvals', null=True) - renewal_document = models.ForeignKey(RenewalDocument, blank=True, null=True, related_name='renewal_document') + licence_document = models.ForeignKey(ApprovalDocument, blank=True, null=True, related_name='licence_document', on_delete=models.SET_NULL) + authorised_user_summary_document = models.ForeignKey(AuthorisedUserSummaryDocument, blank=True, null=True, related_name='approvals', on_delete=models.SET_NULL) + cover_letter_document = models.ForeignKey(ApprovalDocument, blank=True, null=True, related_name='cover_letter_document', on_delete=models.SET_NULL) + replaced_by = models.OneToOneField('self', blank=True, null=True, related_name='replace', on_delete=models.SET_NULL) + current_proposal = models.ForeignKey(Proposal,related_name='approvals', null=True, on_delete=models.SET_NULL) + renewal_document = models.ForeignKey(RenewalDocument, blank=True, null=True, related_name='renewal_document', on_delete=models.SET_NULL) renewal_sent = models.BooleanField(default=False) issue_date = models.DateTimeField() wla_queue_date = models.DateTimeField(blank=True, null=True) @@ -223,9 +232,11 @@ class Approval(RevisionedMixin): expiry_date = models.DateField(blank=True, null=True) surrender_details = JSONField(blank=True,null=True) suspension_details = JSONField(blank=True,null=True) - submitter = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='mooringlicensing_approvals') - org_applicant = models.ForeignKey(Organisation,on_delete=models.PROTECT, blank=True, null=True, related_name='org_approvals') - proxy_applicant = models.ForeignKey(EmailUser,on_delete=models.PROTECT, blank=True, null=True, related_name='proxy_approvals') + # submitter = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='mooringlicensing_approvals') + submitter = models.IntegerField(blank=True, null=True) + org_applicant = models.ForeignKey(Organisation, on_delete=models.PROTECT, blank=True, null=True, related_name='org_approvals') + # proxy_applicant = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='proxy_approvals') + proxy_applicant = models.IntegerField(blank=True, null=True) extracted_fields = JSONField(blank=True, null=True) cancellation_details = models.TextField(blank=True) extend_details = models.TextField(blank=True) @@ -252,6 +263,10 @@ class Meta: unique_together = ('lodgement_number', 'issue_date') ordering = ['-id',] + @property + def submitter_obj(self): + return retrieve_email_userro(self.submitter) if self.submitter else None + def get_max_fee_item(self, fee_season, vessel_details=None): max_fee_item = None for proposal in self.proposal_set.all(): @@ -278,70 +293,75 @@ def get_licence_document_as_attachment(self): def postal_address_line1(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.line1 + submitter = retrieve_email_userro(self.submitter) + if submitter.postal_same_as_residential: + ret_value = submitter.residential_address.line1 else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.line1 + if submitter.postal_address: + ret_value = submitter.postal_address.line1 if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.line1 + ret_value = submitter.residential_address.line1 return ret_value @property def postal_address_line2(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.line2 + submitter = retrieve_email_userro(self.submitter) + if submitter.postal_same_as_residential: + ret_value = submitter.residential_address.line2 else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.line2 + if submitter.postal_address: + ret_value = submitter.postal_address.line2 if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.line2 + ret_value = submitter.residential_address.line2 return ret_value @property def postal_address_state(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.state + submitter = retrieve_email_userro(self.submitter) + if submitter.postal_same_as_residential: + ret_value = submitter.residential_address.state else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.state + if submitter.postal_address: + ret_value = submitter.postal_address.state if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.state + ret_value = submitter.residential_address.state return ret_value @property def postal_address_suburb(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.locality + submitter = retrieve_email_userro(self.submitter) + if submitter.postal_same_as_residential: + ret_value = submitter.residential_address.locality else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.locality + if submitter.postal_address: + ret_value = submitter.postal_address.locality if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.locality + ret_value = submitter.residential_address.locality return ret_value @property def postal_address_postcode(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.postcode + submitter = retrieve_email_userro(self.submitter) + if submitter.postal_same_as_residential: + ret_value = submitter.residential_address.postcode else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.postcode + if submitter.postal_address: + ret_value = submitter.postal_address.postcode if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.postcode + ret_value = submitter.residential_address.postcode return ret_value @property @@ -481,14 +501,16 @@ def applicant(self): if self.org_applicant: return self.org_applicant.organisation.name elif self.proxy_applicant: + applicant = retrieve_email_userro(self.proxy_applicant) return "{} {}".format( - self.proxy_applicant.first_name, - self.proxy_applicant.last_name) + applicant.first_name, + applicant.last_name) else: try: + submitter = retrieve_email_userro(self.submitter) return "{} {}".format( - self.submitter.first_name, - self.submitter.last_name) + submitter.first_name, + submitter.last_name) except: return "Applicant Not Set" @@ -516,9 +538,11 @@ def applicant_id(self): if self.org_applicant: return self.org_applicant.id elif self.proxy_applicant: - return self.proxy_applicant.id + # return self.proxy_applicant.id + return self.proxy_applicant else: - return self.submitter.id + # return self.submitter.id + return self.submitter @property def title(self): @@ -566,9 +590,13 @@ def allowed_assessors_user(self, request): return self.current_proposal.allowed_assessors_user(request) def is_assessor(self,user): + if isinstance(user, EmailUserRO): + user = user.id return self.current_proposal.is_assessor(user) def is_approver(self,user): + if isinstance(user, EmailUserRO): + user = user.id return self.current_proposal.is_approver(user) @property @@ -701,12 +729,7 @@ def approval_cancellation(self,request,details): self.set_to_cancel = True self.save() if type(self.child_obj) == WaitingListAllocation: - wla = self.child_obj - wla.internal_status = None - wla.wla_queue_date = None - wla.wla_order = None - wla.save() - wla.set_wla_order() + self.child_obj.processes_after_cancel() # Log proposal action self.log_user_action(ApprovalUserAction.ACTION_CANCEL_APPROVAL.format(self.id),request) # Log entry for organisation @@ -811,12 +834,7 @@ def approval_surrender(self,request,details): self.set_to_surrender = True self.save() if type(self.child_obj) == WaitingListAllocation: - wla = self.child_obj - wla.internal_status = None - wla.wla_queue_date = None - wla.wla_order = None - wla.save() - wla.set_wla_order() + self.child_obj.processes_after_cancel() # Log approval action self.log_user_action(ApprovalUserAction.ACTION_SURRENDER_APPROVAL.format(self.id),request) # Log entry for proposal @@ -926,7 +944,7 @@ def manage_stickers(self, proposal): class WaitingListAllocation(Approval): - approval = models.OneToOneField(Approval, parent_link=True) + approval = models.OneToOneField(Approval, parent_link=True, on_delete=models.PROTECT) code = 'wla' prefix = 'WLA' description = 'Waiting List Allocation' @@ -956,12 +974,16 @@ def get_context_for_licence_permit(self): vessel_draft = '' # Return context for the licence/permit document + from mooringlicensing.ledger_api_utils import retrieve_email_userro + # submitter = retrieve_email_userro(self.submitter) context = { 'approval': self, 'application': self.current_proposal, 'issue_date': self.issue_date.strftime('%d/%m/%Y'), - 'applicant_name': self.submitter.get_full_name(), - 'applicant_full_name': self.submitter.get_full_name(), + # 'applicant_name': self.submitter.get_full_name(), + # 'applicant_full_name': self.submitter.get_full_name(), + 'applicant_name': self.submitter_obj.get_full_name(), + 'applicant_full_name': self.submitter_obj.get_full_name(), 'bay_name': self.current_proposal.preferred_bay.name, 'allocation_date': self.wla_queue_date.strftime('%d/%m/%Y'), 'position_number': self.wla_order, @@ -992,6 +1014,9 @@ def manage_stickers(self, proposal): return [], [] def set_wla_order(self): + """ + Renumber all the related allocations with 'current'/'suspended' status from #1 to #n + """ place = 1 # set wla order per bay for current allocations for w in WaitingListAllocation.objects.filter( @@ -1003,12 +1028,18 @@ def set_wla_order(self): w.wla_order = place w.save() place += 1 - self.refresh_from_db() + self.refresh_from_db() # Should be self.proposal.refresh_from_db()??? return self + def processes_after_cancel(self): + self.internal_status = None + self.wla_queue_date = None + self.wla_order = None + self.save() + self.set_wla_order() class AnnualAdmissionPermit(Approval): - approval = models.OneToOneField(Approval, parent_link=True) + approval = models.OneToOneField(Approval, parent_link=True, on_delete=models.PROTECT) code = 'aap' prefix = 'AAP' description = 'Annual Admission Permit' @@ -1040,7 +1071,7 @@ def get_context_for_licence_permit(self): 'approval': self, 'application': self.current_proposal, 'issue_date': self.issue_date.strftime('%d/%m/%Y'), - 'applicant_name': self.submitter.get_full_name(), + 'applicant_name': retrieve_email_userro(self.submitter).get_full_name(), 'p_address_line1': self.postal_address_line1, 'p_address_line2': self.postal_address_line2, 'p_address_suburb': self.postal_address_suburb, @@ -1161,7 +1192,7 @@ def manage_stickers(self, proposal): class AuthorisedUserPermit(Approval): - approval = models.OneToOneField(Approval, parent_link=True) + approval = models.OneToOneField(Approval, parent_link=True, on_delete=models.PROTECT) code = 'aup' prefix = 'AUP' description = 'Authorised User Permit' @@ -1187,13 +1218,13 @@ def get_context_for_licence_permit(self): m = {} # calculate phone number(s) numbers = [] - if mooring.mooring_licence.submitter.mobile_number: - numbers.append(mooring.mooring_licence.submitter.mobile_number) - elif mooring.mooring_licence.submitter.phone_number: - numbers.append(mooring.mooring_licence.submitter.phone_number) + if mooring.mooring_licence.submitter_obj.mobile_number: + numbers.append(mooring.mooring_licence.submitter_obj.mobile_number) + elif mooring.mooring_licence.submitter_obj.phone_number: + numbers.append(mooring.mooring_licence.submitter_obj.phone_number) m['name'] = mooring.name - m['licensee_full_name'] = mooring.mooring_licence.submitter.get_full_name() - m['licensee_email'] = mooring.mooring_licence.submitter.email + m['licensee_full_name'] = mooring.mooring_licence.submitter_obj.get_full_name() + m['licensee_email'] = mooring.mooring_licence.submitter_obj.email m['licensee_phone'] = ', '.join(numbers) moorings.append(m) @@ -1214,7 +1245,7 @@ def get_context_for_licence_permit(self): 'approval': self, 'application': self.current_proposal, 'issue_date': self.issue_date.strftime('%d/%m/%Y'), - 'applicant_name': self.submitter.get_full_name(), + 'applicant_name': self.submitter_obj.get_full_name(), 'p_address_line1': self.postal_address_line1, 'p_address_line2': self.postal_address_line2, 'p_address_suburb': self.postal_address_suburb, @@ -1532,7 +1563,7 @@ def _assign_to_new_stickers(self, moas_to_be_on_new_sticker, proposal, stickers_ class MooringLicence(Approval): - approval = models.OneToOneField(Approval, parent_link=True) + approval = models.OneToOneField(Approval, parent_link=True, on_delete=models.PROTECT) code = 'ml' prefix = 'MOL' description = 'Mooring Licence' @@ -1564,7 +1595,7 @@ def get_context_for_au_summary(self): authorised_by = aup.get_authorised_by() authorised_by = authorised_by.upper().replace('_', ' ') - authorised_person['full_name'] = aup.submitter.get_full_name() + authorised_person['full_name'] = aup.submitter_obj.get_full_name() authorised_person['vessel'] = { 'rego_no': aup.current_proposal.vessel_details.vessel.rego_no if aup.current_proposal.vessel_details else '', 'vessel_name': aup.current_proposal.vessel_details.vessel_name if aup.current_proposal.vessel_details else '', @@ -1573,8 +1604,8 @@ def get_context_for_au_summary(self): } authorised_person['authorised_date'] = aup.issue_date.strftime('%d/%m/%Y') authorised_person['authorised_by'] = authorised_by - authorised_person['mobile_number'] = aup.submitter.mobile_number - authorised_person['email_address'] = aup.submitter.email + authorised_person['mobile_number'] = aup.submitter_obj.mobile_number + authorised_person['email_address'] = aup.submitter_obj.email authorised_persons.append(authorised_person) today = datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)).date() @@ -1624,7 +1655,7 @@ def get_context_for_licence_permit(self): 'approval': self, 'application': self.current_proposal, 'issue_date': self.issue_date.strftime('%d/%m/%Y'), - 'applicant_name': self.submitter.get_full_name(), + 'applicant_name': self.submitter_obj.get_full_name(), 'p_address_line1': self.postal_address_line1, 'p_address_line2': self.postal_address_line2, 'p_address_suburb': self.postal_address_suburb, @@ -1896,7 +1927,7 @@ class Meta: class ApprovalLogEntry(CommunicationsLogEntry): - approval = models.ForeignKey(Approval, related_name='comms_logs') + approval = models.ForeignKey(Approval, related_name='comms_logs', on_delete=models.CASCADE) class Meta: app_label = 'mooringlicensing' @@ -1908,7 +1939,7 @@ def save(self, **kwargs): super(ApprovalLogEntry, self).save(**kwargs) class ApprovalLogDocument(Document): - log_entry = models.ForeignKey('ApprovalLogEntry',related_name='documents', null=True,) + log_entry = models.ForeignKey('ApprovalLogEntry',related_name='documents', null=True, on_delete=models.CASCADE) _file = models.FileField(upload_to=update_approval_comms_log_filename, null=True, max_length=512) class Meta: @@ -1937,14 +1968,15 @@ class Meta: def log_action(cls, approval, action, user=None): return cls.objects.create( approval=approval, - who=user, + who=user.id if user else None, what=str(action) ) - who = models.ForeignKey(EmailUser, null=True, blank=True) + # who = models.ForeignKey(EmailUser, null=True, blank=True, on_delete=models.PROTECT) + who = models.IntegerField(null=True, blank=True) when = models.DateTimeField(null=False, blank=False, auto_now_add=True) what = models.TextField(blank=False) - approval= models.ForeignKey(Approval, related_name='action_logs') + approval= models.ForeignKey(Approval, related_name='action_logs', on_delete=models.PROTECT) class DcvOrganisation(RevisionedMixin): @@ -1961,7 +1993,7 @@ class Meta: class DcvVessel(RevisionedMixin): rego_no = models.CharField(max_length=200, unique=True, blank=True, null=True) vessel_name = models.CharField(max_length=400, blank=True) - dcv_organisation = models.ForeignKey(DcvOrganisation, blank=True, null=True) + dcv_organisation = models.ForeignKey(DcvOrganisation, blank=True, null=True, on_delete=models.SET_NULL) def __str__(self): return self.rego_no @@ -1973,22 +2005,29 @@ class Meta: class DcvAdmission(RevisionedMixin): LODGEMENT_NUMBER_PREFIX = 'DCV' - submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='dcv_admissions') + # submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='dcv_admissions', on_delete=models.SET_NULL) + submitter = models.IntegerField(blank=True, null=True) lodgement_number = models.CharField(max_length=10, blank=True, unique=True) lodgement_datetime = models.DateTimeField(blank=True, null=True) # This is the datetime when payment skipper = models.CharField(max_length=50, blank=True, null=True) contact_number = models.CharField(max_length=50, blank=True, null=True) - dcv_vessel = models.ForeignKey(DcvVessel, blank=True, null=True, related_name='dcv_admissions') + dcv_vessel = models.ForeignKey(DcvVessel, blank=True, null=True, related_name='dcv_admissions', on_delete=models.SET_NULL) + # uuid = models.CharField(max_length=36, blank=True, null=True) class Meta: app_label = 'mooringlicensing' + @property + def submitter_obj(self): + return retrieve_email_userro(self.submitter) if self.submitter else None + def __str__(self): return self.lodgement_number @property def fee_paid(self): - if self.invoice and self.invoice.payment_status in ['paid', 'over_paid']: + # if self.invoice and self.invoice.payment_status in ['paid', 'over_paid']: + if self.invoice and get_invoice_payment_status(self.invoice.id) in ['paid', 'over_paid']: return True return False @@ -2042,13 +2081,69 @@ def get_admission_urls(self): urls.append(admission._file.url) return urls + def create_fee_lines(self): + db_processes_after_success = {} + + target_datetime = datetime.datetime.now(pytz.timezone(TIME_ZONE)) + target_date = target_datetime.date() + target_datetime_str = target_datetime.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d/%m/%Y %I:%M %p') + + db_processes_after_success['datetime_for_calculating_fee'] = target_datetime.__str__() + + application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_DCV_ADMISSION['code']) + # vessel_length = 1 # any number greater than 0 + vessel_length = GlobalSettings.default_values[GlobalSettings.KEY_MINIMUM_VESSEL_LENGTH] + 1 + proposal_type = None + oracle_code = application_type.get_oracle_code_by_date(target_date=target_date) + + line_items = [] + for dcv_admission_arrival in self.dcv_admission_arrivals.all(): + fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_date(application_type, dcv_admission_arrival.arrival_date) + db_processes_after_success['fee_constructor_id'] = fee_constructor.id + + if not fee_constructor: + raise Exception('FeeConstructor object for the ApplicationType: {} and the Season: {}'.format(application_type, dcv_admission_arrival.arrival_date)) + + fee_items = [] + number_of_people_str = [] + total_amount = 0 + + for number_of_people in dcv_admission_arrival.numberofpeople_set.all(): + if number_of_people.number: + # When more than 1 people, + fee_item = fee_constructor.get_fee_item(vessel_length, proposal_type, dcv_admission_arrival.arrival_date, number_of_people.age_group, number_of_people.admission_type) + fee_item.number_of_people = number_of_people.number + fee_items.append(fee_item) + number_of_people_str.append('[{}-{}: {}]'.format(number_of_people.age_group, number_of_people.admission_type, number_of_people.number)) + total_amount += fee_item.get_absolute_amount() * number_of_people.number + + db_processes_after_success['fee_item_ids'] = [item.id for item in fee_items] + + line_item = { + 'ledger_description': '{} Fee: {} (Arrival: {}, Private: {}, {})'.format( + fee_constructor.application_type.description, + self.lodgement_number, + dcv_admission_arrival.arrival_date, + dcv_admission_arrival.private_visit, + ', '.join(number_of_people_str), + ), + 'oracle_code': oracle_code, + 'price_incl_tax': total_amount, + 'price_excl_tax': calculate_excl_gst(total_amount) if fee_constructor.incur_gst else total_amount, + 'quantity': 1, + } + line_items.append(line_item) + + logger.info('{}'.format(line_items)) + + return line_items, db_processes_after_success class DcvAdmissionArrival(RevisionedMixin): - dcv_admission = models.ForeignKey(DcvAdmission, null=True, blank=True, related_name='dcv_admission_arrivals') + dcv_admission = models.ForeignKey(DcvAdmission, null=True, blank=True, related_name='dcv_admission_arrivals', on_delete=models.SET_NULL) arrival_date = models.DateField(null=True, blank=True) departure_date = models.DateField(null=True, blank=True) private_visit = models.BooleanField(default=False) - fee_season = models.ForeignKey('FeeSeason', null=True, blank=True) + fee_season = models.ForeignKey('FeeSeason', null=True, blank=True, on_delete=models.SET_NULL) start_date = models.DateField(null=True, blank=True) # This is the season.start_date when payment end_date = models.DateField(null=True, blank=True) # This is the season.end_date when payment fee_constructor = models.ForeignKey('FeeConstructor', on_delete=models.PROTECT, blank=True, null=True, related_name='dcv_admission_arrivals') @@ -2133,9 +2228,9 @@ class Meta: class NumberOfPeople(RevisionedMixin): number = models.PositiveSmallIntegerField(null=True, blank=True, default=0) - dcv_admission_arrival = models.ForeignKey(DcvAdmissionArrival, null=True, blank=True) - age_group = models.ForeignKey(AgeGroup, null=True, blank=True) - admission_type = models.ForeignKey(AdmissionType, null=True, blank=True) + dcv_admission_arrival = models.ForeignKey(DcvAdmissionArrival, null=True, blank=True, on_delete=models.SET_NULL) + age_group = models.ForeignKey(AgeGroup, null=True, blank=True, on_delete=models.SET_NULL) + admission_type = models.ForeignKey(AdmissionType, null=True, blank=True, on_delete=models.SET_NULL) class Meta: app_label = 'mooringlicensing' @@ -2155,85 +2250,168 @@ class DcvPermit(RevisionedMixin): ) LODGEMENT_NUMBER_PREFIX = 'DCVP' - submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='dcv_permits') + # submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='dcv_permits', on_delete=models.SET_NULL) + submitter = models.IntegerField(blank=True, null=True) lodgement_number = models.CharField(max_length=10, blank=True, unique=True) lodgement_datetime = models.DateTimeField(blank=True, null=True) # This is the datetime when payment - fee_season = models.ForeignKey('FeeSeason', null=True, blank=True, related_name='dcv_permits') + fee_season = models.ForeignKey('FeeSeason', null=True, blank=True, related_name='dcv_permits', on_delete=models.SET_NULL) start_date = models.DateField(null=True, blank=True) # This is the season.start_date when payment end_date = models.DateField(null=True, blank=True) # This is the season.end_date when payment - dcv_vessel = models.ForeignKey(DcvVessel, blank=True, null=True, related_name='dcv_permits') - dcv_organisation = models.ForeignKey(DcvOrganisation, blank=True, null=True) + dcv_vessel = models.ForeignKey(DcvVessel, blank=True, null=True, related_name='dcv_permits', on_delete=models.SET_NULL) + dcv_organisation = models.ForeignKey(DcvOrganisation, blank=True, null=True, on_delete=models.SET_NULL) renewal_sent = models.BooleanField(default=False) migrated = models.BooleanField(default=False) + # uuid = models.CharField(max_length=36, blank=True, null=True) + + @property + def submitter_obj(self): + return retrieve_email_userro(self.submitter) if self.submitter else None + + def create_fee_lines(self): + """ Create the ledger lines - line item for application fee sent to payment system """ + + # Any changes to the DB should be made after the success of payment process + db_processes_after_success = {} + + # if isinstance(instance, Proposal): + # application_type = instance.application_type + # vessel_length = instance.vessel_details.vessel_applicable_length + # proposal_type = instance.proposal_type + # elif isinstance(instance, DcvPermit): + # application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_DCV_PERMIT['code']) + # vessel_length = 1 # any number greater than 0 + # proposal_type = None + application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_DCV_PERMIT['code']) + # vessel_length = 1 # any number greater than 0 + vessel_length = GlobalSettings.default_values[GlobalSettings.KEY_MINIMUM_VESSEL_LENGTH] + 1 + proposal_type = None + + target_datetime = datetime.datetime.now(pytz.timezone(TIME_ZONE)) + target_date = target_datetime.date() + target_datetime_str = target_datetime.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d/%m/%Y %I:%M %p') + + # Retrieve FeeItem object from FeeConstructor object + # if isinstance(instance, Proposal): + # fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_date(application_type, target_date) + # if not fee_constructor: + # # Fees have not been configured for this application type and date + # raise Exception('FeeConstructor object for the ApplicationType: {} not found for the date: {}'.format(application_type, target_date)) + # elif isinstance(instance, DcvPermit): + # fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_season(application_type, instance.fee_season) + # if not fee_constructor: + # # Fees have not been configured for this application type and date + # raise Exception('FeeConstructor object for the ApplicationType: {} and the Season: {}'.format(application_type, instance.fee_season)) + # else: + # raise Exception('Something went wrong when calculating the fee') + fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_season( + application_type, self.fee_season + ) + if not fee_constructor: + # Fees have not been configured for this application type and date + raise Exception( + 'FeeConstructor object for the ApplicationType: {} and the Season: {}'.format( + application_type, self.fee_season + ) + ) + + fee_item = fee_constructor.get_fee_item(vessel_length, proposal_type, target_date) + + db_processes_after_success['fee_item_id'] = fee_item.id + db_processes_after_success['fee_constructor_id'] = fee_constructor.id + db_processes_after_success['season_start_date'] = fee_constructor.fee_season.start_date.__str__() + db_processes_after_success['season_end_date'] = fee_constructor.fee_season.end_date.__str__() + db_processes_after_success['datetime_for_calculating_fee'] = target_datetime.__str__() + + line_items = [ + { + # 'ledger_description': '{} Fee: {} (Season: {} to {}) @{}'.format( + 'ledger_description': '{} Fee: {} @{}'.format( + fee_constructor.application_type.description, + self.lodgement_number, + # fee_constructor.fee_season.start_date.strftime('%d/%m/%Y'), + # fee_constructor.fee_season.end_date.strftime('%d/%m/%Y'), + target_datetime_str, + ), + # 'oracle_code': application_type.oracle_code, + 'oracle_code': ApplicationType.get_current_oracle_code_by_application(application_type.code), + 'price_incl_tax': fee_item.amount, + 'price_excl_tax': ledger_api_client.utils.calculate_excl_gst(fee_item.amount) if fee_constructor.incur_gst else fee_item.amount, + 'quantity': 1, + }, + ] + + logger.info('{}'.format(line_items)) + + return line_items, db_processes_after_success @property def postal_address_line1(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.line1 + if self.submitter_obj.postal_same_as_residential: + ret_value = self.submitter_obj.residential_address.line1 else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.line1 + if self.submitter_obj.postal_address: + ret_value = self.submitter_obj.postal_address.line1 if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.line1 + ret_value = self.submitter_obj.residential_address.line1 return ret_value @property def postal_address_line2(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.line2 + if self.submitter_obj.postal_same_as_residential: + ret_value = self.submitter_obj.residential_address.line2 else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.line2 + if self.submitter_obj.postal_address: + ret_value = self.submitter_obj.postal_address.line2 if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.line2 + ret_value = self.submitter_obj.residential_address.line2 return ret_value @property def postal_address_state(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.state + if self.submitter_obj.postal_same_as_residential: + ret_value = self.submitter_obj.residential_address.state else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.state + if self.submitter_obj.postal_address: + ret_value = self.submitter_obj.postal_address.state if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.state + ret_value = self.submitter_obj.residential_address.state return ret_value @property def postal_address_suburb(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.locality + if self.submitter_obj.postal_same_as_residential: + ret_value = self.submitter_obj.residential_address.locality else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.locality + if self.submitter_obj.postal_address: + ret_value = self.submitter_obj.postal_address.locality if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.locality + ret_value = self.submitter_obj.residential_address.locality return ret_value @property def postal_address_postcode(self): ret_value = '' if self.submitter: - if self.submitter.postal_same_as_residential: - ret_value = self.submitter.residential_address.postcode + if self.submitter_obj.postal_same_as_residential: + ret_value = self.submitter_obj.residential_address.postcode else: - if self.submitter.postal_address: - ret_value = self.submitter.postal_address.postcode + if self.submitter_obj.postal_address: + ret_value = self.submitter_obj.postal_address.postcode if not ret_value: # Shouldn't reach here, but if so, just return residential address - ret_value = self.submitter.residential_address.postcode + ret_value = self.submitter_obj.residential_address.postcode return ret_value def get_context_for_licence_permit(self): @@ -2272,7 +2450,8 @@ def expiry_date(self): @property def fee_paid(self): - if self.invoice and self.invoice.payment_status in ['paid', 'over_paid']: + # if self.invoice and self.invoice.payment_status in ['paid', 'over_paid']: + if self.invoice and get_invoice_payment_status(self.invoice.id) in ['paid', 'over_paid']: return True return False @@ -2315,9 +2494,13 @@ def status(self, target_date=datetime.datetime.now(pytz.timezone(TIME_ZONE)).dat return None def save(self, **kwargs): + logger.info(f"Saving DcvPermit: {self}.") if self.lodgement_number in ['', None]: + logger.info(f'DcvPermit has no lodgement number.') self.lodgement_number = self.LODGEMENT_NUMBER_PREFIX + '{0:06d}'.format(self.get_next_id()) + super(DcvPermit, self).save(**kwargs) + logger.info("DcvPermit Saved.") def generate_dcv_permit_doc(self): permit_document = create_dcv_permit_document(self) @@ -2342,7 +2525,7 @@ def update_dcv_permit_doc_filename(instance, filename): class DcvAdmissionDocument(Document): - dcv_admission = models.ForeignKey(DcvAdmission, related_name='admissions') + dcv_admission = models.ForeignKey(DcvAdmission, related_name='admissions', on_delete=models.PROTECT) _file = models.FileField(upload_to=update_dcv_admission_doc_filename, max_length=512) can_delete = models.BooleanField(default=False) # after initial submit prevent document from being deleted @@ -2356,7 +2539,7 @@ class Meta: class DcvPermitDocument(Document): - dcv_permit = models.ForeignKey(DcvPermit, related_name='permits') + dcv_permit = models.ForeignKey(DcvPermit, related_name='permits', on_delete=models.PROTECT) _file = models.FileField(upload_to=update_dcv_permit_doc_filename, max_length=512) can_delete = models.BooleanField(default=False) # after initial submit prevent document from being deleted @@ -2419,17 +2602,17 @@ class Sticker(models.Model): ] number = models.CharField(max_length=9, blank=True, default='') status = models.CharField(max_length=40, choices=STATUS_CHOICES, default=STATUS_CHOICES[0][0]) - sticker_printing_batch = models.ForeignKey(StickerPrintingBatch, blank=True, null=True) # When None, most probably 'awaiting_ - sticker_printing_response = models.ForeignKey(StickerPrintingResponse, blank=True, null=True) - approval = models.ForeignKey(Approval, blank=True, null=True, related_name='stickers') # Sticker links to either approval or dcv_permit, never to both. - dcv_permit = models.ForeignKey(DcvPermit, blank=True, null=True, related_name='stickers') + sticker_printing_batch = models.ForeignKey(StickerPrintingBatch, blank=True, null=True, on_delete=models.SET_NULL) # When None, most probably 'awaiting_ + sticker_printing_response = models.ForeignKey(StickerPrintingResponse, blank=True, null=True, on_delete=models.SET_NULL) + approval = models.ForeignKey(Approval, blank=True, null=True, related_name='stickers', on_delete=models.SET_NULL) # Sticker links to either approval or dcv_permit, never to both. + dcv_permit = models.ForeignKey(DcvPermit, blank=True, null=True, related_name='stickers', on_delete=models.SET_NULL) printing_date = models.DateField(blank=True, null=True) # The day this sticker printed mailing_date = models.DateField(blank=True, null=True) # The day this sticker sent - fee_constructor = models.ForeignKey('FeeConstructor', blank=True, null=True) - fee_season = models.ForeignKey('FeeSeason', blank=True, null=True) - vessel_ownership = models.ForeignKey('VesselOwnership', blank=True, null=True) - proposal_initiated = models.ForeignKey('Proposal', blank=True, null=True) # This propposal created this sticker object. Can be None when sticker created by RequestNewSticker action or so. - sticker_to_replace = models.ForeignKey('self', null=True, blank=True) # This sticker object replaces the sticker_to_replace for renewal + fee_constructor = models.ForeignKey('FeeConstructor', blank=True, null=True, on_delete=models.SET_NULL) + fee_season = models.ForeignKey('FeeSeason', blank=True, null=True, on_delete=models.SET_NULL) + vessel_ownership = models.ForeignKey('VesselOwnership', blank=True, null=True, on_delete=models.SET_NULL) + proposal_initiated = models.ForeignKey('Proposal', blank=True, null=True, on_delete=models.SET_NULL) # This propposal created this sticker object. Can be None when sticker created by RequestNewSticker action or so. + sticker_to_replace = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL) # This sticker object replaces the sticker_to_replace for renewal class Meta: app_label = 'mooringlicensing' @@ -2590,37 +2773,37 @@ def save(self, *args, **kwargs): @property def first_name(self): if self.approval and self.approval.submitter: - return self.approval.submitter.first_name + return self.approval.submitter_obj.first_name return '---' @property def last_name(self): if self.approval and self.approval.submitter: - return self.approval.submitter.last_name + return self.approval.submitter_obj.last_name return '---' @property def postal_address_line1(self): - if self.approval and self.approval.submitter and self.approval.submitter.postal_address: - return self.approval.submitter.postal_address.line1 + if self.approval and self.approval.submitter and self.approval.submitter_obj.postal_address: + return self.approval.submitter_obj.postal_address.line1 return '---' @property def postal_address_line2(self): - if self.approval and self.approval.submitter and self.approval.submitter.postal_address: - return self.approval.submitter.postal_address.line2 + if self.approval and self.approval.submitter and self.approval.submitter_obj.postal_address: + return self.approval.submitter_obj.postal_address.line2 return '---' @property def postal_address_state(self): - if self.approval and self.approval.submitter and self.approval.submitter.postal_address: - return self.approval.submitter.postal_address.state + if self.approval and self.approval.submitter and self.approval.submitter_obj.postal_address: + return self.approval.submitter_obj.postal_address.state return '---' @property def postal_address_suburb(self): - if self.approval and self.approval.submitter and self.approval.submitter.postal_address: - return self.approval.submitter.postal_address.locality + if self.approval and self.approval.submitter and self.approval.submitter_obj.postal_address: + return self.approval.submitter_obj.postal_address.locality return '---' @property @@ -2637,21 +2820,22 @@ def vessel_applicable_length(self): @property def postal_address_postcode(self): - if self.approval and self.approval.submitter and self.approval.submitter.postal_address: - return self.approval.submitter.postal_address.postcode + if self.approval and self.approval.submitter and self.approval.submitter_obj.postal_address: + return self.approval.submitter_obj.postal_address.postcode return '---' class StickerActionDetail(models.Model): - sticker = models.ForeignKey(Sticker, blank=True, null=True, related_name='sticker_action_details') + sticker = models.ForeignKey(Sticker, blank=True, null=True, related_name='sticker_action_details', on_delete=models.SET_NULL) reason = models.TextField(blank=True) date_created = models.DateTimeField(blank=True, null=True, auto_now_add=True) date_updated = models.DateTimeField(blank=True, null=True, auto_now=True) date_of_lost_sticker = models.DateField(blank=True, null=True) date_of_returned_sticker = models.DateField(blank=True, null=True) action = models.CharField(max_length=50, null=True, blank=True) - user = models.ForeignKey(EmailUser, null=True, blank=True) - sticker_action_fee = models.ForeignKey(StickerActionFee, null=True, blank=True, related_name='sticker_action_details') + # user = models.ForeignKey(EmailUser, null=True, blank=True, on_delete=models.SET_NULL) + user = models.IntegerField(null=True, blank=True) + sticker_action_fee = models.ForeignKey(StickerActionFee, null=True, blank=True, related_name='sticker_action_details', on_delete=models.SET_NULL) class Meta: app_label = 'mooringlicensing' diff --git a/mooringlicensing/components/approvals/serializers.py b/mooringlicensing/components/approvals/serializers.py index 98693f9df..51ae4dadc 100755 --- a/mooringlicensing/components/approvals/serializers.py +++ b/mooringlicensing/components/approvals/serializers.py @@ -1,8 +1,9 @@ import logging from django.conf import settings -from ledger.accounts.models import EmailUser -from ledger.payments.models import Invoice +# from ledger.accounts.models import EmailUser +# from ledger.payments.models import Invoice +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice from django.db.models import Q, Min, Count from mooringlicensing.components.main import serializers @@ -24,34 +25,18 @@ from mooringlicensing.components.organisations.models import ( Organisation ) -from mooringlicensing.components.main.serializers import CommunicationLogEntrySerializer, InvoiceSerializer +from mooringlicensing.components.main.serializers import CommunicationLogEntrySerializer, InvoiceSerializer, \ + EmailUserSerializer from mooringlicensing.components.proposals.serializers import InternalProposalSerializer, \ MooringSimpleSerializer # EmailUserAppViewSerializer from mooringlicensing.components.users.serializers import UserSerializer from rest_framework import serializers from django.core.exceptions import ObjectDoesNotExist +from mooringlicensing.ledger_api_utils import retrieve_email_userro -logger = logging.getLogger('mooringlicensing') - - -class EmailUserSerializer(serializers.ModelSerializer): - full_name = serializers.SerializerMethodField() - - class Meta: - model = EmailUser - fields = ( - 'id', - 'email', - 'first_name', - 'last_name', - 'title', - 'organisation', - 'full_name', - ) - - def get_full_name(self, obj): - return obj.get_full_name() +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class ApprovalPaymentSerializer(serializers.ModelSerializer): @@ -299,7 +284,8 @@ class Meta: class ApprovalSerializer(serializers.ModelSerializer): - submitter = UserSerializer() + # submitter = UserSerializer() + submitter = serializers.SerializerMethodField() current_proposal = InternalProposalSerializer() licence_document = serializers.CharField(source='licence_document._file.url') renewal_document = serializers.SerializerMethodField(read_only=True) @@ -381,6 +367,10 @@ class Meta: 'is_approver', ) + def get_submitter(self, obj): + serializer = UserSerializer(obj.submitter_obj) + return serializer.data + def get_mooring_licence_mooring(self, obj): if type(obj.child_obj) == MooringLicence: return obj.child_obj.mooring.name @@ -443,9 +433,9 @@ def get_mooring_licence_authorised_users(self, obj): approval.current_proposal.vessel_details.vessel.latest_vessel_details.vessel_name if approval.current_proposal.vessel_details else '' ), - "holder": approval.submitter.get_full_name(), - "mobile": approval.submitter.mobile_number, - "email": approval.submitter.email, + "holder": approval.submitter_obj.get_full_name(), + "mobile": approval.submitter_obj.mobile_number, + "email": approval.submitter_obj.email, "status": approval.get_status_display(), }) return authorised_users @@ -482,17 +472,18 @@ def get_authorised_user_moorings_detail(self, obj): moorings = [] if type(obj.child_obj) == AuthorisedUserPermit: for moa in obj.mooringonapproval_set.filter(end_date__isnull=True): - if moa.mooring.mooring_licence: + #import ipdb; ipdb.set_trace() + if moa.mooring.mooring_licence is not None: licence_holder_data = UserSerializer(moa.mooring.mooring_licence.submitter).data - moorings.append({ - "id": moa.id, - "mooring_name": moa.mooring.name, - "sticker": moa.sticker.number if moa.sticker else '', - "licensee": licence_holder_data.get('full_name') if licence_holder_data else '', - 'allocated_by': 'Site Licensee' if moa.site_licensee else 'RIA', - "mobile": licence_holder_data.get('mobile_number') if licence_holder_data else '', - "email": licence_holder_data.get('email') if licence_holder_data else '', - }) + moorings.append({ + "id": moa.id, + "mooring_name": moa.mooring.name, + "sticker": moa.sticker.number if moa.sticker else '', + "licensee": licence_holder_data.get('full_name') if licence_holder_data else '', + 'allocated_by': 'Site Licensee' if moa.site_licensee else 'RIA', + "mobile": licence_holder_data.get('mobile_number') if licence_holder_data else '', + "email": licence_holder_data.get('email') if licence_holder_data else '', + }) return moorings def get_authorised_user_moorings(self, obj): @@ -622,7 +613,7 @@ def get_approval_type_dict(self, obj): def get_holder(self, obj): submitter = '' if obj.submitter: - submitter = obj.submitter.get_full_name() + submitter = obj.submitter_obj.get_full_name() return submitter def get_issue_date_str(self, obj): @@ -639,9 +630,10 @@ def get_expiry_date_str(self, obj): class ListApprovalSerializer(serializers.ModelSerializer): - licence_document = serializers.CharField(source='licence_document._file.url') - # licence_document = serializers.SerializerMethodField() - authorised_user_summary_document = serializers.CharField(source='authorised_user_summary_document._file.url') + # licence_document = serializers.CharField(source='licence_document._file.url') + licence_document = serializers.SerializerMethodField() + # authorised_user_summary_document = serializers.CharField(source='authorised_user_summary_document._file.url') + authorised_user_summary_document = serializers.SerializerMethodField() renewal_document = serializers.SerializerMethodField(read_only=True) status = serializers.SerializerMethodField() internal_status = serializers.SerializerMethodField() @@ -752,6 +744,17 @@ class Meta: 'vessel_regos', ) + def get_licence_document(self, obj): + if obj.licence_document and obj.licence_document._file: + return obj.licence_document._file.url + return 'no-licence-document-found' + + def get_authorised_user_summary_document(self, obj): + if obj.authorised_user_summary_document: + return obj.authorised_user_summary_document._file.url + else: + return '' + def get_allowed_assessors_user(self, obj): request = self.context.get('request') if request: @@ -951,11 +954,22 @@ def get_approval_type_dict(self, obj): def get_holder(self, obj): holder_str = '' if obj.submitter: - holder_str = '{}
{}
{}
'.format( - obj.submitter.get_full_name(), - obj.submitter.mobile_number, - obj.submitter.email - ) + items = [] + from mooringlicensing.ledger_api_utils import retrieve_email_userro + # items.append(obj.submitter.get_full_name()) + # submitter = retrieve_email_userro(obj.submitter) + items.append(obj.submitter_obj.get_full_name()) + # if obj.submitter.mobile_number: + if obj.submitter_obj.mobile_number: + items.append(' ' + obj.submitter_obj.mobile_number) + # if obj.submitter.phone_number: + if obj.submitter_obj.phone_number: + items.append(' ' + obj.submitter_obj.phone_number) + # items.append(obj.submitter.email) + items.append(obj.submitter_obj.email) + + items = '
'.join(items) + holder_str = '' + items + '' return holder_str def get_issue_date_str(self, obj): @@ -1020,7 +1034,7 @@ def get_approval_type_dict(self, obj): raise def get_submitter_phone_number(self, obj): - return obj.submitter.mobile_number if obj.submitter.mobile_number else obj.submitter.phone_number + return obj.submitter_obj.mobile_number if obj.submitter_obj.mobile_number else obj.submitter_obj.phone_number def get_vessel_data(self, obj): vessel_data = [] @@ -1061,7 +1075,8 @@ class StickerActionDetailSerializer(serializers.ModelSerializer): date_of_returned_sticker = serializers.DateField(input_formats=['%d/%m/%Y'], required=False, allow_null=True) date_created = serializers.DateTimeField(read_only=True) date_updated = serializers.DateTimeField(read_only=True) - user_detail = EmailUserSerializer(source='user', read_only=True) + # user_detail = EmailUserSerializer(source='user', read_only=True) + user_detail = serializers.SerializerMethodField() class Meta: model = StickerActionDetail @@ -1078,6 +1093,10 @@ class Meta: 'user_detail', # For reading the user data ) + def get_user_detail(self, obj): + serializer = EmailUserSerializer(retrieve_email_userro(obj.user)) + return serializer.data + class StickerForDcvSaveSerializer(serializers.ModelSerializer): @@ -1263,7 +1282,9 @@ def get_invoices(self, obj): return serializer.data def get_fee_invoice_url(self, obj): - url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = get_invoice_url(obj.invoice.reference) if obj.invoice else '' + url = f'/ledger-toolkit-api/invoice-pdf/{obj.invoice.reference}/' if obj.invoice else '' return url def get_dcv_organisation_name(self, obj): @@ -1271,7 +1292,7 @@ def get_dcv_organisation_name(self, obj): if obj.dcv_organisation: return obj.dcv_organisation.name else: - return obj.submitter.get_full_name() + ' (P)' + return obj.submitter_obj.get_full_name() + ' (P)' except: return '' @@ -1332,7 +1353,9 @@ def get_invoices(self, obj): return serializer.data def get_fee_invoice_url(self, obj): - url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = get_invoice_url(obj.invoice.reference) if obj.invoice else '' + url = f'/ledger-toolkit-api/invoice-pdf/{obj.invoice.reference}/' if obj.invoice else '' return url def get_lodgement_date(self, obj): @@ -1384,7 +1407,7 @@ def get_approval_status(self, obj): return obj.approval.get_status_display() def get_holder(self, obj): - return obj.approval.submitter.get_full_name() + return obj.approval.submitter_obj.get_full_name() def get_sticker_numbers(self, obj): numbers = "" diff --git a/mooringlicensing/components/approvals/signals.py b/mooringlicensing/components/approvals/signals.py index 0f05fe768..10db41401 100644 --- a/mooringlicensing/components/approvals/signals.py +++ b/mooringlicensing/components/approvals/signals.py @@ -5,7 +5,8 @@ from mooringlicensing.components.approvals.models import Sticker, ApprovalHistory from mooringlicensing.components.proposals.models import Proposal -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class StickerListener(object): diff --git a/mooringlicensing/components/compliances/api.py b/mooringlicensing/components/compliances/api.py index cee18a952..82755f83b 100755 --- a/mooringlicensing/components/compliances/api.py +++ b/mooringlicensing/components/compliances/api.py @@ -1,32 +1,37 @@ import traceback -import os -import datetime -import base64 -import geojson +# import os +# import datetime +# import base64 +# import geojson from rest_framework_datatables.filters import DatatablesFilterBackend from rest_framework_datatables.renderers import DatatablesRenderer -from wsgiref.util import FileWrapper +# from wsgiref.util import FileWrapper from django.db.models import Q, Min from django.db import transaction -from django.http import HttpResponse -from django.core.files.base import ContentFile +# from django.http import HttpResponse +# from django.core.files.base import ContentFile from django.core.exceptions import ValidationError from django.conf import settings -from django.contrib import messages -from django.utils import timezone -from rest_framework import viewsets, serializers, status, generics, views -from rest_framework.decorators import detail_route, list_route, renderer_classes +# from django.contrib import messages +# from django.utils import timezone +from rest_framework import viewsets, serializers, views +# from rest_framework.decorators import detail_route, list_route, renderer_classes +from rest_framework.decorators import action as detail_route +from rest_framework.decorators import action as list_route +from rest_framework.decorators import renderer_classes from rest_framework.response import Response from rest_framework.renderers import JSONRenderer -from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission -from rest_framework.pagination import PageNumberPagination -from datetime import datetime, timedelta -from collections import OrderedDict +# from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission +# from rest_framework.pagination import PageNumberPagination +# from datetime import datetime, timedelta +# from collections import OrderedDict from django.core.cache import cache -from ledger.accounts.models import EmailUser, Address -from ledger.address.models import Country -from datetime import datetime, timedelta, date +# from ledger.accounts.models import EmailUser, Address +from ledger_api_client.ledger_models import EmailUserRO as EmailUser + +# from ledger.address.models import Country +# from datetime import datetime, timedelta, date from mooringlicensing.components.compliances.models import ( Compliance, ComplianceAmendmentRequest, @@ -70,13 +75,13 @@ def list(self, request, *args, **kwargs): serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def internal_compliance(self, request, *args, **kwargs): instance = self.get_object() serializer = InternalComplianceSerializer(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def submit(self, request, *args, **kwargs): try: @@ -123,7 +128,7 @@ def submit(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def assign_request_user(self, request, *args, **kwargs): try: instance = self.get_object() @@ -140,7 +145,7 @@ def assign_request_user(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def delete_document(self, request, *args, **kwargs): try: instance = self.get_object() @@ -159,7 +164,7 @@ def delete_document(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def assign_to(self, request, *args, **kwargs): try: instance = self.get_object() @@ -184,7 +189,7 @@ def assign_to(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def unassign(self, request, *args, **kwargs): try: instance = self.get_object() @@ -201,7 +206,7 @@ def unassign(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def accept(self, request, *args, **kwargs): try: instance = self.get_object() @@ -218,7 +223,7 @@ def accept(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def amendment_request(self, request, *args, **kwargs): try: instance = self.get_object() @@ -236,7 +241,7 @@ def amendment_request(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def action_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -253,7 +258,7 @@ def action_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def comms_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -270,7 +275,7 @@ def comms_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def add_comms_log(self, request, *args, **kwargs): try: @@ -365,9 +370,11 @@ def filter_queryset(self, request, queryset, view): if filter_compliance_status and not filter_compliance_status.lower() == 'all': queryset = queryset.filter(customer_status=filter_compliance_status) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -409,11 +416,11 @@ def get_queryset(self): else: qs = Compliance.objects.all() elif is_customer(self.request): - qs = Compliance.objects.filter(Q(approval__submitter=request_user)).exclude(processing_status="discarded") + qs = Compliance.objects.filter(Q(approval__submitter=request_user.id)).exclude(processing_status="discarded") return qs - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_external(self, request, *args, **kwargs): """ User is accessing /external/ page diff --git a/mooringlicensing/components/compliances/email.py b/mooringlicensing/components/compliances/email.py index d91305960..51ae1938e 100755 --- a/mooringlicensing/components/compliances/email.py +++ b/mooringlicensing/components/compliances/email.py @@ -2,15 +2,21 @@ from django.core.mail import EmailMultiAlternatives, EmailMessage from django.utils.encoding import smart_text -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse +from django.urls import reverse from django.conf import settings from mooringlicensing.components.emails.emails import TemplateEmailBase -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO as EmailUser from django.core.files.storage import default_storage from django.core.files.base import ContentFile from mooringlicensing.components.emails.utils import get_public_url, make_http_https +from mooringlicensing.components.users.utils import _log_user_email + +# from mooringlicensing.components.main.models import EmailUserLogEntry +# from mooringlicensing.components.main.utils import _log_user_email logger = logging.getLogger(__name__) @@ -81,7 +87,7 @@ def send_amendment_email_notification(amendment_request, request, compliance, is 'public_url': get_public_url(request), } - submitter = compliance.submitter.email if compliance.submitter and compliance.submitter.email else compliance.proposal.submitter.email + submitter = compliance.submitter_obj.email if compliance.submitter_obj and compliance.submitter_obj.email else compliance.proposal.submitter_obj.email all_ccs = [] if compliance.proposal.org_applicant and compliance.proposal.org_applicant.email: cc_list = compliance.proposal.org_applicant.email @@ -96,7 +102,7 @@ def send_amendment_email_notification(amendment_request, request, compliance, is if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) #send reminder emails if Compliance has not been lodged by due date. Used in Cron job so cannot use 'request' parameter @@ -114,7 +120,7 @@ def send_reminder_email_notification(compliance, is_test=False): 'public_url': get_public_url(), } - submitter = compliance.submitter.email if compliance.submitter and compliance.submitter.email else compliance.proposal.submitter.email + submitter = compliance.submitter_obj.email if compliance.submitter_obj and compliance.submitter_obj.email else compliance.proposal.submitter_obj.email all_ccs = [] if compliance.proposal.org_applicant and compliance.proposal.org_applicant.email: cc_list = compliance.proposal.org_applicant.email @@ -133,7 +139,7 @@ def send_reminder_email_notification(compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender_user) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_internal_reminder_email_notification(compliance, is_test=False): @@ -162,7 +168,7 @@ def send_internal_reminder_email_notification(compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender_user) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_due_email_notification(compliance, is_test=False): @@ -170,7 +176,7 @@ def send_due_email_notification(compliance, is_test=False): url = settings.SITE_URL url += reverse('external-compliance-detail', kwargs={'compliance_pk': compliance.id}) - submitter = compliance.submitter if compliance.submitter and compliance.submitter.email else compliance.proposal.submitter + submitter = compliance.submitter_obj if compliance.submitter_obj and compliance.submitter_obj.email else compliance.proposal.submitter_obj context = { 'recipient': submitter, @@ -198,7 +204,7 @@ def send_due_email_notification(compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender_user) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_internal_due_email_notification(compliance, is_test=False): @@ -228,13 +234,13 @@ def send_internal_due_email_notification(compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender_user) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_compliance_accept_email_notification(compliance,request, is_test=False): email = ComplianceAcceptNotificationEmail() - submitter = compliance.submitter if compliance.submitter else compliance.proposal.submitter + submitter = compliance.submitter_obj if compliance.submitter_obj else compliance.proposal.submitter_obj context = { 'compliance': compliance, 'public_url': get_public_url(request), @@ -254,14 +260,14 @@ def send_compliance_accept_email_notification(compliance,request, is_test=False) if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_external_submit_email_notification(request, compliance, is_test=False): email = ComplianceExternalSubmitSendNotificationEmail() url = request.build_absolute_uri(reverse('external-compliance-detail',kwargs={'compliance_pk': compliance.id})) url = ''.join(url.split('-internal')) - submitter = compliance.submitter if compliance.submitter and compliance.submitter.email else compliance.proposal.submitter + submitter = compliance.submitter_obj if compliance.submitter_obj and compliance.submitter_obj.email else compliance.proposal.submitter_obj context = { 'compliance': compliance, @@ -284,7 +290,7 @@ def send_external_submit_email_notification(request, compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def send_submit_email_notification(request, compliance, is_test=False): @@ -309,7 +315,7 @@ def send_submit_email_notification(request, compliance, is_test=False): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) def _log_compliance_email(email_message, compliance, sender=None): @@ -335,7 +341,7 @@ def _log_compliance_email(email_message, compliance, sender=None): else: text = smart_text(email_message) subject = '' - to = compliance.submitter.email + to = compliance.submitter_obj.email fromm = smart_text(sender) if sender else SYSTEM_NAME all_ccs = '' @@ -407,55 +413,3 @@ def _log_org_email(email_message, organisation, customer ,sender=None): email_entry = OrganisationLogEntry.objects.create(**kwargs) return email_entry - - -def _log_user_email(email_message, target_email_user, customer, sender=None, attachments=[]): - from ledger.accounts.models import EmailUserLogEntry - if isinstance(email_message, (EmailMultiAlternatives, EmailMessage,)): - # TODO this will log the plain text body, should we log the html instead - text = email_message.body - subject = email_message.subject - fromm = smart_text(sender) if sender else smart_text(email_message.from_email) - # the to email is normally a list - if isinstance(email_message.to, list): - to = ','.join(email_message.to) - else: - to = smart_text(email_message.to) - # we log the cc and bcc in the same cc field of the log entry as a ',' comma separated string - all_ccs = [] - if email_message.cc: - all_ccs += list(email_message.cc) - if email_message.bcc: - all_ccs += list(email_message.bcc) - all_ccs = ','.join(all_ccs) - - else: - text = smart_text(email_message) - subject = '' - to = customer - fromm = smart_text(sender) if sender else SYSTEM_NAME - all_ccs = '' - - customer = customer - - staff = sender - - kwargs = { - 'subject': subject, - 'text': text, - 'emailuser': target_email_user if target_email_user else customer, - 'customer': customer, - 'staff': staff, - 'to': to, - 'fromm': fromm, - 'cc': all_ccs - } - - email_entry = EmailUserLogEntry.objects.create(**kwargs) - - for attachment in attachments: - path_to_file = '{}/emailuser/{}/communications/{}'.format(settings.MEDIA_APP_DIR, target_email_user.id, attachment[0]) - path = default_storage.save(path_to_file, ContentFile(attachment[1])) - email_entry.documents.get_or_create(_file=path_to_file, name=attachment[0]) - - return email_entry diff --git a/mooringlicensing/components/compliances/models.py b/mooringlicensing/components/compliances/models.py index b60f18dcd..2f358e2ea 100755 --- a/mooringlicensing/components/compliances/models.py +++ b/mooringlicensing/components/compliances/models.py @@ -1,28 +1,16 @@ from __future__ import unicode_literals -import json -import datetime from django.db import models,transaction -from django.dispatch import receiver -from django.db.models.signals import pre_delete -from django.utils.encoding import python_2_unicode_compatible from django.core.exceptions import ValidationError -from django.contrib.postgres.fields.jsonb import JSONField from django.utils import timezone -from django.contrib.sites.models import Site from django.conf import settings -from taggit.managers import TaggableManager -from taggit.models import TaggedItemBase -from ledger.accounts.models import Organisation as ledger_organisation -from ledger.accounts.models import EmailUser, RevisionedMixin -from ledger.licence.models import Licence -from mooringlicensing import exceptions -from mooringlicensing.components.organisations.models import Organisation +# from ledger.accounts.models import EmailUser, RevisionedMixin +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice from mooringlicensing.components.main.models import ( CommunicationsLogEntry, # Region, UserAction, - Document, NumberOfDaysSetting, NumberOfDaysType + Document, RevisionedMixin ) from mooringlicensing.components.proposals.models import ProposalRequirement, AmendmentReason from mooringlicensing.components.compliances.email import ( @@ -35,10 +23,11 @@ send_due_email_notification, send_internal_due_email_notification ) -from ledger.payments.invoice.models import Invoice +# from ledger.payments.invoice.models import Invoice import logging +from mooringlicensing.ledger_api_utils import retrieve_email_userro from mooringlicensing.settings import CODE_DAYS_BEFORE_DUE_COMPLIANCE logger = logging.getLogger(__name__) @@ -74,17 +63,19 @@ class Compliance(RevisionedMixin): ) lodgement_number = models.CharField(max_length=9, blank=True, default='') - proposal = models.ForeignKey('mooringlicensing.Proposal',related_name='compliances') - approval = models.ForeignKey('mooringlicensing.Approval',related_name='compliances') + proposal = models.ForeignKey('mooringlicensing.Proposal', related_name='compliances', on_delete=models.PROTECT) + approval = models.ForeignKey('mooringlicensing.Approval', related_name='compliances', on_delete=models.PROTECT) due_date = models.DateField() text = models.TextField(blank=True) num_participants = models.SmallIntegerField('Number of participants', blank=True, null=True) processing_status = models.CharField(choices=PROCESSING_STATUS_CHOICES,max_length=20) customer_status = models.CharField(choices=CUSTOMER_STATUS_CHOICES,max_length=20) - assigned_to = models.ForeignKey(EmailUser,related_name='mooringlicensing_compliance_assignments',null=True,blank=True) + # assigned_to = models.ForeignKey(EmailUser, related_name='mooringlicensing_compliance_assignments', null=True, blank=True, on_delete=models.SET_NULL) + assigned_to = models.IntegerField( null=True, blank=True) requirement = models.ForeignKey(ProposalRequirement, blank=True, null=True, related_name='compliance_requirement', on_delete=models.SET_NULL) lodgement_date = models.DateTimeField(blank=True, null=True) - submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_compliances') + # submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_compliances', on_delete=models.SET_NULL) + submitter = models.IntegerField(blank=True, null=True) reminder_sent = models.BooleanField(default=False) post_reminder_sent = models.BooleanField(default=False) fee_invoice_reference = models.CharField(max_length=50, null=True, blank=True, default='') @@ -92,6 +83,10 @@ class Compliance(RevisionedMixin): class Meta: app_label = 'mooringlicensing' + @property + def submitter_obj(self): + return retrieve_email_userro(self.submitter) if self.submitter else None + @property def regions(self): return self.proposal.regions_list @@ -258,7 +253,7 @@ def update_proposal_complaince_filename(instance, filename): class ComplianceDocument(Document): - compliance = models.ForeignKey('Compliance',related_name='documents') + compliance = models.ForeignKey('Compliance', related_name='documents', on_delete=models.CASCADE) _file = models.FileField(upload_to=update_proposal_complaince_filename, max_length=512) can_delete = models.BooleanField(default=True) # after initial submit prevent document from being deleted @@ -270,6 +265,7 @@ def delete(self): class Meta: app_label = 'mooringlicensing' + class ComplianceUserAction(UserAction): ACTION_CREATE = "Create compliance {}" ACTION_SUBMIT_REQUEST = "Submit compliance {}" @@ -280,25 +276,24 @@ class ComplianceUserAction(UserAction): ACTION_REMINDER_SENT = "Reminder sent for compliance {}" ACTION_STATUS_CHANGE = "Change status to Due for compliance {}" # Assessors - - ACTION_CONCLUDE_REQUEST = "Conclude request {}" @classmethod def log_action(cls, compliance, action, user): return cls.objects.create( compliance=compliance, - who=user, + who=user.id if user else None, what=str(action) ) - compliance = models.ForeignKey(Compliance,related_name='action_logs') + compliance = models.ForeignKey(Compliance, related_name='action_logs', on_delete=models.CASCADE) class Meta: app_label = 'mooringlicensing' + class ComplianceLogEntry(CommunicationsLogEntry): - compliance = models.ForeignKey(Compliance, related_name='comms_logs') + compliance = models.ForeignKey(Compliance, related_name='comms_logs', on_delete=models.CASCADE) def save(self, **kwargs): # save the request id if the reference not provided @@ -309,26 +304,30 @@ def save(self, **kwargs): class Meta: app_label = 'mooringlicensing' + def update_compliance_comms_log_filename(instance, filename): return '{}/proposals/{}/compliance/communications/{}'.format(settings.MEDIA_APP_DIR, instance.log_entry.compliance.proposal.id,filename) class ComplianceLogDocument(Document): - log_entry = models.ForeignKey('ComplianceLogEntry',related_name='documents') + log_entry = models.ForeignKey('ComplianceLogEntry', related_name='documents', on_delete=models.PROTECT) _file = models.FileField(upload_to=update_compliance_comms_log_filename, max_length=512) class Meta: app_label = 'mooringlicensing' + class CompRequest(models.Model): - compliance = models.ForeignKey(Compliance) + compliance = models.ForeignKey(Compliance, on_delete=models.PROTECT) subject = models.CharField(max_length=200, blank=True) text = models.TextField(blank=True) - officer = models.ForeignKey(EmailUser, null=True) + # officer = models.ForeignKey(EmailUser, null=True, on_delete=models.SET_NULL) + officer = models.IntegerField(null=True, blank=True) class Meta: app_label = 'mooringlicensing' + class ComplianceAmendmentReason(models.Model): reason = models.CharField('Reason', max_length=125) @@ -343,7 +342,7 @@ class ComplianceAmendmentRequest(CompRequest): STATUS_CHOICES = (('requested', 'Requested'), ('amended', 'Amended')) status = models.CharField('Status', max_length=30, choices=STATUS_CHOICES, default=STATUS_CHOICES[0][0]) - reason = models.ForeignKey(ComplianceAmendmentReason, blank=True, null=True) + reason = models.ForeignKey(ComplianceAmendmentReason, blank=True, null=True, on_delete=models.SET_NULL) class Meta: app_label = 'mooringlicensing' @@ -360,7 +359,7 @@ def generate_amendment(self,request): compliance.log_user_action(ComplianceUserAction.ACTION_ID_REQUEST_AMENDMENTS,request) # Create a log entry for the organisation applicant_field=getattr(compliance.proposal, compliance.proposal.applicant_field) - applicant_field.log_user_action(ComplianceUserAction.ACTION_ID_REQUEST_AMENDMENTS,request) + # applicant_field.log_user_action(ComplianceUserAction.ACTION_ID_REQUEST_AMENDMENTS,request) send_amendment_email_notification(self,request, compliance) diff --git a/mooringlicensing/components/compliances/serializers.py b/mooringlicensing/components/compliances/serializers.py index eb1cc02bb..2f937c65a 100755 --- a/mooringlicensing/components/compliances/serializers.py +++ b/mooringlicensing/components/compliances/serializers.py @@ -1,16 +1,15 @@ -from django.utils import timezone -from ledger.accounts.models import EmailUser,Address +# from django.utils import timezone +# from ledger.accounts.models import EmailUser,Address +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Address from mooringlicensing.components.compliances.models import ( Compliance, ComplianceUserAction, ComplianceLogEntry, ComplianceAmendmentRequest, ComplianceAmendmentReason ) +from mooringlicensing.components.main.serializers import EmailUserSerializer from mooringlicensing.components.proposals.serializers import ProposalRequirementSerializer from rest_framework import serializers +from mooringlicensing.ledger_api_utils import retrieve_email_userro -class EmailUserSerializer(serializers.ModelSerializer): - class Meta: - model = EmailUser - fields = ('id','email','first_name','last_name','title','organisation') class ComplianceSerializer(serializers.ModelSerializer): title = serializers.CharField(source='proposal.title') @@ -76,7 +75,7 @@ def get_assigned_to(self,obj): def get_submitter(self,obj): if obj.submitter: - return obj.submitter.get_full_name() + return obj.submitter_obj.get_full_name() return None def get_application_type_code(self, obj): @@ -150,7 +149,7 @@ def get_approval_lodgement_number(self,obj): def get_submitter(self,obj): if obj.submitter: - return obj.submitter.get_full_name() + return obj.submitter_obj.get_full_name() return None def get_processing_status(self, obj): @@ -171,11 +170,16 @@ class Meta: ) class ComplianceActionSerializer(serializers.ModelSerializer): - who = serializers.CharField(source='who.get_full_name') + # who = serializers.CharField(source='who.get_full_name') + who = serializers.SerializerMethodField() class Meta: model = ComplianceUserAction fields = '__all__' + def get_who(self, obj): + who_obj = retrieve_email_userro(obj.who) + return who_obj.get_full_name() + class ComplianceCommsSerializer(serializers.ModelSerializer): documents = serializers.SerializerMethodField() class Meta: @@ -262,7 +266,7 @@ def get_approval_type(self, obj): return obj.approval.child_obj.description def get_approval_submitter(self, obj): - return obj.approval.submitter.get_full_name() + return obj.approval.submitter_obj.get_full_name() def get_assigned_to_name(self, obj): assigned_to = '' diff --git a/mooringlicensing/components/emails/emails.py b/mooringlicensing/components/emails/emails.py index cc825766b..5dc5b2fbf 100755 --- a/mooringlicensing/components/emails/emails.py +++ b/mooringlicensing/components/emails/emails.py @@ -5,17 +5,18 @@ import six from django.conf import settings from django.core.mail import EmailMultiAlternatives, EmailMessage -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse from django.template import loader, Template from django.utils.encoding import smart_text from django.utils.html import strip_tags from confy import env -from ledger.accounts.models import Document +from ledger_api_client.ledger_models import Document from mooringlicensing.settings import SYSTEM_NAME -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) def _render(template, context): @@ -26,8 +27,8 @@ def _render(template, context): return template.render(context) -def host_reverse(name, args=None, kwargs=None): - return "{}{}".format(settings.DEFAULT_HOST, reverse(name, args=args, kwargs=kwargs)) +# def host_reverse(name, args=None, kwargs=None): +# return "{}{}".format(settings.DEFAULT_HOST, reverse(name, args=args, kwargs=kwargs)) class TemplateEmailBase(object): diff --git a/mooringlicensing/components/emails/utils.py b/mooringlicensing/components/emails/utils.py index e40b7f131..f4854f448 100644 --- a/mooringlicensing/components/emails/utils.py +++ b/mooringlicensing/components/emails/utils.py @@ -1,5 +1,6 @@ from django.conf import settings -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO as EmailUser def get_user_as_email_user(sender): @@ -20,6 +21,11 @@ def make_url_for_internal(url): else: url = url.replace('.dbca.wa.gov.au', '-internal.dbca.wa.gov.au') + # For seg-dev environment + if '-ria-seg-dev' not in url: + url = url.replace('-seg-dev', '-ria-seg-dev') + url = url.replace('-internal', '-internal-oim01') + url = make_http_https(url) return url @@ -27,13 +33,18 @@ def make_url_for_internal(url): def make_url_for_external(url): # Public URL should not have 'internal' substring if '-dev-internal' in url: - web_url = url.replace('-dev-internal', '-dev') + url = url.replace('-dev-internal', '-dev') elif '-uat-internal' in url: - web_url = url.replace('-uat-internal', '-uat') + url = url.replace('-uat-internal', '-uat') else: - web_url = url.replace('-internal', '') + url = url.replace('-internal', '') - web_url = make_http_https(web_url) + # For seg-dev environment + if '-ria-seg-dev' in url: + url = url.replace('-ria-seg-dev', '-seg-dev') + url = url.replace('-oim01', '') + + web_url = make_http_https(url) return web_url diff --git a/mooringlicensing/components/main/api.py b/mooringlicensing/components/main/api.py index 244e5a358..16b170f48 100755 --- a/mooringlicensing/components/main/api.py +++ b/mooringlicensing/components/main/api.py @@ -1,16 +1,14 @@ import traceback from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse -from ledger.payments.utils import oracle_parser_on_invoice -from django.conf import settings +# from ledger.payments.utils import oracle_parser_on_invoice from django.db import transaction from wsgiref.util import FileWrapper from rest_framework import viewsets, serializers, status, generics, views -from rest_framework.decorators import detail_route, list_route, renderer_classes, parser_classes +# from rest_framework.decorators import detail_route, list_route, renderer_classes, parser_classes +from rest_framework.decorators import action as detail_route +from rest_framework.decorators import renderer_classes from rest_framework.response import Response from rest_framework.renderers import JSONRenderer -from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission -from rest_framework.pagination import PageNumberPagination -from django.urls import reverse from mooringlicensing.components.main.models import ( GlobalSettings, TemporaryDocumentCollection, @@ -23,21 +21,22 @@ ) from mooringlicensing.components.main.process_document import save_document, cancel_document, delete_document from django.core.exceptions import ValidationError -from django.db.models import Q +# from django.db.models import Q from mooringlicensing.components.payments_ml import reports from mooringlicensing.components.proposals.models import Proposal from mooringlicensing.components.proposals.serializers import ProposalSerializer -from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket -from collections import namedtuple -import json -from decimal import Decimal +# from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket +# from collections import namedtuple +# import json +# from decimal import Decimal import logging -from mooringlicensing.settings import PAYMENT_SYSTEM_PREFIX, SYSTEM_NAME +from mooringlicensing.settings import LEDGER_SYSTEM_ID, SYSTEM_NAME -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class GlobalSettingsViewSet(viewsets.ReadOnlyModelViewSet): @@ -87,8 +86,22 @@ def get(self,request,format=None): def oracle_integration(date, override): - system = PAYMENT_SYSTEM_PREFIX - oracle_codes = oracle_parser_on_invoice(date, system, SYSTEM_NAME, override=override) + system = LEDGER_SYSTEM_ID + + # TODO: implement oracle_parser_on_invoice + # oracle_codes = oracle_parser_on_invoice(date, system, SYSTEM_NAME, override=override) + +class GetExternalDashboardSectionsList(views.APIView): + """ + Return the section's name list for the external dashboard + """ + renderer_classes = [JSONRenderer, ] + + def get(self, request, format=None): + # data = ['LicencesAndPermitsTable', 'ApplicationsTable', 'CompliancesTable', 'WaitingListTable', 'AuthorisedUserApplicationsTable',] + data = GlobalSettings.objects.get(key=GlobalSettings.KEY_EXTERNAL_DASHBOARD_SECTIONS_LIST).value + data = [item.strip() for item in data.split(",")] + return Response(data) class OracleJob(views.APIView): @@ -144,7 +157,7 @@ def create(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) def process_temp_document(self, request, *args, **kwargs): print("process_temp_document") diff --git a/mooringlicensing/components/main/decorators.py b/mooringlicensing/components/main/decorators.py index 383233da9..da61fe67c 100644 --- a/mooringlicensing/components/main/decorators.py +++ b/mooringlicensing/components/main/decorators.py @@ -9,11 +9,13 @@ from rest_framework import serializers from rest_framework.request import Request import logging +from functools import wraps logger = logging.getLogger() def basic_exception_handler(func): + @wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) diff --git a/mooringlicensing/components/main/models.py b/mooringlicensing/components/main/models.py index 24a62a4b9..88f368c18 100755 --- a/mooringlicensing/components/main/models.py +++ b/mooringlicensing/components/main/models.py @@ -2,28 +2,30 @@ import pytz import os -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE from django.db import models -from django.utils.encoding import python_2_unicode_compatible +# from django.utils.encoding import python_2_unicode_compatible from django.core.exceptions import ValidationError -from ledger.accounts.models import EmailUser, RevisionedMixin +# from ledger.accounts.models import EmailUser, RevisionedMixin from datetime import datetime from django.dispatch import receiver -from django.db.models.signals import post_save, post_delete, pre_delete +from django.db.models.signals import post_save, post_delete from mooringlicensing import settings class UserSystemSettings(models.Model): - user = models.OneToOneField(EmailUser, related_name='system_settings') + # user = models.OneToOneField(EmailUser, related_name='system_settings') + user = models.IntegerField(unique=True) # EmailUserRO class Meta: app_label = 'mooringlicensing' verbose_name_plural = "User System Settings" -@python_2_unicode_compatible +# @python_2_unicode_compatible class UserAction(models.Model): - who = models.ForeignKey(EmailUser, null=False, blank=False) + # who = models.ForeignKey(EmailUser, null=False, blank=False) + who = models.IntegerField(null=True, blank=True) # EmailUserRO when = models.DateTimeField(null=False, blank=False, auto_now_add=True) what = models.TextField(blank=False) @@ -62,8 +64,10 @@ class CommunicationsLogEntry(models.Model): subject = models.CharField(max_length=200, blank=True, verbose_name="Subject / Description") text = models.TextField(blank=True) - customer = models.ForeignKey(EmailUser, null=True, related_name='+') - staff = models.ForeignKey(EmailUser, null=True, related_name='+') + # customer = models.ForeignKey(EmailUser, null=True, related_name='+') + customer = models.IntegerField(null=True) # EmailUserRO + # staff = models.ForeignKey(EmailUser, null=True, related_name='+') + staff = models.IntegerField(null=True, blank=True) # EmailUserRO created = models.DateTimeField(auto_now_add=True, null=False, blank=False) @@ -71,7 +75,7 @@ class Meta: app_label = 'mooringlicensing' -@python_2_unicode_compatible +# @python_2_unicode_compatible class Document(models.Model): name = models.CharField(max_length=255, blank=True, verbose_name='name', help_text='') @@ -142,6 +146,7 @@ class GlobalSettings(models.Model): KEY_MINIMUM_VESSEL_LENGTH = 'minimum_vessel_length' KEY_MINUMUM_MOORING_VESSEL_LENGTH = 'minimum_mooring_vessel_length' KEY_MINUMUM_STICKER_NUMBER_FOR_DCV_PERMIT = 'min_sticker_number_for_dcv_permit' + KEY_EXTERNAL_DASHBOARD_SECTIONS_LIST = 'external_dashboard_sections_list' keys_for_file = ( KEY_DCV_PERMIT_TEMPLATE_FILE, @@ -162,7 +167,8 @@ class GlobalSettings(models.Model): (KEY_ML_AU_LIST_TEMPLATE_FILE, 'Mooring Licence Authorised User Summary template file'), (KEY_MINIMUM_VESSEL_LENGTH, 'Minimum vessel length'), (KEY_MINUMUM_MOORING_VESSEL_LENGTH, 'Minimum mooring vessel length'), - (KEY_MINUMUM_STICKER_NUMBER_FOR_DCV_PERMIT, 'Minimun sticker number for DCV Permit') + (KEY_MINUMUM_STICKER_NUMBER_FOR_DCV_PERMIT, 'Minimun sticker number for DCV Permit'), + (KEY_EXTERNAL_DASHBOARD_SECTIONS_LIST, 'External dashboard sections list'), ) template_folder = 'mooringlicensing/management/templates' default_values = { @@ -176,18 +182,19 @@ class GlobalSettings(models.Model): KEY_MINIMUM_VESSEL_LENGTH: 3.75, KEY_MINUMUM_MOORING_VESSEL_LENGTH: 6.50, KEY_MINUMUM_STICKER_NUMBER_FOR_DCV_PERMIT: 200000, + KEY_EXTERNAL_DASHBOARD_SECTIONS_LIST: 'LicencesAndPermitsTable, ApplicationsTable, CompliancesTable, WaitingListTable, AuthorisedUserApplicationsTable', } key = models.CharField(max_length=255, choices=keys, blank=False, null=False,) value = models.CharField(max_length=255) - _file = models.FileField(upload_to='approval_permit_template', null=True, blank=True) + _file = models.FileField(upload_to='approval_permit_template', null=True, blank=True, max_length=512) class Meta: app_label = 'mooringlicensing' verbose_name_plural = "Global Settings" -@python_2_unicode_compatible +# @python_2_unicode_compatible class SystemMaintenance(models.Model): name = models.CharField(max_length=100) description = models.TextField() @@ -216,7 +223,9 @@ class Meta: class TemporaryDocument(Document): temp_document_collection = models.ForeignKey( TemporaryDocumentCollection, - related_name='documents') + related_name='documents', + on_delete=models.CASCADE, + ) _file = models.FileField(max_length=255) class Meta: @@ -227,6 +236,41 @@ def update_electoral_roll_doc_filename(instance, filename): return '{}/emailusers/{}/documents/{}'.format(settings.MEDIA_APP_DIR, instance.emailuser.id,filename) +class RevisionedMixin(models.Model): + """ + A model tracked by reversion through the save method. + """ + + def save(self, **kwargs): + from reversion import revisions + + if kwargs.pop("no_revision", False): + super(RevisionedMixin, self).save(**kwargs) + else: + with revisions.create_revision(): + if "version_user" in kwargs: + revisions.set_user(kwargs.pop("version_user", None)) + if "version_comment" in kwargs: + revisions.set_comment(kwargs.pop("version_comment", "")) + super(RevisionedMixin, self).save(**kwargs) + + @property + def created_date(self): + from reversion.models import Version + + # return revisions.get_for_object(self).last().revision.date_created + return Version.objects.get_for_object(self).last().revision.date_created + + @property + def modified_date(self): + from reversion.models import Version + + # return revisions.get_for_object(self).first().revision.date_created + return Version.objects.get_for_object(self).first().revision.date_created + + class Meta: + abstract = True + class VesselSizeCategoryGroup(RevisionedMixin): name = models.CharField(max_length=100, null=False, blank=False) @@ -274,7 +318,7 @@ class VesselSizeCategory(RevisionedMixin): name = models.CharField(max_length=100) start_size = models.DecimalField(max_digits=8, decimal_places=2, default='0.00', help_text='unit [m]') include_start_size = models.BooleanField(default=True) # When true, 'start_size' is included. - vessel_size_category_group = models.ForeignKey(VesselSizeCategoryGroup, null=True, blank=True, related_name='vessel_size_categories') + vessel_size_category_group = models.ForeignKey(VesselSizeCategoryGroup, null=True, blank=True, related_name='vessel_size_categories', on_delete=models.SET_NULL) null_vessel = models.BooleanField(default=False) def get_one_smaller_category(self): @@ -340,11 +384,11 @@ class Meta: verbose_name = 'Number of days Settings' verbose_name_plural = 'Number of days Settings' - def get_setting_by_date(self, target_date=datetime.now(pytz.timezone(TIME_ZONE)).date()): + def get_setting_by_date(self, target_date=datetime.now(pytz.timezone(settings.TIME_ZONE)).date()): return NumberOfDaysSetting.get_setting_by_date(self, target_date) def get_number_of_days_currently_applied(self): - setting = self.get_setting_by_date(target_date=datetime.now(pytz.timezone(TIME_ZONE)).date()) + setting = self.get_setting_by_date(target_date=datetime.now(pytz.timezone(settings.TIME_ZONE)).date()) if setting: return setting.number_of_days else: @@ -357,7 +401,7 @@ def get_number_of_days_currently_applied(self): class NumberOfDaysSetting(RevisionedMixin): number_of_days = models.PositiveSmallIntegerField(blank=True, null=True) date_of_enforcement = models.DateField(blank=True, null=True) - number_of_days_type = models.ForeignKey(NumberOfDaysType, blank=True, null=True, related_name='settings') + number_of_days_type = models.ForeignKey(NumberOfDaysType, blank=True, null=True, related_name='settings', on_delete=models.CASCADE) def __str__(self): return '{} ({})'.format(self.number_of_days, self.number_of_days_type.name) @@ -367,7 +411,7 @@ class Meta: ordering = ['-date_of_enforcement',] @staticmethod - def get_setting_by_date(number_of_days_type, target_date=datetime.now(pytz.timezone(TIME_ZONE)).date()): + def get_setting_by_date(number_of_days_type, target_date=datetime.now(pytz.timezone(settings.TIME_ZONE)).date()): """ Return an setting object which is enabled at the target_date """ @@ -378,6 +422,10 @@ def get_setting_by_date(number_of_days_type, target_date=datetime.now(pytz.timez return setting +# def update_mooring_comms_log_filename(instance, filename): +# return '{}/moorings/{}/communications/{}/{}'.format(settings.MEDIA_APP_DIR, instance.log_entry.mooring.id, instance.log_entry.id, filename) + + import reversion reversion.register(UserSystemSettings, follow=[]) reversion.register(CommunicationsLogEntry, follow=[]) diff --git a/mooringlicensing/components/main/serializers.py b/mooringlicensing/components/main/serializers.py index 015ae8f97..0132df908 100755 --- a/mooringlicensing/components/main/serializers.py +++ b/mooringlicensing/components/main/serializers.py @@ -1,16 +1,46 @@ -from ledger.payments.invoice.models import Invoice from rest_framework import serializers from django.db.models import Sum, Max from mooringlicensing.components.main.models import ( CommunicationsLogEntry, GlobalSettings, TemporaryDocumentCollection, ) -from ledger.accounts.models import EmailUser -from datetime import datetime, date +# from ledger.payments.invoice.models import Invoice +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO, Invoice +from ledger_api_client import utils + +from mooringlicensing.ledger_api_utils import get_invoice_payment_status + + +class EmailUserSerializer(serializers.ModelSerializer): + fullname = serializers.SerializerMethodField() + # text = serializers.SerializerMethodField() + # email = serializers.SerializerMethodField() + + class Meta: + model = EmailUserRO + fields = ( + "id", + "email", + "first_name", + "last_name", + "title", + "organisation", + "fullname", + # "text", + ) + # def get_email(self, obj): + # return '' + + def get_fullname(self, obj): + return "{} {}".format(obj.first_name, obj.last_name) + + def get_text(self, obj): + return self.get_fullname(obj) class CommunicationLogEntrySerializer(serializers.ModelSerializer): - customer = serializers.PrimaryKeyRelatedField(queryset=EmailUser.objects.all(),required=False) + customer = serializers.PrimaryKeyRelatedField(queryset=EmailUserRO.objects.all(),required=False) documents = serializers.SerializerMethodField() class Meta: model = CommunicationsLogEntry @@ -51,6 +81,7 @@ class OracleSerializer(serializers.Serializer): class InvoiceSerializer(serializers.ModelSerializer): payment_status = serializers.SerializerMethodField() + invoice_url = serializers.SerializerMethodField() class Meta: model = Invoice @@ -60,18 +91,25 @@ class Meta: 'reference', 'payment_status', 'settlement_date', + 'invoice_url', ) def get_payment_status(self, invoice): - if invoice.payment_status.lower() == 'unpaid': + invoice_payment_status = get_invoice_payment_status(invoice.id).lower() + + if invoice_payment_status == 'unpaid': return 'Unpaid' - elif invoice.payment_status.lower() == 'partially_paid': + elif invoice_payment_status == 'partially_paid': return 'Partially Paid' - elif invoice.payment_status.lower() == 'paid': + elif invoice_payment_status == 'paid': return 'Paid' else: return 'Over Paid' + def get_invoice_url(self, invoice): + return f'/ledger-toolkit-api/invoice-pdf/{invoice.reference}/' + + class TemporaryDocumentCollectionSerializer(serializers.ModelSerializer): class Meta: model = TemporaryDocumentCollection diff --git a/mooringlicensing/components/main/utils.py b/mooringlicensing/components/main/utils.py index 606ce0522..3487b8aaa 100755 --- a/mooringlicensing/components/main/utils.py +++ b/mooringlicensing/components/main/utils.py @@ -1,5 +1,6 @@ from io import BytesIO -from ledger.settings_base import TIME_ZONE + +from ledger_api_client.settings_base import TIME_ZONE from django.utils import timezone from confy import env @@ -11,33 +12,37 @@ from django.db import connection, transaction from mooringlicensing.components.approvals.models import Sticker, AnnualAdmissionPermit, AuthorisedUserPermit, \ - MooringLicence, Approval, ApprovalHistory + MooringLicence, Approval from mooringlicensing.components.approvals.serializers import ListApprovalSerializer from mooringlicensing.components.proposals.email import send_sticker_printing_batch_email from mooringlicensing.components.proposals.models import ( MooringBay, Mooring, - StickerPrintingBatch, ProposalType + StickerPrintingBatch ) -from mooringlicensing.components.main.decorators import basic_exception_handler, query_debugger +from mooringlicensing.components.main.decorators import query_debugger from rest_framework import serializers from openpyxl import Workbook from copy import deepcopy import logging -logger = logging.getLogger('mooringlicensing') -def belongs_to(user, group_name): - """ - Check if the user belongs to the given group. - :param user: - :param group_name: - :return: - """ - return user.groups.filter(name=group_name).exists() +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) + +# def belongs_to(user, group_name): +# """ +# Check if the user belongs to the given group. +# :param user: +# :param group_name: +# :return: +# """ +# return user.groups.filter(name=group_name).exists() def is_payment_officer(user): - return user.is_authenticated() and (belongs_to(user, settings.GROUP_MOORING_LICENSING_PAYMENT_OFFICER) or user.is_superuser) + # return user.is_authenticated() and (belongs_to(user, settings.GROUP_MOORING_LICENSING_PAYMENT_OFFICER) or user.is_superuser) + from mooringlicensing.helpers import belongs_to + return user.is_authenticated and (belongs_to(user, settings.GROUP_MOORING_LICENSING_PAYMENT_OFFICER) or user.is_superuser) def to_local_tz(_date): @@ -214,6 +219,10 @@ def handle_validation_error(e): def sticker_export(): + """ + This function exports sticker details data as a spreadsheet file, + and store it as a StickerPrintingBatch object. + """ logger = logging.getLogger('cron_tasks') # TODO: Implement below # Note: if the user wants to apply for e.g. three new authorisations, @@ -305,6 +314,9 @@ def sticker_export(): def email_stickers_document(): + """ + Email the file generated at the sticker_export() function to the sticker company: + """ logger = logging.getLogger('cron_tasks') updates, errors = [], [] @@ -312,7 +324,10 @@ def email_stickers_document(): batches = StickerPrintingBatch.objects.filter(emailed_datetime__isnull=True) if batches.count(): current_datetime = timezone.localtime(timezone.now()) + + # Send sticker details spreadsheet file to the printing company send_sticker_printing_batch_email(batches) + for batch in batches: batch.emailed_datetime = current_datetime batch.save() @@ -487,3 +502,4 @@ def calculate_max_length(fee_constructor, max_amount_paid, proposal_type): max_length = calculate_minimum_max_length(fee_items_interested, max_amount_paid) return max_length + diff --git a/mooringlicensing/components/organisations/api.py b/mooringlicensing/components/organisations/api.py index ba381a546..b803e3f33 100755 --- a/mooringlicensing/components/organisations/api.py +++ b/mooringlicensing/components/organisations/api.py @@ -1,74 +1,78 @@ import traceback -import base64 -import geojson -from six.moves.urllib.parse import urlparse -from wsgiref.util import FileWrapper +# import base64 +# import geojson +# from six.moves.urllib.parse import urlparse +# from wsgiref.util import FileWrapper from django.db.models import Q, Min from django.db import transaction -from django.http import HttpResponse -from django.core.files.base import ContentFile +# from django.http import HttpResponse +# from django.core.files.base import ContentFile from django.core.exceptions import ValidationError -from django.conf import settings -from django.contrib import messages -from django.views.decorators.http import require_http_methods -from django.views.decorators.csrf import csrf_exempt -from django.utils import timezone +# from django.conf import settings +# from django.contrib import messages +# from django.views.decorators.http import require_http_methods +# from django.views.decorators.csrf import csrf_exempt +# from django.utils import timezone from rest_framework import viewsets, serializers, status, generics, views -from rest_framework.decorators import detail_route, list_route,renderer_classes +# from rest_framework.decorators import detail_route, list_route,renderer_classes +from rest_framework.decorators import action as detail_route +from rest_framework.decorators import action as list_route +from rest_framework.decorators import renderer_classes from rest_framework.response import Response from rest_framework.renderers import JSONRenderer -from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission -from rest_framework.pagination import PageNumberPagination -from datetime import datetime, timedelta -from collections import OrderedDict -from django.core.cache import cache -from ledger.accounts.models import EmailUser,OrganisationAddress -from ledger.address.models import Country -from datetime import datetime,timedelta, date +# from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission +# from rest_framework.pagination import PageNumberPagination +# from datetime import datetime, timedelta +# from collections import OrderedDict +# from django.core.cache import cache +# from ledger.accounts.models import EmailUser,OrganisationAddress +from ledger_api_client.ledger_models import EmailUserRO as Emailuser +# from ledger.address.models import Country +# from datetime import datetime,timedelta, date from mooringlicensing.helpers import is_customer, is_internal -from mooringlicensing.components.organisations.models import ( - Organisation, - OrganisationContact, - OrganisationRequest, - OrganisationRequestUserAction, - OrganisationContact, - OrganisationAccessGroup, - OrganisationRequestLogEntry, - OrganisationAction, - ledger_organisation, - ) +from mooringlicensing.components.organisations.models import ( + Organisation, + # OrganisationContact, + OrganisationRequest, + OrganisationRequestUserAction, + OrganisationContact, + OrganisationAccessGroup, + # OrganisationRequestLogEntry, + # OrganisationAction, + # ledger_organisation, + ) from mooringlicensing.components.organisations.serializers import ( - OrganisationSerializer, - OrganisationAddressSerializer, - DetailsSerializer, - SaveDiscountSerializer, - OrganisationRequestSerializer, - OrganisationRequestDTSerializer, - OrganisationContactSerializer, - OrganisationCheckSerializer, - OrganisationPinCheckSerializer, - OrganisationRequestActionSerializer, - OrganisationActionSerializer, - OrganisationRequestCommsSerializer, - OrganisationCommsSerializer, - OrganisationUnlinkUserSerializer, - OrgUserAcceptSerializer, - MyOrganisationsSerializer, - OrganisationCheckExistSerializer, - LedgerOrganisationFilterSerializer, - OrganisationLogEntrySerializer, - OrganisationRequestLogEntrySerializer, - ) + OrganisationSerializer, + OrganisationAddressSerializer, + DetailsSerializer, + SaveDiscountSerializer, + OrganisationRequestSerializer, + OrganisationRequestDTSerializer, + OrganisationContactSerializer, + OrganisationCheckSerializer, + OrganisationPinCheckSerializer, + OrganisationRequestActionSerializer, + OrganisationActionSerializer, + OrganisationRequestCommsSerializer, + OrganisationCommsSerializer, + OrganisationUnlinkUserSerializer, + OrgUserAcceptSerializer, + MyOrganisationsSerializer, + OrganisationCheckExistSerializer, + LedgerOrganisationFilterSerializer, + OrganisationLogEntrySerializer, + OrganisationRequestLogEntrySerializer, + ) #from mooringlicensing.components.applications.serializers import ( # BaseApplicationSerializer, # ) -from mooringlicensing.components.organisations.emails import ( - send_organisation_address_updated_email_notification, - send_organisation_id_upload_email_notification, - send_organisation_request_email_notification, - ) +# from mooringlicensing.components.organisations.emails import ( +# send_organisation_address_updated_email_notification, +# send_organisation_id_upload_email_notification, +# send_organisation_request_email_notification, +# ) #from wildlifecompliance.components.applications.models import ( @@ -80,369 +84,369 @@ class OrganisationViewSet(viewsets.ModelViewSet): - queryset = Organisation.objects.all() - serializer_class = OrganisationSerializer - - def _get_queryset(self): - user = self.request.user - if is_internal(self.request): - return Organisation.objects.all() - elif is_customer(self.request): - return user.mooringlicensing_organisations.all() - return Organisation.objects.none() - - @detail_route(methods=['GET',]) - def contacts(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrganisationContactSerializer(instance.contacts.exclude(user_status='pending'),many=True) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def contacts_linked(self, request, *args, **kwargs): - try: - qs = self.get_queryset() - serializer = OrganisationContactSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def contacts_exclude(self, request, *args, **kwargs): - try: - instance = self.get_object() - qs = instance.contacts.exclude(user_status='draft') - serializer = OrganisationContactSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def validate_pins(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrganisationPinCheckSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - ret = instance.validate_pins(serializer.validated_data['pin1'],serializer.validated_data['pin2'],request) - - if ret == None: - # user has already been to this organisation - don't add again - data = {'valid': ret} - return Response({'valid' : 'User already exists'}) - - data = {'valid': ret} - if data['valid']: - # Notify each Admin member of request. - instance.send_organisation_request_link_notification(request) - return Response(data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST', ]) - def accept_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email=serializer.validated_data['email'].lower() - ) - instance.accept_user(user_obj, request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST', ]) - def accept_declined_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email=serializer.validated_data['email'].lower() - ) - instance.accept_declined_user(user_obj, request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def decline_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.decline_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def unlink_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.unlink_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - - - @detail_route(methods=['POST',]) - def make_admin_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.make_admin_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def make_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.make_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def make_consultant(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.make_consultant(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def suspend_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.suspend_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def reinstate_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.reinstate_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - def relink_user(self, request, *args, **kwargs): - try: - instance = self.get_object() - serializer = OrgUserAcceptSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - user_obj = EmailUser.objects.get( - email = serializer.validated_data['email'].lower() - ) - instance.relink_user(user_obj,request) - serializer = self.get_serializer(instance) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - - @detail_route(methods=['GET',]) - def action_log(self, request, *args, **kwargs): - try: - instance = self.get_object() - qs = instance.action_logs.all() - serializer = OrganisationActionSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) + queryset = Organisation.objects.all() + serializer_class = OrganisationSerializer + + def _get_queryset(self): + user = self.request.user + if is_internal(self.request): + return Organisation.objects.all() + elif is_customer(self.request): + return user.mooringlicensing_organisations.all() + return Organisation.objects.none() + + @detail_route(methods=['GET',], detail=True) + def contacts(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrganisationContactSerializer(instance.contacts.exclude(user_status='pending'),many=True) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def contacts_linked(self, request, *args, **kwargs): + try: + qs = self.get_queryset() + serializer = OrganisationContactSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def contacts_exclude(self, request, *args, **kwargs): + try: + instance = self.get_object() + qs = instance.contacts.exclude(user_status='draft') + serializer = OrganisationContactSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def validate_pins(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrganisationPinCheckSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + ret = instance.validate_pins(serializer.validated_data['pin1'],serializer.validated_data['pin2'],request) + + if ret == None: + # user has already been to this organisation - don't add again + data = {'valid': ret} + return Response({'valid' : 'User already exists'}) + + data = {'valid': ret} + if data['valid']: + # Notify each Admin member of request. + instance.send_organisation_request_link_notification(request) + return Response(data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST', ], detail=True) + def accept_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email=serializer.validated_data['email'].lower() + ) + instance.accept_user(user_obj, request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST', ], detail=True) + def accept_declined_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email=serializer.validated_data['email'].lower() + ) + instance.accept_declined_user(user_obj, request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def decline_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.decline_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def unlink_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.unlink_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + + + @detail_route(methods=['POST',], detail=True) + def make_admin_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.make_admin_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def make_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.make_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def make_consultant(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.make_consultant(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def suspend_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.suspend_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def reinstate_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.reinstate_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + def relink_user(self, request, *args, **kwargs): + try: + instance = self.get_object() + serializer = OrgUserAcceptSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user_obj = EmailUser.objects.get( + email = serializer.validated_data['email'].lower() + ) + instance.relink_user(user_obj,request) + serializer = self.get_serializer(instance) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + + @detail_route(methods=['GET',], detail=True) + def action_log(self, request, *args, **kwargs): + try: + instance = self.get_object() + qs = instance.action_logs.all() + serializer = OrganisationActionSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) # @detail_route(methods=['GET',]) # def applications(self, request, *args, **kwargs): @@ -461,154 +465,156 @@ def action_log(self, request, *args, **kwargs): # print(traceback.print_exc()) # raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) - def comms_log(self, request, *args, **kwargs): - try: - instance = self.get_object() - qs = instance.comms_logs.all() - serializer = OrganisationCommsSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['POST',]) - @renderer_classes((JSONRenderer,)) - def add_comms_log(self, request, *args, **kwargs): - try: - with transaction.atomic(): - instance = self.get_object() - mutable=request.data._mutable - request.data._mutable=True - request.data['organisation'] = u'{}'.format(instance.id) - request.data['staff'] = u'{}'.format(request.user.id) - request.data._mutable=mutable - serializer = OrganisationLogEntrySerializer(data=request.data) - serializer.is_valid(raise_exception=True) - comms = serializer.save() - # Save the files - for f in request.FILES: - document = comms.documents.create() - document.name = str(request.FILES[f]) - document._file = request.FILES[f] - document.save() - # End Save Documents - - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @list_route(methods=['POST',]) - def existance(self, request, *args, **kwargs): - try: - serializer = OrganisationCheckSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - data = Organisation.existance(serializer.validated_data['abn']) - # Check request user cannot be relinked to org. - data.update([('user', request.user.id)]) - data.update([('abn', request.data['abn'])]) - serializer = OrganisationCheckExistSerializer(data=data) - serializer.is_valid(raise_exception=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def update_details(self, request, *args, **kwargs): - try: - org = self.get_object() - instance = org.organisation - data=request.data - serializer = DetailsSerializer(instance,data=data, context={'request':request}) - serializer.is_valid(raise_exception=True) - instance = serializer.save() - #serializer = self.get_serializer(org) - - if is_internal(request) and 'apply_application_discount' in request.data: - data = request.data - if not data['apply_application_discount']: - data['application_discount'] = 0 - if not data['apply_licence_discount']: - data['licence_discount'] = 0 - - if data['application_discount'] == 0: - data['apply_application_discount'] = False - if data['licence_discount'] == 0: - data['apply_licence_discount'] = False - - serializer = SaveDiscountSerializer(org,data=data) - serializer.is_valid(raise_exception=True) - instance = serializer.save() - - serializer = self.get_serializer(org) - return Response(serializer.data); - except serializers.ValidationError as e: - print(e.get_full_details()) - #raise serializers.ValidationError(str( e.get_full_details() )) - raise - except ValidationError as e: - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def update_address(self, request, *args, **kwargs): - try: - org = self.get_object() - instance = org.organisation - serializer = OrganisationAddressSerializer(data=request.data) - serializer.is_valid(raise_exception=True) - address, created = OrganisationAddress.objects.get_or_create( - line1 = serializer.validated_data['line1'], - locality = serializer.validated_data['locality'], - state = serializer.validated_data['state'], - country = serializer.validated_data['country'], - postcode = serializer.validated_data['postcode'], - organisation = instance - ) - instance.postal_address = address - instance.save() - #send_organisation_address_updated_email_notification(request.user, instance, org, request) - serializer = self.get_serializer(org) - return Response(serializer.data); - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def upload_id(self, request, *args, **kwargs): - pass + @detail_route(methods=['GET',], detail=True) + def comms_log(self, request, *args, **kwargs): + try: + instance = self.get_object() + qs = instance.comms_logs.all() + serializer = OrganisationCommsSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + @detail_route(methods=['POST',], detail=True) + @renderer_classes((JSONRenderer,)) + def add_comms_log(self, request, *args, **kwargs): + try: + with transaction.atomic(): + instance = self.get_object() + mutable=request.data._mutable + request.data._mutable=True + request.data['organisation'] = u'{}'.format(instance.id) + request.data['staff'] = u'{}'.format(request.user.id) + request.data._mutable=mutable + serializer = OrganisationLogEntrySerializer(data=request.data) + serializer.is_valid(raise_exception=True) + comms = serializer.save() + # Save the files + for f in request.FILES: + document = comms.documents.create() + document.name = str(request.FILES[f]) + document._file = request.FILES[f] + document.save() + # End Save Documents + + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @list_route(methods=['POST',], detail=False) + def existance(self, request, *args, **kwargs): + try: + serializer = OrganisationCheckSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + data = Organisation.existance(serializer.validated_data['abn']) + # Check request user cannot be relinked to org. + data.update([('user', request.user.id)]) + data.update([('abn', request.data['abn'])]) + serializer = OrganisationCheckExistSerializer(data=data) + serializer.is_valid(raise_exception=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def update_details(self, request, *args, **kwargs): + try: + org = self.get_object() + instance = org.organisation + data=request.data + serializer = DetailsSerializer(instance,data=data, context={'request':request}) + serializer.is_valid(raise_exception=True) + instance = serializer.save() + #serializer = self.get_serializer(org) + + if is_internal(request) and 'apply_application_discount' in request.data: + data = request.data + if not data['apply_application_discount']: + data['application_discount'] = 0 + if not data['apply_licence_discount']: + data['licence_discount'] = 0 + + if data['application_discount'] == 0: + data['apply_application_discount'] = False + if data['licence_discount'] == 0: + data['apply_licence_discount'] = False + + serializer = SaveDiscountSerializer(org,data=data) + serializer.is_valid(raise_exception=True) + instance = serializer.save() + + serializer = self.get_serializer(org) + return Response(serializer.data); + except serializers.ValidationError as e: + print(e.get_full_details()) + #raise serializers.ValidationError(str( e.get_full_details() )) + raise + except ValidationError as e: + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def update_address(self, request, *args, **kwargs): + try: + org = self.get_object() + instance = org.organisation + serializer = OrganisationAddressSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + + # TODO: Update address + # address, created = OrganisationAddress.objects.get_or_create( + # line1 = serializer.validated_data['line1'], + # locality = serializer.validated_data['locality'], + # state = serializer.validated_data['state'], + # country = serializer.validated_data['country'], + # postcode = serializer.validated_data['postcode'], + # organisation = instance + # ) + # instance.postal_address = address + instance.save() + #send_organisation_address_updated_email_notification(request.user, instance, org, request) + serializer = self.get_serializer(org) + return Response(serializer.data); + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def upload_id(self, request, *args, **kwargs): + pass # try: # instance = self.get_object() # instance.organisation.upload_identification(request) @@ -657,363 +663,366 @@ def upload_id(self, request, *args, **kwargs): from rest_framework import filters class OrganisationListFilterView(generics.ListAPIView): - """ https://cop-internal.dbca.wa.gov.au/api/filtered_organisations?search=Org1 - """ - #queryset = Organisation.objects.all() - queryset = ledger_organisation.objects.none() - serializer_class = LedgerOrganisationFilterSerializer - filter_backends = (filters.SearchFilter,) - search_fields = ('name',) - - def get_queryset(self): - org_list = Organisation.objects.all().values_list('organisation_id', flat=True) - return ledger_organisation.objects.filter(id__in=org_list) + """ https://cop-internal.dbca.wa.gov.au/api/filtered_organisations?search=Org1 + """ + pass + # queryset = ledger_organisation.objects.none() + # serializer_class = LedgerOrganisationFilterSerializer + # filter_backends = (filters.SearchFilter,) + # search_fields = ('name',) + # + # def get_queryset(self): + # org_list = Organisation.objects.all().values_list('organisation_id', flat=True) + # return ledger_organisation.objects.filter(id__in=org_list) class OrganisationRequestsViewSet(viewsets.ModelViewSet): - queryset = OrganisationRequest.objects.all() - serializer_class = OrganisationRequestSerializer - - def get_queryset(self): - user = self.request.user - if is_internal(self.request): - return OrganisationRequest.objects.all() - elif is_customer(self.request): - return user.organisationrequest_set.all() - return OrganisationRequest.objects.none() - - @list_route(methods=['GET',]) - def datatable_list(self, request, *args, **kwargs): - try: - qs = self.get_queryset() - serializer = OrganisationRequestDTSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - # @list_route(methods=['GET',]) - # def user_organisation_request_list(self, request, *args, **kwargs): - # try: - # queryset = self.get_queryset() - # queryset = queryset.filter(requester = request.user) - - # # instance = OrganisationRequest.objects.get(requester = request.user) - # serializer = self.get_serializer(queryset, many=True) - # return Response(serializer.data) - # except serializers.ValidationError: - # print(traceback.print_exc()) - # raise - # except ValidationError as e: - # print(traceback.print_exc()) - # raise serializers.ValidationError(repr(e.error_dict)) - # except Exception as e: - # print(traceback.print_exc()) - # raise serializers.ValidationError(str(e)) - - @list_route(methods=['GET', ]) - def get_pending_requests(self, request, *args, **kwargs): - try: - qs = self.get_queryset().filter(requester=request.user, status='with_assessor') - serializer = OrganisationRequestDTSerializer(qs, many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @list_route(methods=['GET', ]) - def get_amendment_requested_requests(self, request, *args, **kwargs): - try: - qs = self.get_queryset().filter(requester=request.user, status='amendment_requested') - serializer = OrganisationRequestDTSerializer(qs, many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - @detail_route(methods=['GET',]) - def assign_request_user(self, request, *args, **kwargs): - try: - instance = self.get_object(requester =request.user) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def unassign(self, request, *args, **kwargs): - try: - instance = self.get_object() - instance.unassign(request) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def accept(self, request, *args, **kwargs): - try: - instance = self.get_object() - instance.accept(request) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - # print(traceback.print_exc()) - # raise serializers.ValidationError(repr(e.error_dict)) - if hasattr(e,'error_dict'): - raise serializers.ValidationError(repr(e.error_dict)) - else: - if hasattr(e,'message'): - raise serializers.ValidationError(e.message) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def amendment_request(self, request, *args, **kwargs): - try: - instance = self.get_object() - instance.amendment_request(request) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['PUT',]) - def reupload_identification_amendment_request(self, request, *args, **kwargs): - try: - instance = self.get_object() - instance.reupload_identification_amendment_request(request) - serializer = OrganisationRequestSerializer(instance, partial=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def decline(self, request, *args, **kwargs): - try: - instance = self.get_object() - reason = '' - instance.decline(reason, request) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - def assign_to(self, request, *args, **kwargs): - try: - instance = self.get_object() - user_id = request.data.get('user_id',None) - user = None - if not user_id: - raise serializers.ValiationError('A user id is required') - try: - user = EmailUser.objects.get(id=user_id) - except EmailUser.DoesNotExist: - raise serializers.ValidationError('A user with the id passed in does not exist') - instance.assign_to(user,request) - serializer = OrganisationRequestSerializer(instance) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def action_log(self, request, *args, **kwargs): - try: - instance = self.get_object() - qs = instance.action_logs.all() - serializer = OrganisationRequestActionSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['GET',]) - def comms_log(self, request, *args, **kwargs): - try: - instance = self.get_object() - qs = instance.comms_logs.all() - serializer = OrganisationRequestCommsSerializer(qs,many=True) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - @detail_route(methods=['POST',]) - @renderer_classes((JSONRenderer,)) - def add_comms_log(self, request, *args, **kwargs): - try: - with transaction.atomic(): - instance = self.get_object() - mutable=request.data._mutable - request.data._mutable=True - request.data['organisation'] = u'{}'.format(instance.id) - request.data['request'] = u'{}'.format(instance.id) - request.data['staff'] = u'{}'.format(request.user.id) - request.data._mutable=mutable - serializer = OrganisationRequestLogEntrySerializer(data=request.data) - serializer.is_valid(raise_exception=True) - comms = serializer.save() - # Save the files - for f in request.FILES: - document = comms.documents.create() - document.name = str(request.FILES[f]) - document._file = request.FILES[f] - document.save() - # End Save Documents - - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) - - - def create(self, request, *args, **kwargs): - try: - serializer = self.get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - serializer.validated_data['requester'] = request.user - if request.data['role'] == 'consultant': - # Check if consultant can be relinked to org. - data = Organisation.existance(request.data['abn']) - data.update([('user', request.user.id)]) - data.update([('abn', request.data['abn'])]) - existing_org = OrganisationCheckExistSerializer(data=data) - existing_org.is_valid(raise_exception=True) - with transaction.atomic(): - instance = serializer.save() - instance.log_user_action(OrganisationRequestUserAction.ACTION_LODGE_REQUEST.format(instance.id),request) - instance.send_organisation_request_email_notification(request) - return Response(serializer.data) - except serializers.ValidationError: - print(traceback.print_exc()) - raise - except ValidationError as e: - print(traceback.print_exc()) - raise serializers.ValidationError(repr(e.error_dict)) - except Exception as e: - print(traceback.print_exc()) - raise serializers.ValidationError(str(e)) + queryset = OrganisationRequest.objects.all() + serializer_class = OrganisationRequestSerializer + + def get_queryset(self): + user = self.request.user + if is_internal(self.request): + return OrganisationRequest.objects.all() + elif is_customer(self.request): + # return user.organisationrequest_set.all() + # user_id = user.id + # print(user_id) + org_requests = OrganisationRequest.objects.filter(requester=user.id) + return org_requests + return OrganisationRequest.objects.none() + + @list_route(methods=['GET',], detail=False) + def datatable_list(self, request, *args, **kwargs): + try: + qs = self.get_queryset() + serializer = OrganisationRequestDTSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + # @list_route(methods=['GET',]) + # def user_organisation_request_list(self, request, *args, **kwargs): + # try: + # queryset = self.get_queryset() + # queryset = queryset.filter(requester = request.user) + + # # instance = OrganisationRequest.objects.get(requester = request.user) + # serializer = self.get_serializer(queryset, many=True) + # return Response(serializer.data) + # except serializers.ValidationError: + # print(traceback.print_exc()) + # raise + # except ValidationError as e: + # print(traceback.print_exc()) + # raise serializers.ValidationError(repr(e.error_dict)) + # except Exception as e: + # print(traceback.print_exc()) + # raise serializers.ValidationError(str(e)) + + @list_route(methods=['GET', ], detail=False) + def get_pending_requests(self, request, *args, **kwargs): + try: + qs = self.get_queryset().filter(requester=request.user.id, status='with_assessor') + serializer = OrganisationRequestDTSerializer(qs, many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @list_route(methods=['GET', ], detail=False) + def get_amendment_requested_requests(self, request, *args, **kwargs): + try: + qs = self.get_queryset().filter(requester=request.user, status='amendment_requested') + serializer = OrganisationRequestDTSerializer(qs, many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def assign_request_user(self, request, *args, **kwargs): + try: + instance = self.get_object(requester =request.user) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def unassign(self, request, *args, **kwargs): + try: + instance = self.get_object() + instance.unassign(request) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def accept(self, request, *args, **kwargs): + try: + instance = self.get_object() + instance.accept(request) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + # print(traceback.print_exc()) + # raise serializers.ValidationError(repr(e.error_dict)) + if hasattr(e,'error_dict'): + raise serializers.ValidationError(repr(e.error_dict)) + else: + if hasattr(e,'message'): + raise serializers.ValidationError(e.message) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def amendment_request(self, request, *args, **kwargs): + try: + instance = self.get_object() + instance.amendment_request(request) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['PUT',], detail=True) + def reupload_identification_amendment_request(self, request, *args, **kwargs): + try: + instance = self.get_object() + instance.reupload_identification_amendment_request(request) + serializer = OrganisationRequestSerializer(instance, partial=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def decline(self, request, *args, **kwargs): + try: + instance = self.get_object() + reason = '' + instance.decline(reason, request) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + def assign_to(self, request, *args, **kwargs): + try: + instance = self.get_object() + user_id = request.data.get('user_id',None) + user = None + if not user_id: + raise serializers.ValiationError('A user id is required') + try: + user = EmailUser.objects.get(id=user_id) + except EmailUser.DoesNotExist: + raise serializers.ValidationError('A user with the id passed in does not exist') + instance.assign_to(user,request) + serializer = OrganisationRequestSerializer(instance) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def action_log(self, request, *args, **kwargs): + try: + instance = self.get_object() + qs = instance.action_logs.all() + serializer = OrganisationRequestActionSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['GET',], detail=True) + def comms_log(self, request, *args, **kwargs): + try: + instance = self.get_object() + qs = instance.comms_logs.all() + serializer = OrganisationRequestCommsSerializer(qs,many=True) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + @detail_route(methods=['POST',], detail=True) + @renderer_classes((JSONRenderer,)) + def add_comms_log(self, request, *args, **kwargs): + try: + with transaction.atomic(): + instance = self.get_object() + mutable=request.data._mutable + request.data._mutable=True + request.data['organisation'] = u'{}'.format(instance.id) + request.data['request'] = u'{}'.format(instance.id) + request.data['staff'] = u'{}'.format(request.user.id) + request.data._mutable=mutable + serializer = OrganisationRequestLogEntrySerializer(data=request.data) + serializer.is_valid(raise_exception=True) + comms = serializer.save() + # Save the files + for f in request.FILES: + document = comms.documents.create() + document.name = str(request.FILES[f]) + document._file = request.FILES[f] + document.save() + # End Save Documents + + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) + + + def create(self, request, *args, **kwargs): + try: + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.validated_data['requester'] = request.user + if request.data['role'] == 'consultant': + # Check if consultant can be relinked to org. + data = Organisation.existance(request.data['abn']) + data.update([('user', request.user.id)]) + data.update([('abn', request.data['abn'])]) + existing_org = OrganisationCheckExistSerializer(data=data) + existing_org.is_valid(raise_exception=True) + with transaction.atomic(): + instance = serializer.save() + instance.log_user_action(OrganisationRequestUserAction.ACTION_LODGE_REQUEST.format(instance.id),request) + instance.send_organisation_request_email_notification(request) + return Response(serializer.data) + except serializers.ValidationError: + print(traceback.print_exc()) + raise + except ValidationError as e: + print(traceback.print_exc()) + raise serializers.ValidationError(repr(e.error_dict)) + except Exception as e: + print(traceback.print_exc()) + raise serializers.ValidationError(str(e)) class OrganisationAccessGroupMembers(views.APIView): - renderer_classes = [JSONRenderer,] - def get(self,request, format=None): - members = [] - if is_internal(request): - group = OrganisationAccessGroup.objects.first() - if group: - for m in group.all_members: - members.append({'name': m.get_full_name(),'id': m.id}) - else: - for m in EmailUser.objects.filter(is_superuser=True,is_staff=True,is_active=True): - members.append({'name': m.get_full_name(),'id': m.id}) - return Response(members) + renderer_classes = [JSONRenderer,] + def get(self,request, format=None): + members = [] + if is_internal(request): + group = OrganisationAccessGroup.objects.first() + if group: + for m in group.all_members: + members.append({'name': m.get_full_name(),'id': m.id}) + else: + for m in EmailUser.objects.filter(is_superuser=True,is_staff=True,is_active=True): + members.append({'name': m.get_full_name(),'id': m.id}) + return Response(members) class OrganisationContactViewSet(viewsets.ModelViewSet): - serializer_class = OrganisationContactSerializer - queryset = OrganisationContact.objects.all() - - def get_queryset(self): - user = self.request.user - if is_internal(self.request): - return OrganisationContact.objects.all() - elif is_customer(self.request): - user_orgs = [org.id for org in user.mooringlicensing_organisations.all()] - return OrganisationContact.objects.filter( Q(organisation_id__in = user_orgs) ) - return OrganisationContact.objects.none() + serializer_class = OrganisationContactSerializer + queryset = OrganisationContact.objects.all() + + def get_queryset(self): + user = self.request.user + if is_internal(self.request): + return OrganisationContact.objects.all() + elif is_customer(self.request): + user_orgs = [org.id for org in user.mooringlicensing_organisations.all()] + return OrganisationContact.objects.filter( Q(organisation_id__in = user_orgs) ) + return OrganisationContact.objects.none() class MyOrganisationsViewSet(viewsets.ModelViewSet): - queryset = Organisation.objects.all() - serializer_class = MyOrganisationsSerializer - - def get_queryset(self): - user = self.request.user - if is_internal(self.request): - return Organisation.objects.all() - elif is_customer(self.request): - return user.mooringlicensing_organisations.all() - return Organisation.objects.none() + queryset = Organisation.objects.all() + serializer_class = MyOrganisationsSerializer + + def get_queryset(self): + user = self.request.user + if is_internal(self.request): + return Organisation.objects.all() + elif is_customer(self.request): + return user.mooringlicensing_organisations.all() + return Organisation.objects.none() diff --git a/mooringlicensing/components/organisations/emails.py b/mooringlicensing/components/organisations/emails.py index 519a3d819..58ef75fa0 100755 --- a/mooringlicensing/components/organisations/emails.py +++ b/mooringlicensing/components/organisations/emails.py @@ -2,7 +2,7 @@ from django.core.mail import EmailMultiAlternatives, EmailMessage from django.utils.encoding import smart_text -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse from django.conf import settings from mooringlicensing.components.emails.emails import TemplateEmailBase diff --git a/mooringlicensing/components/organisations/models.py b/mooringlicensing/components/organisations/models.py index 53969b3b2..f22311e32 100755 --- a/mooringlicensing/components/organisations/models.py +++ b/mooringlicensing/components/organisations/models.py @@ -4,12 +4,12 @@ from django.contrib.sites.models import Site from django.dispatch import receiver from django.db.models.signals import pre_delete -from django.utils.encoding import python_2_unicode_compatible +# from django.utils.encoding import python_2_unicode_compatible from django.core.exceptions import ValidationError from django.contrib.postgres.fields.jsonb import JSONField from django.core.validators import MaxValueValidator, MinValueValidator -from ledger.accounts.models import Organisation as ledger_organisation -from ledger.accounts.models import EmailUser,RevisionedMixin #,Document +# from ledger.accounts.models import Organisation as ledger_organisation +# from ledger.accounts.models import EmailUser,RevisionedMixin #,Document from mooringlicensing.components.main.models import UserAction,CommunicationsLogEntry, Document from mooringlicensing.components.organisations.utils import random_generator, can_admin_org, has_atleast_one_admin from mooringlicensing.components.organisations.emails import ( @@ -27,12 +27,16 @@ send_organisation_request_link_email_notification, ) +from django.contrib.postgres.fields import ArrayField -@python_2_unicode_compatible + +# @python_2_unicode_compatible class Organisation(models.Model): - organisation = models.ForeignKey(ledger_organisation) + # organisation = models.ForeignKey(ledger_organisation) + organisation = models.IntegerField() # Ledger Organisation # TODO: business logic related to delegate changes. - delegates = models.ManyToManyField(EmailUser, blank=True, through='UserDelegation', related_name='mooringlicensing_organisations') + # delegates = models.ManyToManyField(EmailUser, blank=True, through='UserDelegation', related_name='mooringlicensing_organisations') + delegates = ArrayField(models.IntegerField(), blank=True) # EmailUserRO #pin_one = models.CharField(max_length=50,blank=True) #pin_two = models.CharField(max_length=50,blank=True) admin_pin_one = models.CharField(max_length=50,blank=True) @@ -470,7 +474,7 @@ def email(self): def first_five(self): return ','.join([user.get_full_name() for user in self.delegates.all()[:5] if can_admin_org(self, user)]) -@python_2_unicode_compatible +# @python_2_unicode_compatible class OrganisationContact(models.Model): USER_STATUS_CHOICES = (('draft', 'Draft'), ('pending', 'Pending'), @@ -485,7 +489,7 @@ class OrganisationContact(models.Model): user_status = models.CharField('Status', max_length=40, choices=USER_STATUS_CHOICES,default=USER_STATUS_CHOICES[0][0]) user_role = models.CharField('Role', max_length=40, choices=USER_ROLE_CHOICES,default='organisation_user') is_admin = models.BooleanField(default= False) - organisation = models.ForeignKey(Organisation, related_name='contacts') + organisation = models.ForeignKey(Organisation, related_name='contacts', on_delete=models.CASCADE) email = models.EmailField(blank=False) first_name = models.CharField(max_length=128, blank=False, verbose_name='Given name(s)') last_name = models.CharField(max_length=128, blank=False) @@ -518,8 +522,9 @@ def check_consultant(self): return self.user_status == 'active' and self.user_role =='consultant' class UserDelegation(models.Model): - organisation = models.ForeignKey(Organisation) - user = models.ForeignKey(EmailUser) + organisation = models.ForeignKey(Organisation, on_delete=models.CASCADE) + # user = models.ForeignKey(EmailUser) + user = models.IntegerField() # EmailUserRO class Meta: unique_together = (('organisation','user'),) @@ -561,11 +566,11 @@ class OrganisationAction(UserAction): def log_action(cls, organisation, action, user): return cls.objects.create( organisation=organisation, - who=user, + who=user.id if user else None, what=str(action) ) - organisation = models.ForeignKey(Organisation,related_name='action_logs') + organisation = models.ForeignKey(Organisation,related_name='action_logs', on_delete=models.CASCADE) class Meta: app_label = 'mooringlicensing' @@ -575,7 +580,7 @@ def update_organisation_comms_log_filename(instance, filename): class OrganisationLogDocument(Document): - log_entry = models.ForeignKey('OrganisationLogEntry',related_name='documents') + log_entry = models.ForeignKey('OrganisationLogEntry',related_name='documents', on_delete=models.CASCADE) _file = models.FileField(upload_to=update_organisation_comms_log_filename, max_length=512) class Meta: @@ -583,7 +588,7 @@ class Meta: class OrganisationLogEntry(CommunicationsLogEntry): - organisation = models.ForeignKey(Organisation, related_name='comms_logs') + organisation = models.ForeignKey(Organisation, related_name='comms_logs', on_delete=models.CASCADE) def save(self, **kwargs): # save the request id if the reference not provided @@ -607,8 +612,10 @@ class OrganisationRequest(models.Model): ) name = models.CharField(max_length=128) abn = models.CharField(max_length=50, null=True, blank=True, verbose_name='ABN') - requester = models.ForeignKey(EmailUser) - assigned_officer = models.ForeignKey(EmailUser, blank=True, null=True, related_name='org_request_assignee') + # requester = models.ForeignKey(EmailUser) + requester = models.IntegerField() # EmailUserRO + # assigned_officer = models.ForeignKey(EmailUser, blank=True, null=True, related_name='org_request_assignee') + assigned_officer = models.IntegerField(null=True, blank=True) # EmailUserRO identification = models.FileField(upload_to='organisation/requests/%Y/%m/%d', max_length=512, null=True, blank=True) status = models.CharField(max_length=100,choices=STATUS_CHOICES, default="with_assessor") lodgement_date = models.DateTimeField(auto_now_add=True) @@ -710,8 +717,9 @@ def log_user_action(self, action, request): return OrganisationRequestUserAction.log_action(self, action, request.user) class OrganisationAccessGroup(models.Model): - site = models.OneToOneField(Site, default='1') - members = models.ManyToManyField(EmailUser) + # site = models.OneToOneField(Site, default='1') + # members = models.ManyToManyField(EmailUser) + members = ArrayField(models.IntegerField(), blank=True) # EmailUserRO def __str__(self): return 'Organisation Access Group' @@ -745,19 +753,20 @@ class OrganisationRequestUserAction(UserAction): def log_action(cls, request, action, user): return cls.objects.create( request=request, - who=user, + who=user.id if user else None, what=str(action) ) - request = models.ForeignKey(OrganisationRequest,related_name='action_logs') + request = models.ForeignKey(OrganisationRequest,related_name='action_logs', on_delete=models.CASCADE) class Meta: app_label = 'mooringlicensing' class OrganisationRequestDeclinedDetails(models.Model): - request = models.ForeignKey(OrganisationRequest) - officer = models.ForeignKey(EmailUser, null=False) + request = models.ForeignKey(OrganisationRequest, on_delete=models.CASCADE) + # officer = models.ForeignKey(EmailUser, null=False) + officer = models.IntegerField(null=False, blank=False) # EmailUserRO reason = models.TextField(blank=True) class Meta: @@ -768,14 +777,14 @@ def update_organisation_request_comms_log_filename(instance, filename): class OrganisationRequestLogDocument(Document): - log_entry = models.ForeignKey('OrganisationRequestLogEntry',related_name='documents') + log_entry = models.ForeignKey('OrganisationRequestLogEntry',related_name='documents', on_delete=models.CASCADE) _file = models.FileField(upload_to=update_organisation_request_comms_log_filename, max_length=512) class Meta: app_label = 'mooringlicensing' class OrganisationRequestLogEntry(CommunicationsLogEntry): - request = models.ForeignKey(OrganisationRequest, related_name='comms_logs') + request = models.ForeignKey(OrganisationRequest, related_name='comms_logs', on_delete=models.CASCADE) def save(self, **kwargs): # save the request id if the reference not provided diff --git a/mooringlicensing/components/organisations/serializers.py b/mooringlicensing/components/organisations/serializers.py index e609799a4..5e13a2c9a 100755 --- a/mooringlicensing/components/organisations/serializers.py +++ b/mooringlicensing/components/organisations/serializers.py @@ -1,5 +1,6 @@ from django.conf import settings -from ledger.accounts.models import EmailUser,OrganisationAddress +# from ledger.accounts.models import EmailUser,OrganisationAddress +from ledger_api_client.ledger_models import EmailUserRO as EmailUser from mooringlicensing.components.organisations.models import ( Organisation, OrganisationContact, @@ -8,7 +9,7 @@ OrganisationAction, OrganisationRequestLogEntry, OrganisationLogEntry, - ledger_organisation, + # ledger_organisation, ) from mooringlicensing.components.organisations.utils import ( can_manage_org, @@ -25,26 +26,32 @@ class LedgerOrganisationSerializer(serializers.ModelSerializer): - class Meta: - model = ledger_organisation - fields = '__all__' - + pass + # TODO: implement this class -class LedgerOrganisationFilterSerializer(serializers.ModelSerializer): - #address = serializers.SerializerMethodField(read_only=True) - email = serializers.SerializerMethodField(read_only=True) + # class Meta: + # model = ledger_organisation + # fields = '__all__' - class Meta: - model = ledger_organisation - fields = ( - 'id', - 'name', - 'email', - #'address', - ) - def get_email(self, obj): - return '' +class LedgerOrganisationFilterSerializer(serializers.ModelSerializer): + pass + # TODO: implement this class + + # #address = serializers.SerializerMethodField(read_only=True) + # email = serializers.SerializerMethodField(read_only=True) + # + # class Meta: + # model = ledger_organisation + # fields = ( + # 'id', + # 'name', + # 'email', + # #'address', + # ) + # + # def get_email(self, obj): + # return '' class OrganisationCheckSerializer(serializers.Serializer): @@ -64,17 +71,21 @@ class OrganisationPinCheckSerializer(serializers.Serializer): pin1 = serializers.CharField() pin2 = serializers.CharField() + class OrganisationAddressSerializer(serializers.ModelSerializer): - class Meta: - model = OrganisationAddress - fields = ( - 'id', - 'line1', - 'locality', - 'state', - 'country', - 'postcode' - ) + pass + # TODO: implement this class + + # class Meta: + # model = OrganisationAddress + # fields = ( + # 'id', + # 'line1', + # 'locality', + # 'state', + # 'country', + # 'postcode' + # ) class DelegateSerializer(serializers.ModelSerializer): name = serializers.CharField(source='get_full_name') @@ -211,19 +222,18 @@ def get_is_admin(self, obj): class DetailsSerializer(serializers.ModelSerializer): - class Meta: - model = ledger_organisation - fields = ( - 'id', - 'name', - 'trading_name', - 'email', - 'abn', -# 'apply_application_discount', -# 'application_discount', -# 'apply_licence_discount', -# 'licence_discount', - ) + pass + # TODO: implement this class + + # class Meta: + # model = ledger_organisation + # fields = ( + # 'id', + # 'name', + # 'trading_name', + # 'email', + # 'abn', + # ) def validate(self, data): #import ipdb; ipdb.set_trace() diff --git a/mooringlicensing/components/organisations/signals.py b/mooringlicensing/components/organisations/signals.py index e17c0161e..36a3e6e64 100755 --- a/mooringlicensing/components/organisations/signals.py +++ b/mooringlicensing/components/organisations/signals.py @@ -3,7 +3,7 @@ from django.conf import settings from mooringlicensing.components.organisations.models import Organisation,OrganisationContact -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser class OrganisationListener(object): """ diff --git a/mooringlicensing/components/organisations/utils.py b/mooringlicensing/components/organisations/utils.py index e4b20d008..bd2998a05 100755 --- a/mooringlicensing/components/organisations/utils.py +++ b/mooringlicensing/components/organisations/utils.py @@ -4,7 +4,7 @@ def can_manage_org(organisation,user): from mooringlicensing.components.organisations.models import Organisation, OrganisationAccessGroup,UserDelegation - from ledger.accounts.models import EmailUser + # from ledger.accounts.models import EmailUser try: UserDelegation.objects.get(organisation=organisation,user=user) return can_admin_org(organisation, user) @@ -40,7 +40,7 @@ def is_last_admin(organisation, user): def can_admin_org(organisation,user): from mooringlicensing.components.organisations.models import Organisation, OrganisationAccessGroup,UserDelegation,OrganisationContact - from ledger.accounts.models import EmailUser + # from ledger.accounts.models import EmailUser try: org_contact=OrganisationContact.objects.get(organisation_id=organisation,email=user.email) # if org_contact.can_edit @@ -79,7 +79,7 @@ def can_approve(organisation, user): def is_consultant(organisation,user): from mooringlicensing.components.organisations.models import Organisation, OrganisationAccessGroup,UserDelegation,OrganisationContact - from ledger.accounts.models import EmailUser + # from ledger.accounts.models import EmailUser try: org_contact=OrganisationContact.objects.get(organisation_id=organisation,email=user.email) # if org_contact.can_edit diff --git a/mooringlicensing/components/payments_ml/api.py b/mooringlicensing/components/payments_ml/api.py index ab292105d..7e3dcef0b 100644 --- a/mooringlicensing/components/payments_ml/api.py +++ b/mooringlicensing/components/payments_ml/api.py @@ -12,14 +12,19 @@ from mooringlicensing.components.main.models import ApplicationType from mooringlicensing.components.payments_ml.models import FeeConstructor from mooringlicensing.components.payments_ml.serializers import FeeConstructorSerializer -from ledger.payments.models import Invoice, OracleAccountCode -from ledger.payments.bpoint.models import BpointTransaction, BpointToken -from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket, calculate_excl_gst -from ledger.payments.utils import oracle_parser_on_invoice, update_payments +# from ledger.payments.models import Invoice, OracleAccountCode +from ledger_api_client.ledger_models import Invoice +# from ledger.payments.bpoint.models import BpointTransaction, BpointToken +# from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket, calculate_excl_gst +from ledger_api_client.utils import create_basket_session, create_checkout_session, place_order_submission, calculate_excl_gst +# from ledger.payments.utils import oracle_parser_on_invoice, update_payments +from ledger_api_client.utils import update_payments + from mooringlicensing.components.proposals.models import Proposal from mooringlicensing import mooring_booking_utils as utils -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class GetSeasonsForDcvPermitDict(views.APIView): diff --git a/mooringlicensing/components/payments_ml/invoice_pdf.py b/mooringlicensing/components/payments_ml/invoice_pdf.py index 1a7aa3406..7ea04ce9c 100644 --- a/mooringlicensing/components/payments_ml/invoice_pdf.py +++ b/mooringlicensing/components/payments_ml/invoice_pdf.py @@ -1,7 +1,8 @@ import os from io import BytesIO -from oscar.templatetags.currency_filters import currency +# from oscar.templatetags.currency_filters import currency +from ledger_api_client.utils import currency from reportlab.lib import enums from reportlab.lib.pagesizes import A4 from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, Flowable, FrameBreak @@ -10,9 +11,11 @@ from reportlab.lib.units import inch from reportlab.lib import colors from django.conf import settings -from ledger.checkout.utils import calculate_excl_gst +# from ledger.checkout.utils import calculate_excl_gst +from ledger_api_client.utils import calculate_excl_gst from mooringlicensing.components.main.utils import to_local_tz from mooringlicensing.components.payments_ml.models import StickerActionFee, FeeItemStickerReplacement +from mooringlicensing.ledger_api_utils import get_invoice_payment_status DPAW_HEADER_LOGO = os.path.join(settings.PROJECT_DIR, 'payments','static', 'payments', 'img','dbca_logo.jpg') DPAW_HEADER_LOGO_SM = os.path.join(settings.PROJECT_DIR, 'payments','static', 'payments', 'img','dbca_logo_small.png') @@ -321,7 +324,8 @@ def _create_invoice(invoice_buffer, invoice, proposal): elements.append(t) elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 2)) # /Products Table - if invoice.payment_status != 'paid' and invoice.payment_status != 'over_paid': + invoice_payment_status = get_invoice_payment_status(invoice.id) + if invoice_payment_status != 'paid' and invoice_payment_status != 'over_paid': elements.append(Paragraph(settings.INVOICE_UNPAID_WARNING, styles['Left'])) elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 6)) @@ -339,15 +343,15 @@ def _create_invoice(invoice_buffer, invoice, proposal): return invoice_buffer # proposal needs to be nullable for Annual site fees -def create_invoice_pdf_bytes(filename, invoice, proposal=None): - invoice_buffer = BytesIO() - _create_invoice(invoice_buffer, invoice, proposal) - -# Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - invoice_buffer.close() - - return value +# def create_invoice_pdf_bytes(filename, invoice, proposal=None): +# invoice_buffer = BytesIO() +# _create_invoice(invoice_buffer, invoice, proposal) +# +# # Get the value of the BytesIO buffer +# value = invoice_buffer.getvalue() +# invoice_buffer.close() +# +# return value def create_annual_rental_fee_invoice(invoice_buffer, approval, invoice): @@ -420,7 +424,8 @@ def create_annual_rental_fee_invoice(invoice_buffer, approval, invoice): elements.append(t) elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 2)) # /Products Table - if invoice.payment_status != 'paid' and invoice.payment_status != 'over_paid': + invoice_payment_status = get_invoice_payment_status(invoice.id) + if invoice_payment_status != 'paid' and invoice_payment_status != 'over_paid': elements.append(Paragraph(settings.INVOICE_UNPAID_WARNING, styles['Left'])) elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 6)) diff --git a/mooringlicensing/components/payments_ml/models.py b/mooringlicensing/components/payments_ml/models.py index 4c0b9627b..8b943c583 100644 --- a/mooringlicensing/components/payments_ml/models.py +++ b/mooringlicensing/components/payments_ml/models.py @@ -1,5 +1,6 @@ import datetime import logging +import uuid from decimal import Decimal from math import ceil @@ -8,9 +9,11 @@ from django.core.exceptions import ValidationError from django.db import models from django.db.models import Min -from ledger.accounts.models import RevisionedMixin, EmailUser -from ledger.payments.invoice.models import Invoice -from ledger.settings_base import TIME_ZONE +# from ledger.accounts.models import RevisionedMixin, EmailUser +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice +# from ledger.settings_base import TIME_ZONE +from mooringlicensing.settings import TIME_ZONE from mooringlicensing import settings from mooringlicensing.components.main.models import ApplicationType, VesselSizeCategoryGroup, VesselSizeCategory @@ -18,7 +21,8 @@ AuthorisedUserApplication, VesselDetails, WaitingListApplication from smart_selects.db_fields import ChainedForeignKey -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class Payment(models.Model): @@ -102,13 +106,27 @@ class DcvAdmissionFee(Payment): dcv_admission = models.ForeignKey('DcvAdmission', on_delete=models.PROTECT, blank=True, null=True, related_name='dcv_admission_fees') payment_type = models.SmallIntegerField(choices=PAYMENT_TYPE_CHOICES, default=0) cost = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - created_by = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='created_by_dcv_admission_fee') + # created_by = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='created_by_dcv_admission_fee') + created_by = models.IntegerField(blank=True, null=True) invoice_reference = models.CharField(max_length=50, null=True, blank=True, default='') fee_items = models.ManyToManyField('FeeItem', related_name='dcv_admission_fees') + uuid = models.CharField(max_length=36, blank=True, null=True) def __str__(self): return 'DcvAdmission {} : Invoice {}'.format(self.dcv_admission, self.invoice_reference) + def save(self, *args, **kwargs): + logger.info(f"Saving DcvAdmissionFee: {self}.") + if not self.uuid: + logger.info("DcvAdmissionFee has no uuid") + self.uuid = uuid.uuid4() + logger.info( + f"DcvAdmissionFee assigned uuid: {self.uuid}", + ) + logger.info(f"Saving DcvAdmissionFee: {self}.") + super().save(*args, **kwargs) + logger.info("DcvAdmissionFee Saved.") + class Meta: app_label = 'mooringlicensing' @@ -128,13 +146,27 @@ class DcvPermitFee(Payment): dcv_permit = models.ForeignKey('DcvPermit', on_delete=models.PROTECT, blank=True, null=True, related_name='dcv_permit_fees') payment_type = models.SmallIntegerField(choices=PAYMENT_TYPE_CHOICES, default=0) cost = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - created_by = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='created_by_dcv_permit_fee') + # created_by = models.ForeignKey(EmailUser, on_delete=models.PROTECT, blank=True, null=True, related_name='created_by_dcv_permit_fee') + created_by = models.IntegerField(blank=True, null=True) invoice_reference = models.CharField(max_length=50, null=True, blank=True, default='') fee_items = models.ManyToManyField('FeeItem', related_name='dcv_permit_fees') + uuid = models.CharField(max_length=36, blank=True, null=True) def __str__(self): return 'DcvPermit {} : Invoice {}'.format(self.dcv_permit, self.invoice_reference) + def save(self, *args, **kwargs): + logger.info(f"Saving DcvPermitFee: {self}.") + if not self.uuid: + logger.info("DcvPermitFee has no uuid") + self.uuid = uuid.uuid4() + logger.info( + f"DcvPermitFee assigned uuid: {self.uuid}", + ) + logger.info(f"Saving DcvPermitFee: {self}.") + super().save(*args, **kwargs) + logger.info("DcvPermitFee Saved.") + class Meta: app_label = 'mooringlicensing' @@ -153,8 +185,10 @@ class StickerActionFee(Payment): payment_type = models.SmallIntegerField(choices=PAYMENT_TYPE_CHOICES, default=0) cost = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - created_by = models.ForeignKey(EmailUser,on_delete=models.PROTECT, blank=True, null=True,) + # created_by = models.ForeignKey(EmailUser,on_delete=models.PROTECT, blank=True, null=True,) + created_by = models.IntegerField(blank=True, null=True,) invoice_reference = models.CharField(max_length=50, null=True, blank=True, default='') + uuid = models.CharField(max_length=36, blank=True, null=True) def __str__(self): stickers = [] @@ -165,17 +199,32 @@ def __str__(self): class Meta: app_label = 'mooringlicensing' + def save(self, *args, **kwargs): + logger.info(f"Saving StickerActionFee: {self}.") + if not self.uuid: + logger.info("StickerActionFee has no uuid") + self.uuid = uuid.uuid4() + logger.info( + f"StickerActionFee assigned uuid: {self.uuid}", + ) + logger.info(f"Saving StickerActionFee: {self}.") + super().save(*args, **kwargs) + logger.info("StickerActionFee Saved.") + class FeeItemApplicationFee(models.Model): - fee_item = models.ForeignKey('FeeItem',) - application_fee = models.ForeignKey('ApplicationFee',) - vessel_details = models.ForeignKey(VesselDetails, null=True, blank=True) + fee_item = models.ForeignKey('FeeItem', on_delete=models.CASCADE) + application_fee = models.ForeignKey('ApplicationFee', on_delete=models.CASCADE) + vessel_details = models.ForeignKey(VesselDetails, null=True, blank=True, on_delete=models.SET_NULL) amount_to_be_paid = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True, default=None) amount_paid = models.DecimalField(max_digits=8, decimal_places=2, null=True, blank=True, default=None) class Meta: app_label = 'mooringlicensing' + def __str__(self): + return f'FeeItem: {self.fee_item}, ApplicationFee: {self.application_fee}, amount_to_be_paid: {self.amount_to_be_paid}, amount_paid: {self.amount_paid}' + @property def application_type(self): return self.fee_item.application_type @@ -196,10 +245,12 @@ class ApplicationFee(Payment): proposal = models.ForeignKey('Proposal', on_delete=models.PROTECT, blank=True, null=True, related_name='application_fees') payment_type = models.SmallIntegerField(choices=PAYMENT_TYPE_CHOICES, default=0) cost = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - created_by = models.ForeignKey(EmailUser,on_delete=models.PROTECT, blank=True, null=True,related_name='created_by_application_fee') + # created_by = models.ForeignKey(EmailUser,on_delete=models.PROTECT, blank=True, null=True,related_name='created_by_application_fee') + created_by = models.IntegerField(blank=True, null=True) invoice_reference = models.CharField(max_length=50, null=True, blank=True, default='') fee_items = models.ManyToManyField('FeeItem', related_name='application_fees', through='FeeItemApplicationFee') system_invoice = models.BooleanField(default=False) + uuid = models.CharField(max_length=36, blank=True, null=True) def __str__(self): return 'Application {} : Invoice {}'.format(self.proposal, self.invoice_reference) @@ -214,9 +265,21 @@ def fee_constructor(self): class Meta: app_label = 'mooringlicensing' + def save(self, *args, **kwargs): + logger.info(f"Saving ApplicationFee: {self}.") + if not self.uuid: + logger.info("ApplicationFee has no uuid") + self.uuid = uuid.uuid4() + logger.info( + f"ApplicationFee assigned uuid: {self.uuid}", + ) + logger.info(f"Saving ApplicationFee: {self}.") + super().save(*args, **kwargs) + logger.info("ApplicationFee Saved.") + class FeeSeason(models.Model): - application_type = models.ForeignKey(ApplicationType, null=True, blank=True, limit_choices_to={'fee_by_fee_constructor': True}) + application_type = models.ForeignKey(ApplicationType, null=True, blank=True, limit_choices_to={'fee_by_fee_constructor': True}, on_delete=models.SET_NULL) name = models.CharField(max_length=50, null=False, blank=False) def __str__(self): @@ -257,7 +320,7 @@ class Meta: class FeePeriod(models.Model): - fee_season = models.ForeignKey(FeeSeason, null=True, blank=True, related_name='fee_periods') + fee_season = models.ForeignKey(FeeSeason, null=True, blank=True, related_name='fee_periods', on_delete=models.SET_NULL) name = models.CharField(max_length=50, null=True, blank=True, default='') start_date = models.DateField(null=True, blank=True) @@ -276,7 +339,7 @@ class Meta: class FeeConstructor(models.Model): - application_type = models.ForeignKey(ApplicationType, null=False, blank=False, limit_choices_to={'fee_by_fee_constructor': True}) + application_type = models.ForeignKey(ApplicationType, null=False, blank=False, limit_choices_to={'fee_by_fee_constructor': True}, on_delete=models.PROTECT) fee_season = ChainedForeignKey(FeeSeason, chained_field='application_type', chained_model_field='application_type', @@ -286,7 +349,7 @@ class FeeConstructor(models.Model): null=True, blank=True, related_name='fee_constructors') - vessel_size_category_group = models.ForeignKey(VesselSizeCategoryGroup, null=False, blank=False, related_name='fee_constructors') + vessel_size_category_group = models.ForeignKey(VesselSizeCategoryGroup, null=False, blank=False, related_name='fee_constructors', on_delete=models.PROTECT) incur_gst = models.BooleanField(default=True) enabled = models.BooleanField(default=True) @@ -360,7 +423,8 @@ def validate_unique(self, exclude=None): @classmethod def get_current_and_future_fee_constructors_by_application_type_and_date(cls, application_type, target_date=datetime.datetime.now(pytz.timezone(TIME_ZONE)).date()): - logger = logging.getLogger('mooringlicensing') + # logger = logging.getLogger('mooringlicensing') + logger = logging.getLogger(__name__) # Select a fee_constructor object which has been started most recently for the application_type try: @@ -387,7 +451,8 @@ def get_current_and_future_fee_constructors_by_application_type_and_date(cls, ap @classmethod def get_fee_constructor_by_application_type_and_season(cls, application_type, fee_season): - logger = logging.getLogger('mooringlicensing') + # logger = logging.getLogger('mooringlicensing') + logger = logging.getLogger(__name__) try: fee_constructor_qs = cls.objects.filter(application_type=application_type, fee_season=fee_season, enabled=True) @@ -408,7 +473,8 @@ def get_fee_constructor_by_application_type_and_season(cls, application_type, fe @classmethod def get_fee_constructor_by_application_type_and_date(cls, application_type, target_date=datetime.datetime.now(pytz.timezone(TIME_ZONE)).date()): - logger = logging.getLogger('mooringlicensing') + # logger = logging.getLogger('mooringlicensing') + logger = logging.getLogger(__name__) # Select a fee_constructor object which has been started most recently for the application_type try: @@ -528,15 +594,15 @@ class Meta: class FeeItem(models.Model): - fee_constructor = models.ForeignKey(FeeConstructor, null=True, blank=True) - fee_period = models.ForeignKey(FeePeriod, null=True, blank=True) - vessel_size_category = models.ForeignKey(VesselSizeCategory, null=True, blank=True) - proposal_type = models.ForeignKey('ProposalType', null=True, blank=True) + fee_constructor = models.ForeignKey(FeeConstructor, null=True, blank=True, on_delete=models.SET_NULL) + fee_period = models.ForeignKey(FeePeriod, null=True, blank=True, on_delete=models.SET_NULL) + vessel_size_category = models.ForeignKey(VesselSizeCategory, null=True, blank=True, on_delete=models.SET_NULL) + proposal_type = models.ForeignKey('ProposalType', null=True, blank=True, on_delete=models.SET_NULL) amount = models.DecimalField(max_digits=8, decimal_places=2, default='0.00', help_text='$') incremental_amount = models.BooleanField(default=False, help_text='When ticked, The amount will be the increase in the rate per meter') # When False, the 'amount' value is the price for this item. When True, the 'amount' is the price per meter. # For DcvAdmission - age_group = models.ForeignKey('AgeGroup', null=True, blank=True) - admission_type = models.ForeignKey('AdmissionType', null=True, blank=True) + age_group = models.ForeignKey('AgeGroup', null=True, blank=True, on_delete=models.SET_NULL) + admission_type = models.ForeignKey('AdmissionType', null=True, blank=True, on_delete=models.SET_NULL) def __str__(self): return '${}: {}, {}, {}, {}'.format(self.amount, self.fee_constructor.application_type, self.fee_period, self.vessel_size_category, self.proposal_type) @@ -595,7 +661,7 @@ class Meta: class OracleCodeItem(models.Model): - application_type = models.ForeignKey(ApplicationType, blank=True, null=True, related_name='oracle_code_items') + application_type = models.ForeignKey(ApplicationType, blank=True, null=True, related_name='oracle_code_items', on_delete=models.SET_NULL) value = models.CharField(max_length=50, null=True, blank=True, default='T1 EXEMPT') date_of_enforcement = models.DateField(blank=True, null=True) @@ -603,6 +669,17 @@ class Meta: app_label = 'mooringlicensing' +class FeeCalculation(models.Model): + ''' + This model is used to store the details of fee calculation. No relations to other tables, but has a uuid field to link to another table. + ''' + uuid = models.CharField(max_length=36, blank=True, null=True) + data = models.JSONField(blank=True, null=True) + + class Meta: + app_label = 'mooringlicensing' + + import reversion #reversion.register(DcvAdmissionFee, follow=[]) #reversion.register(DcvPermitFee, follow=[]) diff --git a/mooringlicensing/components/payments_ml/reports.py b/mooringlicensing/components/payments_ml/reports.py index 342ce68b7..0e75c93a6 100644 --- a/mooringlicensing/components/payments_ml/reports.py +++ b/mooringlicensing/components/payments_ml/reports.py @@ -1,15 +1,16 @@ import csv import pytz -from six.moves import StringIO +# from six.moves import StringIO from django.utils import timezone from django.db.models.query_utils import Q -from ledger.payments.models import CashTransaction, BpointTransaction, BpayTransaction,Invoice -from ledger.settings_base import TIME_ZONE +# from ledger.payments.models import CashTransaction, BpointTransaction, BpayTransaction,Invoice +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE from mooringlicensing.components.main.models import ApplicationType from mooringlicensing.components.payments_ml.models import ApplicationFee, DcvAdmissionFee, DcvPermitFee from mooringlicensing.components.compliances.models import Compliance -from mooringlicensing.settings import PAYMENT_SYSTEM_PREFIX +from mooringlicensing.settings import LEDGER_SYSTEM_ID def booking_bpoint_settlement_report(_date): @@ -32,7 +33,7 @@ def booking_bpoint_settlement_report(_date): bpoint.extend([x for x in BpointTransaction.objects.filter( Q(created__date=_date), Q(response_code=0), - Q(crn1__startswith=PAYMENT_SYSTEM_PREFIX), + Q(crn1__startswith=LEDGER_SYSTEM_ID), ).exclude(crn1__endswith='_test')]) for b in bpoint: @@ -94,7 +95,7 @@ def booking_bpoint_settlement_report(_date): invoice.amount, invoice.reference]) elif compliance: - submitter = compliance.approval.submitter if compliance.approval else compliance.proposal.submitter + submitter = compliance.approval.submitter_obj if compliance.approval else compliance.proposal.submitter_obj b_name = u'{}'.format(submitter) created = timezone.localtime(b.created, pytz.timezone('Australia/Perth')) settlement_date = b.settlement_date.strftime('%d/%m/%Y') if b.settlement_date else '' diff --git a/mooringlicensing/components/payments_ml/signals.py b/mooringlicensing/components/payments_ml/signals.py index a022f9909..ae8366e6b 100644 --- a/mooringlicensing/components/payments_ml/signals.py +++ b/mooringlicensing/components/payments_ml/signals.py @@ -2,11 +2,12 @@ from django.db.models.signals import post_save from django.dispatch import receiver from mooringlicensing.components.payments_ml.models import FeeConstructor, ApplicationFee -from ledger.payments.models import CashTransaction +# from ledger.payments.models import CashTransaction from mooringlicensing.components.proposals.models import Proposal -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class FeeConstructorListener(object): @@ -21,16 +22,19 @@ class InvoiceListerner(object): ''' Caution: Once the ledger is segregated, this function doesn't work ''' - - @staticmethod - @receiver(post_save, sender=CashTransaction) - def _post_save(sender, instance, **kwargs): - # Expecting this function is called after 'Record Payment' - if instance.invoice and instance.invoice.payment_status in ('paid', 'over_paid',): - application_fee = ApplicationFee.objects.get(invoice_reference=instance.invoice.reference) - - # Update proposal status - if application_fee.proposal: - if application_fee.proposal.processing_status == Proposal.PROCESSING_STATUS_AWAITING_PAYMENT: - application_fee.proposal.processing_status = Proposal.PROCESSING_STATUS_WITH_ASSESSOR - application_fee.proposal.save() + pass + + # TODO: Update the proposal status when cash payment + + # @staticmethod + # @receiver(post_save, sender=CashTransaction) + # def _post_save(sender, instance, **kwargs): + # # Expecting this function is called after 'Record Payment' + # if instance.invoice and instance.invoice.payment_status in ('paid', 'over_paid',): + # application_fee = ApplicationFee.objects.get(invoice_reference=instance.invoice.reference) + # + # # Update proposal status + # if application_fee.proposal: + # if application_fee.proposal.processing_status == Proposal.PROCESSING_STATUS_AWAITING_PAYMENT: + # application_fee.proposal.processing_status = Proposal.PROCESSING_STATUS_WITH_ASSESSOR + # application_fee.proposal.save() diff --git a/mooringlicensing/components/payments_ml/utils.py b/mooringlicensing/components/payments_ml/utils.py index 70b1cb4d0..013a379da 100644 --- a/mooringlicensing/components/payments_ml/utils.py +++ b/mooringlicensing/components/payments_ml/utils.py @@ -1,177 +1,76 @@ import logging -from _pydecimal import Decimal -from datetime import datetime +# from _pydecimal import Decimal +import decimal import pytz -from django.http import HttpResponse, HttpResponseRedirect +from django.http import HttpResponse from django.urls import reverse -from ledger.checkout.utils import create_basket_session, create_checkout_session, calculate_excl_gst, \ - use_existing_basket_from_invoice -from ledger.settings_base import TIME_ZONE - +# from ledger.checkout.utils import create_basket_session, create_checkout_session, calculate_excl_gst, use_existing_basket_from_invoice +from ledger_api_client.utils import create_basket_session, create_checkout_session, calculate_excl_gst, use_existing_basket_from_invoice +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import * from mooringlicensing import settings -from mooringlicensing.components.approvals.models import DcvPermit, AgeGroup, AdmissionType -from mooringlicensing.components.main.models import ApplicationType -from mooringlicensing.components.payments_ml.models import ApplicationFee, FeeConstructor, DcvPermitFee, \ +from mooringlicensing.components.payments_ml.models import ApplicationFee, DcvPermitFee, \ DcvAdmissionFee, StickerActionFee #test -from mooringlicensing.components.proposals.models import Proposal, AuthorisedUserApplication, MooringLicenceApplication, \ - AnnualAdmissionApplication, ProposalType -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) -def checkout(request, email_user, lines, return_url_ns='public_payment_success', return_preload_url_ns='public_payment_success', invoice_text=None, vouchers=[], proxy=False): +def checkout(request, email_user, lines, return_url, return_preload_url, booking_reference, invoice_text=None, vouchers=[], proxy=False,): basket_params = { - 'products': lines, + 'products': make_serializable(lines), 'vouchers': vouchers, 'system': settings.PAYMENT_SYSTEM_ID, 'custom_basket': True, + 'tax_override': True, + 'booking_reference': booking_reference, } - basket, basket_hash = create_basket_session(request, basket_params) + # basket, basket_hash = create_basket_session(request, basket_params) + # basket, basket_hash = create_basket_session(request, request.user.id, basket_params) + basket_hash = create_basket_session(request, request.user.id, basket_params) checkout_params = { 'system': settings.PAYMENT_SYSTEM_ID, - 'fallback_url': request.build_absolute_uri('/'), # 'http://mooring-ria-jm.dbca.wa.gov.au/' - 'return_url': request.build_absolute_uri(reverse(return_url_ns)), # 'http://mooring-ria-jm.dbca.wa.gov.au/success/' - 'return_preload_url': request.build_absolute_uri(reverse(return_url_ns)), # 'http://mooring-ria-jm.dbca.wa.gov.au/success/' + 'fallback_url': request.build_absolute_uri('/'), + # 'return_url': request.build_absolute_uri(reverse(return_url_ns)), + # 'return_preload_url': request.build_absolute_uri(reverse(return_preload_url_ns)), + 'return_url': return_url, + 'return_preload_url': return_preload_url, 'force_redirect': True, - 'invoice_text': invoice_text, # 'Reservation for Jawaid Mushtaq from 2019-05-17 to 2019-05-19 at RIA 005' + 'invoice_text': invoice_text, # 'Reservation for Jawaid Mushtaq from 2019-05-17 to 2019-05-19 at RIA 005' + # 'basket_owner': email_user, + 'basket_owner': email_user.id, + 'session_type': 'ledger_api', } - if proxy or request.user.is_anonymous(): + # if proxy or request.user.is_anonymous(): + if proxy or request.user.is_anonymous: + # checkout_params['basket_owner'] = email_user.id checkout_params['basket_owner'] = email_user.id - create_checkout_session(request, checkout_params) - response = HttpResponseRedirect(reverse('checkout:index')) + # response = HttpResponseRedirect(reverse('checkout:index')) + # response = HttpResponseRedirect(reverse('ledgergw-payment-details')) # inject the current basket into the redirect response cookies # or else, anonymous users will be directionless - response.set_cookie( - settings.OSCAR_BASKET_COOKIE_OPEN, basket_hash, - max_age=settings.OSCAR_BASKET_COOKIE_LIFETIME, - secure=settings.OSCAR_BASKET_COOKIE_SECURE, httponly=True - ) + # response.set_cookie( + # settings.OSCAR_BASKET_COOKIE_OPEN, basket_hash, + # max_age=settings.OSCAR_BASKET_COOKIE_LIFETIME, + # secure=settings.OSCAR_BASKET_COOKIE_SECURE, + # httponly=True, + # ) + response = HttpResponse( + " Redirecting please wait: " + reverse('ledgergw-payment-details') + "" + ) return response -def create_fee_lines_for_dcv_admission(dcv_admission, invoice_text=None, vouchers=[], internal=False): - db_processes_after_success = {} - - target_datetime = datetime.now(pytz.timezone(TIME_ZONE)) - target_date = target_datetime.date() - target_datetime_str = target_datetime.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d/%m/%Y %I:%M %p') - - db_processes_after_success['datetime_for_calculating_fee'] = target_datetime.__str__() - - application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_DCV_ADMISSION['code']) - vessel_length = 1 # any number greater than 0 - proposal_type = None - oracle_code = application_type.get_oracle_code_by_date(target_date=target_date) - - line_items = [] - for dcv_admission_arrival in dcv_admission.dcv_admission_arrivals.all(): - fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_date(application_type, dcv_admission_arrival.arrival_date) - - if not fee_constructor: - raise Exception('FeeConstructor object for the ApplicationType: {} and the Season: {}'.format(application_type, dcv_admission_arrival.arrival_date)) - - fee_items = [] - number_of_people_str = [] - total_amount = 0 - - for number_of_people in dcv_admission_arrival.numberofpeople_set.all(): - if number_of_people.number: - # When more than 1 people, - fee_item = fee_constructor.get_fee_item(vessel_length, proposal_type, dcv_admission_arrival.arrival_date, number_of_people.age_group, number_of_people.admission_type) - fee_item.number_of_people = number_of_people.number - fee_items.append(fee_item) - number_of_people_str.append('[{}-{}: {}]'.format(number_of_people.age_group, number_of_people.admission_type, number_of_people.number)) - total_amount += fee_item.get_absolute_amount() * number_of_people.number - - line_item = { - 'ledger_description': '{} Fee: {} (Arrival: {}, Private: {}, {})'.format( - fee_constructor.application_type.description, - dcv_admission.lodgement_number, - dcv_admission_arrival.arrival_date, - dcv_admission_arrival.private_visit, - ', '.join(number_of_people_str), - ), - 'oracle_code': oracle_code, - 'price_incl_tax': total_amount, - 'price_excl_tax': calculate_excl_gst(total_amount) if fee_constructor.incur_gst else total_amount, - 'quantity': 1, - } - line_items.append(line_item) - - logger.info('{}'.format(line_items)) - - return line_items, db_processes_after_success - - -def create_fee_lines(instance, invoice_text=None, vouchers=[], internal=False): - """ Create the ledger lines - line item for application fee sent to payment system """ - - # Any changes to the DB should be made after the success of payment process - db_processes_after_success = {} - - if isinstance(instance, Proposal): - application_type = instance.application_type - vessel_length = instance.vessel_details.vessel_applicable_length - proposal_type = instance.proposal_type - elif isinstance(instance, DcvPermit): - application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_DCV_PERMIT['code']) - vessel_length = 1 # any number greater than 0 - proposal_type = None - - target_datetime = datetime.now(pytz.timezone(TIME_ZONE)) - target_date = target_datetime.date() - target_datetime_str = target_datetime.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d/%m/%Y %I:%M %p') - - # Retrieve FeeItem object from FeeConstructor object - if isinstance(instance, Proposal): - fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_date(application_type, target_date) - if not fee_constructor: - # Fees have not been configured for this application type and date - raise Exception('FeeConstructor object for the ApplicationType: {} not found for the date: {}'.format(application_type, target_date)) - elif isinstance(instance, DcvPermit): - fee_constructor = FeeConstructor.get_fee_constructor_by_application_type_and_season(application_type, instance.fee_season) - if not fee_constructor: - # Fees have not been configured for this application type and date - raise Exception('FeeConstructor object for the ApplicationType: {} and the Season: {}'.format(application_type, instance.fee_season)) - else: - raise Exception('Something went wrong when calculating the fee') - - fee_item = fee_constructor.get_fee_item(vessel_length, proposal_type, target_date) - - db_processes_after_success['fee_item_id'] = fee_item.id - db_processes_after_success['fee_constructor_id'] = fee_constructor.id - db_processes_after_success['season_start_date'] = fee_constructor.fee_season.start_date.__str__() - db_processes_after_success['season_end_date'] = fee_constructor.fee_season.end_date.__str__() - db_processes_after_success['datetime_for_calculating_fee'] = target_datetime.__str__() - - line_items = [ - { - 'ledger_description': '{} Fee: {} (Season: {} to {}) @{}'.format( - fee_constructor.application_type.description, - instance.lodgement_number, - fee_constructor.fee_season.start_date.strftime('%d/%m/%Y'), - fee_constructor.fee_season.end_date.strftime('%d/%m/%Y'), - target_datetime_str, - ), - # 'oracle_code': application_type.oracle_code, - 'oracle_code': ApplicationType.get_current_oracle_code_by_application(application_type.code), - 'price_incl_tax': fee_item.amount, - 'price_excl_tax': calculate_excl_gst(fee_item.amount) if fee_constructor.incur_gst else fee_item.amount, - 'quantity': 1, - }, - ] - - logger.info('{}'.format(line_items)) - - return line_items, db_processes_after_success def generate_line_item(application_type, fee_amount_adjusted, fee_constructor, instance, target_datetime, v_rego_no=''): @@ -189,19 +88,20 @@ def generate_line_item(application_type, fee_amount_adjusted, fee_constructor, i proposal_type_text = '{}'.format(instance.proposal_type.description) if hasattr(instance, 'proposal_type') else '' return { - 'ledger_description': '{} fee ({}, {}): {} (Season: {} to {}) @{}'.format( + # 'ledger_description': '{} fee ({}, {}): {} (Season: {} to {}) @{}'.format( + 'ledger_description': '{} fee ({}, {}): {} @{}'.format( # fee_constructor.application_type.description, application_type_display, proposal_type_text, vessel_rego_no, instance.lodgement_number, - fee_constructor.fee_season.start_date.strftime('%d/%m/%Y'), - fee_constructor.fee_season.end_date.strftime('%d/%m/%Y'), + # fee_constructor.fee_season.start_date.strftime('%d/%m/%Y'), + # fee_constructor.fee_season.end_date.strftime('%d/%m/%Y'), target_datetime_str, ), 'oracle_code': application_type.get_oracle_code_by_date(target_datetime.date()), - 'price_incl_tax': fee_amount_adjusted, - 'price_excl_tax': calculate_excl_gst(fee_amount_adjusted) if fee_constructor.incur_gst else fee_amount_adjusted, + 'price_incl_tax': float(fee_amount_adjusted), + 'price_excl_tax': float(calculate_excl_gst(fee_amount_adjusted)) if fee_constructor.incur_gst else float(fee_amount_adjusted), 'quantity': 1, } @@ -324,9 +224,9 @@ def delete_session_dcv_admission_invoice(session): def make_serializable(line_items): for line in line_items: for key in line: - if isinstance(line[key], Decimal): + if isinstance(line[key], decimal.Decimal): # Convert Decimal to str - line[key] = str(line[key]) + line[key] = float(line[key]) return line_items @@ -375,3 +275,4 @@ def oracle_integration(date,override): #system = '0517' oracle_codes = oracle_parser(date, settings.PAYMENT_SYSTEM_ID, 'Disturbance Approval System', override=override) + diff --git a/mooringlicensing/components/payments_ml/views.py b/mooringlicensing/components/payments_ml/views.py index 53ffd60b0..cf5029513 100644 --- a/mooringlicensing/components/payments_ml/views.py +++ b/mooringlicensing/components/payments_ml/views.py @@ -1,29 +1,52 @@ import datetime import logging -from ledger.checkout.utils import calculate_excl_gst + +import ledger_api_client.utils +# from ledger.checkout.utils import calculate_excl_gst import pytz import json -from ledger.settings_base import TIME_ZONE + +from rest_framework.views import APIView + +from mooringlicensing.ledger_api_utils import retrieve_email_userro, get_invoice_payment_status, get_invoice_url +# from ledger.settings_base import TIME_ZONE +from mooringlicensing.settings import TIME_ZONE from decimal import * -from ledger.payments.bpoint.models import BpointTransaction, BpointToken +# from ledger.payments.bpoint.models import BpointTransaction from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin from mooringlicensing.components.main.models import ApplicationType -from mooringlicensing.components.payments_ml.invoice_pdf import create_invoice_pdf_bytes +# from mooringlicensing.components.payments_ml.invoice_pdf import create_invoice_pdf_bytes from rest_framework.response import Response import dateutil.parser from django.db import transaction from django.http import HttpResponse, HttpResponseRedirect -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import ValidationError from django.shortcuts import get_object_or_404, render, redirect from django.views import View from django.views.generic import TemplateView -from ledger.basket.models import Basket -from ledger.payments.invoice.models import Invoice -from ledger.payments.utils import update_payments +# <<<<<<< HEAD +# from ledger.basket.models import Basket +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import Invoice, Basket +# from ledger.payments.utils import update_payments +from ledger_api_client.utils import update_payments, calculate_excl_gst +# from oscar.apps.order.models import Order +from ledger_api_client.order import Order +# ||||||| 741adce2 +# from ledger.basket.models import Basket +# from ledger.payments.invoice.models import Invoice +# from ledger.payments.utils import update_payments +# from oscar.apps.order.models import Order +# ======= +# from ledger.basket.models import Basket +# from ledger.payments.invoice.models import Invoice +# from ledger.payments.utils import update_payments #from oscar.apps.order.models import Order -from ledger.order.models import Order +# from ledger.order.models import Order +# >>>>>>> main from mooringlicensing import settings from mooringlicensing.components.approvals.models import DcvPermit, DcvAdmission, Approval, StickerActionDetail, Sticker @@ -31,19 +54,22 @@ from mooringlicensing.components.approvals.email import send_dcv_permit_mail, send_dcv_admission_mail, \ send_sticker_replacement_email from mooringlicensing.components.payments_ml.models import ApplicationFee, DcvPermitFee, \ - DcvAdmissionFee, FeeItem, StickerActionFee, FeeItemStickerReplacement, FeeItemApplicationFee -from mooringlicensing.components.payments_ml.utils import checkout, create_fee_lines, set_session_application_invoice, \ + DcvAdmissionFee, FeeItem, StickerActionFee, FeeItemStickerReplacement, FeeItemApplicationFee, FeeCalculation +from mooringlicensing.components.payments_ml.utils import checkout, set_session_application_invoice, \ get_session_application_invoice, delete_session_application_invoice, set_session_dcv_permit_invoice, \ get_session_dcv_permit_invoice, delete_session_dcv_permit_invoice, set_session_dcv_admission_invoice, \ - create_fee_lines_for_dcv_admission, get_session_dcv_admission_invoice, delete_session_dcv_admission_invoice, \ + get_session_dcv_admission_invoice, delete_session_dcv_admission_invoice, \ checkout_existing_invoice, set_session_sticker_action_invoice, get_session_sticker_action_invoice, \ - delete_session_sticker_action_invoice, ItemNotSetInSessionException + delete_session_sticker_action_invoice from mooringlicensing.components.proposals.models import Proposal, ProposalUserAction, \ AuthorisedUserApplication, MooringLicenceApplication, WaitingListApplication, AnnualAdmissionApplication, \ VesselDetails -from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PAYMENT_SYSTEM_PREFIX +from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, LEDGER_SYSTEM_ID +from rest_framework import status +from ledger_api_client import utils -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class DcvAdmissionFeeView(TemplateView): @@ -59,15 +85,21 @@ def post(self, request, *args, **kwargs): with transaction.atomic(): set_session_dcv_admission_invoice(request.session, dcv_admission_fee) - lines, db_processes_after_success = create_fee_lines_for_dcv_admission(dcv_admission) + lines, db_processes = dcv_admission.create_fee_lines() - request.session['db_processes'] = db_processes_after_success + # request.session['db_processes'] = db_processes_after_success + new_fee_calculation = FeeCalculation.objects.create(uuid=dcv_admission_fee.uuid, data=db_processes) checkout_response = checkout( request, - dcv_admission.submitter, + dcv_admission.submitter_obj, lines, - return_url_ns='dcv_admission_fee_success', - return_preload_url_ns='dcv_admission_fee_success', + # return_url_ns='dcv_admission_fee_success', + # return_preload_url_ns='dcv_admission_fee_success', + # request.build_absolute_uri(reverse('dcv_admission_fee_success')), + # request.build_absolute_uri(reverse('dcv_admission_fee_success')), + return_url=request.build_absolute_uri(reverse('dcv_admission_fee_success', kwargs={"uuid": dcv_admission_fee.uuid})), + return_preload_url=request.build_absolute_uri(reverse("dcv_admission_fee_success_preload", kwargs={"uuid": dcv_admission_fee.uuid})), + booking_reference=str(dcv_admission_fee.uuid), invoice_text='DCV Admission Fee', ) @@ -88,22 +120,28 @@ def get_object(self): def post(self, request, *args, **kwargs): dcv_permit = self.get_object() - created_by = None if request.user.is_anonymous() else request.user + created_by = None if request.user.is_anonymous else request.user.id dcv_permit_fee = DcvPermitFee.objects.create(dcv_permit=dcv_permit, created_by=created_by, payment_type=DcvPermitFee.PAYMENT_TYPE_TEMPORARY) try: with transaction.atomic(): - set_session_dcv_permit_invoice(request.session, dcv_permit_fee) + # set_session_dcv_permit_invoice(request.session, dcv_permit_fee) - lines, db_processes_after_success = create_fee_lines(dcv_permit) + # lines, db_processes_after_success = create_fee_lines(dcv_permit) + lines, db_processes = dcv_permit.create_fee_lines() + + # request.session['db_processes'] = db_processes_after_success + new_fee_calculation = FeeCalculation.objects.create(uuid=dcv_permit_fee.uuid, data=db_processes) - request.session['db_processes'] = db_processes_after_success checkout_response = checkout( request, - dcv_permit.submitter, + dcv_permit.submitter_obj, lines, - return_url_ns='dcv_permit_fee_success', - return_preload_url_ns='dcv_permit_fee_success', + # request.build_absolute_uri(reverse('dcv_permit_fee_success')), # return url + # request.build_absolute_uri(reverse('dcv_permit_fee_success')), # return preload url + return_url=request.build_absolute_uri(reverse('dcv_permit_fee_success', kwargs={"uuid": dcv_permit_fee.uuid})), + return_preload_url=request.build_absolute_uri(reverse("dcv_permit_fee_success_preload", kwargs={"uuid": dcv_permit_fee.uuid})), + booking_reference=str(dcv_permit_fee.uuid), invoice_text='DCV Permit Fee', ) @@ -134,33 +172,37 @@ def post(self, request, *args, **kwargs): context = { 'proposal': proposal, - 'submitter': proposal.submitter, + 'submitter': proposal.submitter_obj, } return render(request, self.template_name, context) @staticmethod def send_confirmation_mail(proposal, request): # Send invoice - to_email_addresses = proposal.submitter.email + to_email_addresses = proposal.submitter_obj.email email_data = send_application_submit_confirmation_email(request, proposal, [to_email_addresses, ]) -class ApplicationFeeExistingView(TemplateView): +# class ApplicationFeeExistingView(TemplateView): +class ApplicationFeeExistingView(APIView): def get_object(self): - return get_object_or_404(Proposal, id=self.kwargs['proposal_pk']) + return get_object_or_404(Invoice, reference=self.kwargs['invoice_reference']) def get(self, request, *args, **kwargs): - proposal = self.get_object() - application_fee = proposal.get_main_application_fee() + invoice = self.get_object() + # application_fee = proposal.get_main_application_fee() + application_fee = ApplicationFee.objects.get(invoice_reference=invoice.reference) - if application_fee.paid: - return redirect('application_fee_already_paid', proposal_pk=proposal.id) + # if application_fee.paid: + # return redirect('application_fee_already_paid', proposal_pk=proposal.id) + if get_invoice_payment_status(invoice.id) in ['paid', 'over_paid',]: + return redirect('application_fee_already_paid', proposal_pk=application_fee.proposal.id) try: with transaction.atomic(): - set_session_application_invoice(request.session, application_fee) - invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - + # set_session_application_invoice(request.session, application_fee) + # invoice = Invoice.objects.get(reference=application_fee.invoice_reference) + # db_processes = { 'for_existing_invoice': True, 'fee_item_application_fee_ids': [], @@ -168,20 +210,30 @@ def get(self, request, *args, **kwargs): fee_item_application_fees = FeeItemApplicationFee.objects.filter(application_fee=application_fee) for fee_item_application_fee in fee_item_application_fees: db_processes['fee_item_application_fee_ids'].append(fee_item_application_fee.id) - request.session['db_processes'] = db_processes - - checkout_response = checkout_existing_invoice( - request, - invoice, - return_url_ns='fee_success', - ) - logger.info('{} built payment line item {} for Application Fee and handing over to payment gateway'.format( - 'User {} with id {}'.format( - request.user.get_full_name(), request.user.id - ), application_fee.proposal.lodgement_number - )) - return checkout_response + new_fee_calculation = FeeCalculation.objects.create(uuid=application_fee.uuid, data=db_processes) + + # request.session['db_processes'] = db_processes + # + # checkout_response = checkout_existing_invoice( + # request, + # invoice, + # return_url_ns='fee_success', + # ) + + # logger.info('{} built payment line item {} for Application Fee and handing over to payment gateway'.format( + # 'User {} with id {}'.format( + # request.user.get_full_name(), request.user.id + # ), application_fee.proposal.lodgement_number + # )) + # return checkout_response + + # return_url = 'test1' + # fallback_url = 'test2' + return_url = request.build_absolute_uri(reverse('fee_success', kwargs={"uuid": application_fee.uuid})) + fallback_url = request.build_absolute_uri(reverse("external")) + payment_session = ledger_api_client.utils.generate_payment_session(request, invoice.reference, return_url, fallback_url) + return HttpResponseRedirect(payment_session['payment_url']) except Exception as e: logger.error('Error Creating Application Fee: {}'.format(e)) @@ -208,7 +260,7 @@ def post(self, request, *args, **kwargs): # raise forms.ValidationError('Validation error') # 2. Store detais in the session - sticker_action_fee = StickerActionFee.objects.create(created_by=request.user, payment_type=StickerActionFee.PAYMENT_TYPE_TEMPORARY) + sticker_action_fee = StickerActionFee.objects.create(created_by=request.user.id, payment_type=StickerActionFee.PAYMENT_TYPE_TEMPORARY) current_datetime = datetime.datetime.now(pytz.timezone(TIME_ZONE)) try: @@ -216,7 +268,7 @@ def post(self, request, *args, **kwargs): sticker_action_details = StickerActionDetail.objects.filter(id__in=ids) sticker_action_details.update(sticker_action_fee=sticker_action_fee) - set_session_sticker_action_invoice(request.session, sticker_action_fee) + # set_session_sticker_action_invoice(request.session, sticker_action_fee) target_datetime_str = current_datetime.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d/%m/%Y %I:%M %p') application_type = ApplicationType.objects.get(code=settings.APPLICATION_TYPE_REPLACEMENT_STICKER['code']) @@ -228,7 +280,7 @@ def post(self, request, *args, **kwargs): 'ledger_description': 'Sticker Replacement Fee, sticker: {} @{}'.format(sticker_action_detail.sticker, target_datetime_str), 'oracle_code': application_type.get_oracle_code_by_date(current_datetime.date()), 'price_incl_tax': fee_item.amount, - 'price_excl_tax': calculate_excl_gst(fee_item.amount) if fee_item.incur_gst else fee_item.amount, + 'price_excl_tax': ledger_api_client.utils.calculate_excl_gst(fee_item.amount) if fee_item.incur_gst else fee_item.amount, 'quantity': 1, } lines.append(line) @@ -237,8 +289,11 @@ def post(self, request, *args, **kwargs): request, request.user, lines, - return_url_ns='sticker_replacement_fee_success', - return_preload_url_ns='sticker_replacement_fee_success', + # request.build_absolute_uri(reverse('sticker_replacement_fee_success')), + # request.build_absolute_uri(reverse('sticker_replacement_fee_success')), + return_url=request.build_absolute_uri(reverse('sticker_replacement_fee_success', kwargs={"uuid": sticker_action_fee.uuid})), + return_preload_url=request.build_absolute_uri(reverse("sticker_replacement_fee_success_preload", kwargs={"uuid": sticker_action_fee.uuid})), + booking_reference=str(sticker_action_fee.uuid), invoice_text='{}'.format(application_type.description), ) @@ -251,6 +306,56 @@ def post(self, request, *args, **kwargs): sticker_action_fee.delete() raise +class StickerReplacementFeeSuccessViewPreload(APIView): + + def get(self, request, uuid, format=None): + logger.info(f'{StickerReplacementFeeSuccessViewPreload.__name__} get method is called.') + + if uuid: # and invoice_reference: + if not StickerActionFee.objects.filter(uuid=uuid).exists(): + logger.info(f'StickerActionFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'StickerActionFee with uuid: {uuid} exists.') + sticker_action_fee = StickerActionFee.objects.get(uuid=uuid) + sticker_action_details = sticker_action_fee.sticker_action_details + + invoice_reference = request.GET.get("invoice", "") + logger.info(f"Invoice reference: {invoice_reference} and uuid: {uuid}.",) + if not Invoice.objects.filter(reference=invoice_reference).exists(): + logger.info(f'Invoice with invoice_reference: {invoice_reference} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'Invoice with invoice_reference: {invoice_reference} exist.') + invoice = Invoice.objects.get(reference=invoice_reference) + + sticker_action_fee.invoice_reference = invoice.reference + sticker_action_fee.save() + + if sticker_action_fee.payment_type == StickerActionFee.PAYMENT_TYPE_TEMPORARY: + # if fee_inv: + sticker_action_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET + sticker_action_fee.expiry_time = None + sticker_action_fee.save() + + # for sticker_action_detail in sticker_action_details.all(): + # old_sticker = sticker_action_detail.sticker + # new_sticker = old_sticker.request_replacement(Sticker.STICKER_STATUS_LOST) + sticker_action_detail = sticker_action_details.first() + old_sticker = sticker_action_detail.sticker + new_sticker = old_sticker.request_replacement(Sticker.STICKER_STATUS_LOST) + + # Send email with the invoice + send_sticker_replacement_email(request, old_sticker, new_sticker, invoice) + + logger.info( + "Returning status.HTTP_200_OK. Order created successfully.", + ) + # this end-point is called by an unmonitored get request in ledger so there is no point having a + # a response body however we will return a status in case this is used on the ledger end in future + # return Response(status=status.HTTP_204_NO_CONTENT) + return Response(status=status.HTTP_200_OK) + class StickerReplacementFeeSuccessView(TemplateView): template_name = 'mooringlicensing/payments_ml/success_sticker_action_fee.html' @@ -262,10 +367,10 @@ def get(self, request, *args, **kwargs): sticker_action_fee = get_session_sticker_action_invoice(request.session) # This raises an exception when accessed 2nd time? sticker_action_details = sticker_action_fee.sticker_action_details - if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: owner = request.user else: - owner = sticker_action_details.first().sticker.approval.submitter + owner = sticker_action_details.first().sticker.approval.submitter_obj basket = Basket.objects.filter(status='Submitted', owner=owner).order_by('-id')[:1] order = Order.objects.get(basket=basket[0]) @@ -285,7 +390,7 @@ def get(self, request, *args, **kwargs): 'User {} with id {}'.format(owner.get_full_name(), owner.id) )) return redirect('external') - if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: + if inv.system not in [LEDGER_SYSTEM_ID, ]: logger.error('{} tried paying an application fee with an invoice from another system with reference number {}'.format( 'User {} with id {}'.format(owner.get_full_name(), owner.id), inv.reference @@ -295,7 +400,7 @@ def get(self, request, *args, **kwargs): # if fee_inv: sticker_action_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET sticker_action_fee.expiry_time = None - update_payments(invoice.reference) + # update_payments(invoice.reference) for sticker_action_detail in sticker_action_details.all(): old_sticker = sticker_action_detail.sticker @@ -319,7 +424,7 @@ def get(self, request, *args, **kwargs): print('4') if (self.LAST_STICKER_ACTION_FEE_ID in request.session) and StickerActionFee.objects.filter(id=request.session[self.LAST_STICKER_ACTION_FEE_ID]).exists(): sticker_action_fee = StickerActionFee.objects.get(id=request.session[self.LAST_STICKER_ACTION_FEE_ID]) - owner = sticker_action_fee.sticker_action_details.first().sticker.approval.submitter + owner = sticker_action_fee.sticker_action_details.first().sticker.approval.submitter_obj else: return redirect('home') @@ -341,7 +446,7 @@ def get(self, request, *args, **kwargs): def post(self, request, *args, **kwargs): proposal = self.get_object() - application_fee = ApplicationFee.objects.create(proposal=proposal, created_by=request.user, payment_type=ApplicationFee.PAYMENT_TYPE_TEMPORARY) + application_fee = ApplicationFee.objects.create(proposal=proposal, created_by=request.user.id, payment_type=ApplicationFee.PAYMENT_TYPE_TEMPORARY) logger.info('ApplicationFee.id: {} has been created for the Proposal: {}'.format(application_fee.id, proposal)) try: @@ -360,18 +465,24 @@ def post(self, request, *args, **kwargs): } return render(request, self.template_name, context) - request.session['db_processes'] = db_processes_after_success - #request.session['auto_approve'] = request.POST.get('auto_approve', False) + # request.session['db_processes'] = db_processes_after_success + new_fee_calculation = FeeCalculation.objects.create(uuid=application_fee.uuid, data=db_processes_after_success) + + return_url = request.build_absolute_uri(reverse('fee_success', kwargs={"uuid": application_fee.uuid})) + return_preload_url = request.build_absolute_uri(reverse("ledger-api-success-callback", kwargs={"uuid": application_fee.uuid})) checkout_response = checkout( request, - proposal.submitter, + proposal.submitter_obj, lines, - return_url_ns='fee_success', - return_preload_url_ns='fee_success', + return_url, + return_preload_url, + booking_reference=str(application_fee.uuid), invoice_text='{} ({})'.format(proposal.application_type.description, proposal.proposal_type.description), ) - logger.info('{} built payment line item {} for Application Fee and handing over to payment gateway'.format('User {} with id {}'.format(proposal.submitter.get_full_name(),proposal.submitter.id), proposal.id)) + user = proposal.submitter_obj + + logger.info('{} built payment line item {} for Application Fee and handing over to payment gateway'.format('User {} with id {}'.format(user.get_full_name(), user.id), proposal.id)) return checkout_response except Exception as e: @@ -386,125 +497,254 @@ class DcvAdmissionFeeSuccessView(TemplateView): template_name = 'mooringlicensing/payments_ml/success_dcv_admission_fee.html' LAST_DCV_ADMISSION_FEE_ID = 'mooringlicensing_last_dcv_admission_invoice' - def get(self, request, *args, **kwargs): - proposal = None - submitter = None - invoice = None + def get(self, request, uuid, *args, **kwargs): + logger.info(f'{DcvAdmissionFeeSuccessView.__name__} get method is called.') - try: - dcv_admission_fee = get_session_dcv_admission_invoice(request.session) # This raises an exception when accessed 2nd time? + if uuid: + if not DcvAdmissionFee.objects.filter(uuid=uuid).exists(): + logger.info(f'DcvAdmissionFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'DcvAdmissionFee with uuid: {uuid} exists.') + dcv_admission_fee = DcvAdmissionFee.objects.get(uuid=uuid) + dcv_admission = dcv_admission_fee.dcv_admission - # Retrieve db processes stored when calculating the fee, and delete the session - db_operations = request.session['db_processes'] - del request.session['db_processes'] + # invoice = Invoice.objects.get(reference=dcv_admission_fee.invoice_reference) + context = { + 'dcv_admission': dcv_admission, + 'submitter': dcv_admission.submitter_obj, + 'fee_invoice': dcv_admission_fee, + # 'invoice': invoice, + 'invoice_url': get_invoice_url(dcv_admission_fee.invoice_reference, request), + 'admission_urls': dcv_admission.get_admission_urls(), + } + return render(request, self.template_name, context) + # def get(self, request, *args, **kwargs): + # proposal = None + # submitter = None + # invoice = None + # + # try: + # dcv_admission_fee = get_session_dcv_admission_invoice(request.session) # This raises an exception when accessed 2nd time? + # + # # Retrieve db processes stored when calculating the fee, and delete the session + # db_operations = request.session['db_processes'] + # del request.session['db_processes'] + # + # dcv_admission = dcv_admission_fee.dcv_admission + # # recipient = dcv_permit.applicant_email + # submitter = dcv_admission.submitter + # + # if self.request.user.is_authenticated(): + # basket = Basket.objects.filter(status='Submitted', owner=request.user).order_by('-id')[:1] + # else: + # basket = Basket.objects.filter(status='Submitted', owner=dcv_admission.submitter).order_by('-id')[:1] + # + # order = Order.objects.get(basket=basket[0]) + # invoice = Invoice.objects.get(order_number=order.number) + # invoice_ref = invoice.reference + # + # # Update the application_fee object + # dcv_admission_fee.invoice_reference = invoice_ref + # dcv_admission_fee.save() + # + # if dcv_admission_fee.payment_type == ApplicationFee.PAYMENT_TYPE_TEMPORARY: + # try: + # inv = Invoice.objects.get(reference=invoice_ref) + # order = Order.objects.get(number=inv.order_number) + # order.user = submitter + # order.save() + # except Invoice.DoesNotExist: + # logger.error('{} tried paying an dcv_admission fee with an incorrect invoice'.format('User {} with id {}'.format(dcv_admission.submitter_obj.get_full_name(), dcv_admission.submitter_obj.id) if dcv_admission.submitter else 'An anonymous user')) + # return redirect('external-dcv_admission-detail', args=(dcv_admission.id,)) + # if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: + # logger.error('{} tried paying an dcv_admission fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(dcv_admission.submitter_obj.get_full_name(), dcv_admission.submitter_obj.id) if dcv_admission.submitter else 'An anonymous user',inv.reference)) + # return redirect('external-dcv_admission-detail', args=(dcv_admission.id,)) + # + # dcv_admission_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET + # dcv_admission_fee.expiry_time = None + # # update_payments(invoice_ref) + # + # # if dcv_admission and invoice.payment_status in ('paid', 'over_paid',): + # if dcv_admission and get_invoice_payment_status(invoice.id) in ('paid', 'over_paid',): + # self.adjust_db_operations(dcv_admission, db_operations) + # dcv_admission.generate_dcv_admission_doc() + # else: + # logger.error('Invoice payment status is {}'.format(get_invoice_payment_status(invoice.id))) + # raise + # + # dcv_admission_fee.save() + # request.session[self.LAST_DCV_ADMISSION_FEE_ID] = dcv_admission_fee.id + # delete_session_dcv_admission_invoice(request.session) + # + # email_data = send_dcv_admission_mail(dcv_admission, invoice, request) + # context = { + # 'dcv_admission': dcv_admission, + # 'submitter': submitter, + # 'fee_invoice': dcv_admission_fee, + # 'invoice': invoice, + # 'admission_urls': dcv_admission.get_admission_urls(), + # } + # return render(request, self.template_name, context) + # + # except Exception as e: + # if (self.LAST_DCV_ADMISSION_FEE_ID in request.session) and DcvAdmissionFee.objects.filter(id=request.session[self.LAST_DCV_ADMISSION_FEE_ID]).exists(): + # dcv_admission_fee = DcvAdmissionFee.objects.get(id=request.session[self.LAST_DCV_ADMISSION_FEE_ID]) + # dcv_admission = dcv_admission_fee.dcv_admission + # submitter = dcv_admission.submitter + # + # else: + # return redirect('home') + # + # invoice = Invoice.objects.get(reference=dcv_admission_fee.invoice_reference) + # context = { + # 'dcv_admission': dcv_admission, + # 'submitter': submitter, + # 'fee_invoice': dcv_admission_fee, + # 'invoice': invoice, + # 'admission_urls': dcv_admission.get_admission_urls(), + # } + # return render(request, self.template_name, context) + + # @staticmethod + # def adjust_db_operations(dcv_admission, db_operations): + # dcv_admission.lodgement_datetime = dateutil.parser.parse(db_operations['datetime_for_calculating_fee']) + # dcv_admission.save() + + +class DcvAdmissionFeeSuccessViewPreload(APIView): - dcv_admission = dcv_admission_fee.dcv_admission - # recipient = dcv_permit.applicant_email - submitter = dcv_admission.submitter + @staticmethod + def adjust_db_operations(dcv_admission, db_operations): + dcv_admission.lodgement_datetime = dateutil.parser.parse(db_operations['datetime_for_calculating_fee']) + dcv_admission.save() + + def get(self, request, uuid, format=None): + logger.info(f'{DcvAdmissionFeeSuccessViewPreload.__name__} get method is called.') - if self.request.user.is_authenticated(): - basket = Basket.objects.filter(status='Submitted', owner=request.user).order_by('-id')[:1] + if uuid: # and invoice_reference: + if not DcvAdmissionFee.objects.filter(uuid=uuid).exists(): + logger.info(f'DcvAdmissionFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) else: - basket = Basket.objects.filter(status='Submitted', owner=dcv_admission.submitter).order_by('-id')[:1] + logger.info(f'DcvAdmissionFee with uuid: {uuid} exists.') + dcv_admission_fee = DcvAdmissionFee.objects.get(uuid=uuid) + dcv_admission = dcv_admission_fee.dcv_admission - order = Order.objects.get(basket=basket[0]) - invoice = Invoice.objects.get(order_number=order.number) - invoice_ref = invoice.reference + invoice_reference = request.GET.get("invoice", "") + logger.info(f"Invoice reference: {invoice_reference} and uuid: {uuid}.",) + if not Invoice.objects.filter(reference=invoice_reference).exists(): + logger.info(f'Invoice with invoice_reference: {invoice_reference} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'Invoice with invoice_reference: {invoice_reference} exist.') + invoice = Invoice.objects.get(reference=invoice_reference) - # Update the application_fee object - dcv_admission_fee.invoice_reference = invoice_ref - dcv_admission_fee.save() + if not FeeCalculation.objects.filter(uuid=uuid).exists(): + logger.info(f'FeeCalculation with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'FeeCalculation with uuid: {uuid} exist.') + fee_calculation = FeeCalculation.objects.get(uuid=uuid) + db_operations = fee_calculation.data if dcv_admission_fee.payment_type == ApplicationFee.PAYMENT_TYPE_TEMPORARY: - try: - inv = Invoice.objects.get(reference=invoice_ref) - order = Order.objects.get(number=inv.order_number) - order.user = submitter - order.save() - except Invoice.DoesNotExist: - logger.error('{} tried paying an dcv_admission fee with an incorrect invoice'.format('User {} with id {}'.format(dcv_admission.submitter.get_full_name(), dcv_admission.submitter.id) if dcv_admission.submitter else 'An anonymous user')) - return redirect('external-dcv_admission-detail', args=(dcv_admission.id,)) - if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: - logger.error('{} tried paying an dcv_admission fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(dcv_admission.submitter.get_full_name(), dcv_admission.submitter.id) if dcv_admission.submitter else 'An anonymous user',inv.reference)) + if invoice.system not in [LEDGER_SYSTEM_ID, ]: + logger.error('{} tried paying an dcv_admission fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(dcv_admission.submitter_obj.get_full_name(), dcv_admission.submitter_obj.id) if dcv_admission.submitter else 'An anonymous user',inv.reference)) return redirect('external-dcv_admission-detail', args=(dcv_admission.id,)) dcv_admission_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET dcv_admission_fee.expiry_time = None - update_payments(invoice_ref) + # update_payments(invoice_ref) + + if 'fee_item_ids' in db_operations: + for item_id in db_operations['fee_item_ids']: + fee_item = FeeItem.objects.get(id=item_id) + dcv_admission_fee.fee_items.add(fee_item) - if dcv_admission and invoice.payment_status in ('paid', 'over_paid',): + # if dcv_admission and invoice.payment_status in ('paid', 'over_paid',): + if dcv_admission and get_invoice_payment_status(invoice.id) in ('paid', 'over_paid',): self.adjust_db_operations(dcv_admission, db_operations) dcv_admission.generate_dcv_admission_doc() else: - logger.error('Invoice payment status is {}'.format(invoice.payment_status)) + logger.error('Invoice payment status is {}'.format(get_invoice_payment_status(invoice.id))) raise + dcv_admission_fee.invoice_reference = invoice_reference dcv_admission_fee.save() - request.session[self.LAST_DCV_ADMISSION_FEE_ID] = dcv_admission_fee.id - delete_session_dcv_admission_invoice(request.session) - - email_data = send_dcv_admission_mail(dcv_admission, invoice, request) - context = { - 'dcv_admission': dcv_admission, - 'submitter': submitter, - 'fee_invoice': dcv_admission_fee, - 'invoice': invoice, - 'admission_urls': dcv_admission.get_admission_urls(), - } - return render(request, self.template_name, context) - - except Exception as e: - if (self.LAST_DCV_ADMISSION_FEE_ID in request.session) and DcvAdmissionFee.objects.filter(id=request.session[self.LAST_DCV_ADMISSION_FEE_ID]).exists(): - dcv_admission_fee = DcvAdmissionFee.objects.get(id=request.session[self.LAST_DCV_ADMISSION_FEE_ID]) - dcv_admission = dcv_admission_fee.dcv_admission - submitter = dcv_admission.submitter - else: - return redirect('home') - - invoice = Invoice.objects.get(reference=dcv_admission_fee.invoice_reference) - context = { - 'dcv_admission': dcv_admission, - 'submitter': submitter, - 'fee_invoice': dcv_admission_fee, - 'invoice': invoice, - 'admission_urls': dcv_admission.get_admission_urls(), - } - return render(request, self.template_name, context) - - @staticmethod - def adjust_db_operations(dcv_admission, db_operations): - dcv_admission.lodgement_datetime = dateutil.parser.parse(db_operations['datetime_for_calculating_fee']) - dcv_admission.save() + email = send_dcv_admission_mail(dcv_admission, invoice, request) + logger.info( + "Returning status.HTTP_200_OK. Order created successfully.", + ) + # this end-point is called by an unmonitored get request in ledger so there is no point having a + # a response body however we will return a status in case this is used on the ledger end in future + # return Response(status=status.HTTP_204_NO_CONTENT) + return Response(status=status.HTTP_200_OK) class DcvPermitFeeSuccessView(TemplateView): template_name = 'mooringlicensing/payments_ml/success_dcv_permit_fee.html' LAST_DCV_PERMIT_FEE_ID = 'mooringlicensing_last_dcv_permit_invoice' - def get(self, request, *args, **kwargs): - proposal = None - submitter = None - invoice = None - - try: - dcv_permit_fee = get_session_dcv_permit_invoice(request.session) # This raises an exception when accessed 2nd time? + def get(self, request, uuid, *args, **kwargs): + logger.info(f'{DcvPermitFeeSuccessView.__name__} get method is called.') - # Retrieve db processes stored when calculating the fee, and delete the session - db_operations = request.session['db_processes'] - del request.session['db_processes'] + if uuid: + if not DcvPermitFee.objects.filter(uuid=uuid).exists(): + logger.info(f'DcvPermitFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'DcvPermitFee with uuid: {uuid} exists.') + dcv_permit_fee = DcvPermitFee.objects.get(uuid=uuid) dcv_permit = dcv_permit_fee.dcv_permit - submitter = dcv_permit.submitter + invoice_url = get_invoice_url(dcv_permit_fee.invoice_reference, request) + + context = { + 'dcv_permit': dcv_permit, + 'submitter': dcv_permit.submitter_obj, + 'fee_invoice': dcv_permit_fee, + 'invoice_url': invoice_url, + } + return render(request, self.template_name, context) - if self.request.user.is_authenticated(): - basket = Basket.objects.filter(status='Submitted', owner=request.user).order_by('-id')[:1] + +class DcvPermitFeeSuccessViewPreload(APIView): + @staticmethod + def adjust_db_operations(dcv_permit, db_operations): + dcv_permit.start_date = datetime.datetime.strptime(db_operations['season_start_date'], '%Y-%m-%d').date() + dcv_permit.end_date = datetime.datetime.strptime(db_operations['season_end_date'], '%Y-%m-%d').date() + dcv_permit.lodgement_datetime = dateutil.parser.parse(db_operations['datetime_for_calculating_fee']) + dcv_permit.save() + + def get(self, request, uuid, format=None): + logger.info(f'{DcvPermitFeeSuccessViewPreload.__name__} get method is called.') + + if uuid: # and invoice_reference: + if not DcvPermitFee.objects.filter(uuid=uuid).exists(): + logger.info(f'DcvPermitFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) else: - basket = Basket.objects.filter(status='Submitted', owner=dcv_permit.submitter).order_by('-id')[:1] + logger.info(f'DcvPermitFee with uuid: {uuid} exists.') + dcv_permit_fee = DcvPermitFee.objects.get(uuid=uuid) + + invoice_reference = request.GET.get("invoice", "") + logger.info(f"Invoice reference: {invoice_reference} and uuid: {uuid}.",) + if not Invoice.objects.filter(reference=invoice_reference).exists(): + logger.info(f'Invoice with invoice_reference: {invoice_reference} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'Invoice with invoice_reference: {invoice_reference} exist.') + invoice = Invoice.objects.get(reference=invoice_reference) - order = Order.objects.get(basket=basket[0]) - invoice = Invoice.objects.get(order_number=order.number) - invoice_ref = invoice.reference + if not FeeCalculation.objects.filter(uuid=uuid).exists(): + logger.info(f'FeeCalculation with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'FeeCalculation with uuid: {uuid} exist.') + fee_calculation = FeeCalculation.objects.get(uuid=uuid) + db_operations = fee_calculation.data fee_item = FeeItem.objects.get(id=db_operations['fee_item_id']) try: @@ -513,73 +753,59 @@ def get(self, request, *args, **kwargs): fee_item_additional = None # Update the application_fee object - dcv_permit_fee.invoice_reference = invoice_ref + dcv_permit = dcv_permit_fee.dcv_permit + dcv_permit_fee.invoice_reference = invoice_reference dcv_permit_fee.save() dcv_permit_fee.fee_items.add(fee_item) if fee_item_additional: dcv_permit_fee.fee_items.add(fee_item_additional) - if dcv_permit_fee.payment_type == ApplicationFee.PAYMENT_TYPE_TEMPORARY: - try: - inv = Invoice.objects.get(reference=invoice_ref) - order = Order.objects.get(number=inv.order_number) - order.user = request.user - order.save() - except Invoice.DoesNotExist: - logger.error('{} tried paying an dcv_permit fee with an incorrect invoice'.format('User {} with id {}'.format(dcv_permit.submitter.get_full_name(), dcv_permit.submitter.id) if dcv_permit.submitter else 'An anonymous user')) - return redirect('external-dcv_permit-detail', args=(dcv_permit.id,)) - if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: - logger.error('{} tried paying an dcv_permit fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(dcv_permit.submitter.get_full_name(), dcv_permit.submitter.id) if dcv_permit.submitter else 'An anonymous user',inv.reference)) - return redirect('external-dcv_permit-detail', args=(dcv_permit.id,)) + if dcv_permit_fee.payment_type == DcvPermitFee.PAYMENT_TYPE_TEMPORARY: + # try: + # inv = Invoice.objects.get(reference=invoice_reference) + # order = Order.objects.get(number=inv.order_number) + # order.user = request.user + # order.save() + # except Invoice.DoesNotExist: + # logger.error('{} tried paying an dcv_permit fee with an incorrect invoice'.format('User {} with id {}'.format(dcv_permit.submitter_obj.get_full_name(), dcv_permit.submitter_obj.id) if dcv_permit.submitter else 'An anonymous user')) + # return redirect('external-dcv_permit-detail', args=(dcv_permit.id,)) + # if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: + # logger.error('{} tried paying an dcv_permit fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(dcv_permit.submitter_obj.get_full_name(), dcv_permit.submitter_obj.id) if dcv_permit.submitter else 'An anonymous user',inv.reference)) + # return redirect('external-dcv_permit-detail', args=(dcv_permit.id,)) dcv_permit_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET dcv_permit_fee.expiry_time = None - update_payments(invoice_ref) + # update_payments(invoice_reference) - if dcv_permit and invoice.payment_status in ('paid', 'over_paid',): + # if dcv_permit and invoice.payment_status in ('paid', 'over_paid',): + if dcv_permit and get_invoice_payment_status(invoice.id): self.adjust_db_operations(dcv_permit, db_operations) dcv_permit.generate_dcv_permit_doc() else: - logger.error('Invoice payment status is {}'.format(invoice.payment_status)) + # logger.error('Invoice payment status is {}'.format(invoice.payment_status)) + logger.error('Invoice payment status is {}'.format(get_invoice_payment_status(invoice.id))) raise dcv_permit_fee.save() - request.session[self.LAST_DCV_PERMIT_FEE_ID] = dcv_permit_fee.id - delete_session_dcv_permit_invoice(request.session) + # request.session[self.LAST_DCV_PERMIT_FEE_ID] = dcv_permit_fee.id + # delete_session_dcv_permit_invoice(request.session) send_dcv_permit_mail(dcv_permit, invoice, request) - context = { - 'dcv_permit': dcv_permit, - 'submitter': submitter, - 'fee_invoice': dcv_permit_fee, - } - return render(request, self.template_name, context) - - except Exception as e: - print('in DcvPermitFeeSuccessView.get() Exception') - print(e) - if (self.LAST_DCV_PERMIT_FEE_ID in request.session) and DcvPermitFee.objects.filter(id=request.session[self.LAST_DCV_PERMIT_FEE_ID]).exists(): - dcv_permit_fee = DcvPermitFee.objects.get(id=request.session[self.LAST_DCV_PERMIT_FEE_ID]) - dcv_permit = dcv_permit_fee.dcv_permit - submitter = dcv_permit.submitter + # context = { + # 'dcv_permit': dcv_permit, + # 'submitter': submitter, + # 'fee_invoice': dcv_permit_fee, + # } + # return render(request, self.template_name, context) - else: - return redirect('home') - - context = { - 'dcv_permit': dcv_permit, - 'submitter': submitter, - 'fee_invoice': dcv_permit_fee, - } - return render(request, self.template_name, context) - - @staticmethod - def adjust_db_operations(dcv_permit, db_operations): - dcv_permit.start_date = datetime.datetime.strptime(db_operations['season_start_date'], '%Y-%m-%d').date() - dcv_permit.end_date = datetime.datetime.strptime(db_operations['season_end_date'], '%Y-%m-%d').date() - dcv_permit.lodgement_datetime = dateutil.parser.parse(db_operations['datetime_for_calculating_fee']) - dcv_permit.save() + logger.info( + "Returning status.HTTP_200_OK. Order created successfully.", + ) + # this end-point is called by an unmonitored get request in ledger so there is no point having a + # a response body however we will return a status in case this is used on the ledger end in future + # return Response(status=status.HTTP_204_NO_CONTENT) + return Response(status=status.HTTP_200_OK) class ApplicationFeeAlreadyPaid(TemplateView): @@ -592,54 +818,46 @@ def get(self, request, *args, **kwargs): context = { 'proposal': proposal, - 'submitter': proposal.submitter, + 'submitter': proposal.submitter_obj, 'application_fee': application_fee, 'invoice': invoice, } return render(request, self.template_name, context) -class ApplicationFeeSuccessView(TemplateView): - template_name = 'mooringlicensing/payments_ml/success_application_fee.html' - LAST_APPLICATION_FEE_ID = 'mooringlicensing_last_app_invoice' +class ApplicationFeeSuccessViewPreload(APIView): + def get(self, request, uuid, format=None): + logger.info(f'{ApplicationFeeSuccessViewPreload.__name__} get method is called.') - def get(self, request, *args, **kwargs): - print('in ApplicationFeeSuccessView.get()') - - proposal = None - submitter = None - invoice = None + if uuid: # and invoice_reference: + if not ApplicationFee.objects.filter(uuid=uuid).exists(): + logger.info(f'ApplicationFee with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'ApplicationFee with uuid: {uuid} exist.') + application_fee = ApplicationFee.objects.get(uuid=uuid) + + # invoice_reference is set in the URL when normal invoice, + # invoice_reference is not set in the URL when future invoice, but it is set in the application_fee on creation. + invoice_reference = request.GET.get("invoice", "") + invoice_reference = application_fee.invoice_reference if not invoice_reference else invoice_reference + logger.info(f"Invoice reference: {invoice_reference} and uuid: {uuid}.",) + if not Invoice.objects.filter(reference=invoice_reference).exists(): + logger.info(f'Invoice with invoice_reference: {invoice_reference} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'Invoice with invoice_reference: {invoice_reference} exist.') + invoice = Invoice.objects.get(reference=invoice_reference) - try: - application_fee = get_session_application_invoice(request.session) # This raises an exception when accessed 2nd time? - - # Retrieve db processes stored when calculating the fee, and delete the session - db_operations = request.session['db_processes'] - del request.session['db_processes'] - #print("request.session.keys()") - #print(request.session.keys()) - # Retrieve auto_approve stored when calculating the fee, and delete - #auto_approve = request.session.get('auto_approve') - #if 'auto_approve' in request.session.keys(): - # del request.session['auto_approve'] + if not FeeCalculation.objects.filter(uuid=uuid).exists(): + logger.info(f'FeeCalculation with uuid: {uuid} does not exist. Redirecting user to dashboard page.') + return redirect(reverse('external')) + else: + logger.info(f'FeeCalculation with uuid: {uuid} exist.') + fee_calculation = FeeCalculation.objects.get(uuid=uuid) + db_operations = fee_calculation.data proposal = application_fee.proposal - recipient = proposal.applicant_email - submitter = proposal.submitter - - try: - # For the existing invoice, invoice can be retrieved from the application_fee object - invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - except Exception as e: - # For the non-existing invoice, invoice can be retrieved from the basket - if self.request.user.is_authenticated(): - basket = Basket.objects.filter(status='Submitted', owner=request.user).order_by('-id')[:1] - else: - basket = Basket.objects.filter(status='Submitted', owner=proposal.submitter).order_by('-id')[:1] - order = Order.objects.get(basket=basket[0]) - invoice = Invoice.objects.get(order_number=order.number) - - invoice_ref = invoice.reference if 'for_existing_invoice' in db_operations and db_operations['for_existing_invoice']: # For existing invoices, fee_item_application_fee.amount_paid should be updated, once paid @@ -662,13 +880,15 @@ def get(self, request, *args, **kwargs): fee_amount_adjusted = db_operations['fee_amount_adjusted'] amount_to_be_paid = Decimal(fee_amount_adjusted) amount_paid = amount_to_be_paid - FeeItemApplicationFee.objects.create( - fee_item=fee_items.first(), + fee_item = fee_items.first() + fee_item_application_fee = FeeItemApplicationFee.objects.create( + fee_item=fee_item, application_fee=application_fee, vessel_details=proposal.vessel_details, amount_to_be_paid=amount_to_be_paid, amount_paid=amount_paid, ) + logger.info(f'FeeItemApplicationFee: {fee_item_application_fee} created') if isinstance(db_operations, list): # This is used for AU/ML's auto renewal for item in db_operations: @@ -686,27 +906,31 @@ def get(self, request, *args, **kwargs): amount_paid=amount_paid, ) - application_fee.invoice_reference = invoice_ref + application_fee.invoice_reference = invoice_reference application_fee.save() if application_fee.payment_type == ApplicationFee.PAYMENT_TYPE_TEMPORARY: try: - inv = Invoice.objects.get(reference=invoice_ref) - order = Order.objects.get(number=inv.order_number) - order.user = request.user - order.save() + inv = Invoice.objects.get(reference=invoice_reference) + # order = Order.objects.get(number=inv.order_number) + # order.user = request.user + # order.save() except Invoice.DoesNotExist: - logger.error('{} tried paying an application fee with an incorrect invoice'.format('User {} with id {}'.format(proposal.submitter.get_full_name(), proposal.submitter.id) if proposal.submitter else 'An anonymous user')) + logger.error('{} tried paying an application fee with an incorrect invoice'.format('User {} with id {}'.format(proposal.submitter_obj.get_full_name(), proposal.submitter_obj.id) if proposal.submitter else 'An anonymous user')) return redirect('external-proposal-detail', args=(proposal.id,)) - if inv.system not in [PAYMENT_SYSTEM_PREFIX,]: - logger.error('{} tried paying an application fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(proposal.submitter.get_full_name(), proposal.submitter.id) if proposal.submitter else 'An anonymous user',inv.reference)) + if inv.system not in [LEDGER_SYSTEM_ID, ]: + logger.error('{} tried paying an application fee with an invoice from another system with reference number {}'.format('User {} with id {}'.format(proposal.submitter_obj.get_full_name(), proposal.submitter_obj.id) if proposal.submitter else 'An anonymous user',inv.reference)) return redirect('external-proposal-detail', args=(proposal.id,)) application_fee.payment_type = ApplicationFee.PAYMENT_TYPE_INTERNET application_fee.expiry_time = None - update_payments(invoice_ref) + # update_payments(invoice_ref) - if proposal and invoice.payment_status in ('paid', 'over_paid',): + # if proposal and invoice.payment_status in ('paid', 'over_paid',): + # inv_props = utils.get_invoice_properties(inv.id) + # invoice_payment_status = inv_props['data']['invoice']['payment_status'] + invoice_payment_status = get_invoice_payment_status(inv.id) + if proposal and invoice_payment_status in ('paid', 'over_paid',): logger.info('The fee for the proposal: {} has been fully paid'.format(proposal.lodgement_number)) if proposal.application_type.code in (AuthorisedUserApplication.code, MooringLicenceApplication.code): @@ -727,59 +951,91 @@ def get(self, request, *args, **kwargs): proposal.save() else: - msg = 'Invoice: {} payment status is {}. It should be either paid or over_paid'.format(invoice.reference, invoice.payment_status) + # msg = 'Invoice: {} payment status is {}. It should be either paid or over_paid'.format(invoice.reference, invoice.payment_status) + msg = 'Invoice: {} payment status is {}. It should be either paid or over_paid'.format(invoice.reference, get_invoice_payment_status(invoice.id)) logger.error(msg) raise Exception(msg) application_fee.save() - request.session[self.LAST_APPLICATION_FEE_ID] = application_fee.id - delete_session_application_invoice(request.session) + # request.session[self.LAST_APPLICATION_FEE_ID] = application_fee.id + # delete_session_application_invoice(request.session) + # + # wla_or_aaa = True if proposal.application_type.code in [WaitingListApplication.code, AnnualAdmissionApplication.code,] else False + # context = { + # 'proposal': proposal, + # 'submitter': submitter, + # 'fee_invoice': application_fee, + # 'is_wla_or_aaa': wla_or_aaa, + # 'invoice': invoice, + # } + # return render(request, self.template_name, context) + + logger.info( + "Returning status.HTTP_200_OK. Order created successfully.", + ) + # this end-point is called by an unmonitored get request in ledger so there is no point having a + # a response body however we will return a status in case this is used on the ledger end in future + # return Response(status=status.HTTP_204_NO_CONTENT) + return Response(status=status.HTTP_200_OK) - wla_or_aaa = True if proposal.application_type.code in [WaitingListApplication.code, AnnualAdmissionApplication.code,] else False - context = { - 'proposal': proposal, - 'submitter': submitter, - 'fee_invoice': application_fee, - 'is_wla_or_aaa': wla_or_aaa, - 'invoice': invoice, - } - return render(request, self.template_name, context) - except ItemNotSetInSessionException as e: - if self.LAST_APPLICATION_FEE_ID in request.session: - if ApplicationFee.objects.filter(id=request.session[self.LAST_APPLICATION_FEE_ID]).exists(): - application_fee = ApplicationFee.objects.get(id=request.session[self.LAST_APPLICATION_FEE_ID]) - proposal = application_fee.proposal - submitter = proposal.submitter - if type(proposal.child_obj) in [WaitingListApplication, AnnualAdmissionApplication]: - #proposal.auto_approve_check(request) - if proposal.auto_approve: - proposal.final_approval_for_WLA_AAA(request, details={}) - else: - msg = 'ApplicationFee with id: {} does not exist in the database'.format(str(request.session[self.LAST_APPLICATION_FEE_ID])) - logger.error(msg) - return redirect('home') # Should be 'raise' rather than redirect? - else: - msg = '{} is not set in session'.format(self.LAST_APPLICATION_FEE_ID) - logger.error(msg) - return redirect('home') # Should be 'raise' rather than redirect? +class ApplicationFeeSuccessView(TemplateView): + template_name = 'mooringlicensing/payments_ml/success_application_fee.html' + LAST_APPLICATION_FEE_ID = 'mooringlicensing_last_app_invoice' + + def get(self, request, uuid, *args, **kwargs): + logger.info(f'{ApplicationFeeSuccessView.__name__} get method is called.') + + proposal = None + submitter = None + invoice = None + + try: + application_fee = ApplicationFee.objects.get(uuid=uuid) + invoice = Invoice.objects.get(reference=application_fee.invoice_reference) + proposal = application_fee.proposal + submitter = proposal.submitter_obj + if type(proposal.child_obj) in [WaitingListApplication, AnnualAdmissionApplication]: + #proposal.auto_approve_check(request) + if proposal.auto_approve: + proposal.final_approval_for_WLA_AAA(request, details={}) + + # if self.LASTrAPPLICATION_FEE_ID in request.session: + # if ApplicationFee.objects.filter(id=request.session[self.LAST_APPLICATION_FEE_ID]).exists(): + # application_fee = ApplicationFee.objects.get(id=request.session[self.LAST_APPLICATION_FEE_ID]) + # proposal = application_fee.proposal + # submitter = proposal.submitter + # if type(proposal.child_obj) in [WaitingListApplication, AnnualAdmissionApplication]: + # #proposal.auto_approve_check(request) + # if proposal.auto_approve: + # proposal.final_approval_for_WLA_AAA(request, details={}) + # else: + # msg = 'ApplicationFee with id: {} does not exist in the database'.format(str(request.session[self.LAST_APPLICATION_FEE_ID])) + # logger.error(msg) + # return redirect('home') # Should be 'raise' rather than redirect? + # else: + # msg = '{} is not set in session'.format(self.LAST_APPLICATION_FEE_ID) + # logger.error(msg) + # return redirect('home') # Should be 'raise' rather than redirect? + wla_or_aaa = True if proposal.application_type.code in [WaitingListApplication.code, AnnualAdmissionApplication.code,] else False + invoice = Invoice.objects.get(reference=application_fee.invoice_reference) + invoice_url = get_invoice_url(invoice.reference, request) + context = { + 'proposal': proposal, + 'submitter': submitter, + 'fee_invoice': application_fee, + 'is_wla_or_aaa': wla_or_aaa, + 'invoice': invoice, + 'invoice_url': invoice_url, + } + return render(request, self.template_name, context) + except Exception as e: # Should not reach here msg = 'Failed to process the payment. {}'.format(str(e)) logger.error(msg) raise Exception(msg) - wla_or_aaa = True if proposal.application_type.code in [WaitingListApplication.code, AnnualAdmissionApplication.code,] else False - invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - context = { - 'proposal': proposal, - 'submitter': submitter, - 'fee_invoice': application_fee, - 'is_wla_or_aaa': wla_or_aaa, - 'invoice': invoice, - } - return render(request, self.template_name, context) - class DcvAdmissionPDFView(View): def get(self, request, *args, **kwargs): @@ -787,13 +1043,13 @@ def get(self, request, *args, **kwargs): dcv_admission = get_object_or_404(DcvAdmission, id=self.kwargs['id']) response = HttpResponse(content_type='application/pdf') if dcv_admission.admissions.count() < 1: - logger.warn('DcvAdmission: {} does not have any admission document.'.format(dcv_admission)) + logger.warning('DcvAdmission: {} does not have any admission document.'.format(dcv_admission)) return response elif dcv_admission.admissions.count() == 1: response.write(dcv_admission.admissions.first()._file.read()) return response else: - logger.warn('DcvAdmission: {} has more than one admissions.'.format(dcv_admission)) + logger.warning('DcvAdmission: {} has more than one admissions.'.format(dcv_admission)) return response except DcvAdmission.DoesNotExist: raise @@ -808,13 +1064,13 @@ def get(self, request, *args, **kwargs): dcv_permit = get_object_or_404(DcvPermit, id=self.kwargs['id']) response = HttpResponse(content_type='application/pdf') if dcv_permit.permits.count() < 1: - logger.warn('DcvPermit: {} does not have any permit document.'.format(dcv_permit)) + logger.warning('DcvPermit: {} does not have any permit document.'.format(dcv_permit)) return response elif dcv_permit.permits.count() == 1: response.write(dcv_permit.permits.first()._file.read()) return response else: - logger.warn('DcvPermit: {} has more than one permits.'.format(dcv_permit)) + logger.warning('DcvPermit: {} has more than one permits.'.format(dcv_permit)) return response except DcvPermit.DoesNotExist: raise @@ -825,17 +1081,18 @@ def get(self, request, *args, **kwargs): class InvoicePDFView(View): def get(self, request, *args, **kwargs): - try: - invoice = get_object_or_404(Invoice, reference=self.kwargs['reference']) - - response = HttpResponse(content_type='application/pdf') - response.write(create_invoice_pdf_bytes('invoice.pdf', invoice,)) - return response - except Invoice.DoesNotExist: - raise - except Exception as e: - logger.error('Error accessing the Invoice :{}'.format(e)) - raise + raise Exception('Use ledger_api_utils.get_invoice_url() instead.') + # try: + # invoice = get_object_or_404(Invoice, reference=self.kwargs['reference']) + # + # response = HttpResponse(content_type='application/pdf') + # response.write(create_invoice_pdf_bytes('invoice.pdf', invoice,)) + # return response + # except Invoice.DoesNotExist: + # raise + # except Exception as e: + # logger.error('Error accessing the Invoice :{}'.format(e)) + # raise def get_object(self): return get_object_or_404(Invoice, reference=self.kwargs['reference']) diff --git a/mooringlicensing/components/proposals/admin.py b/mooringlicensing/components/proposals/admin.py index 98b087eba..bb703ae6a 100755 --- a/mooringlicensing/components/proposals/admin.py +++ b/mooringlicensing/components/proposals/admin.py @@ -39,6 +39,7 @@ class ProposalStandardRequirementAdmin(admin.ModelAdmin): #'participant_number_required', #'default' ] + list_filter = ('application_type', 'obsolete',) def get_form(self, request, obj=None, **kwargs): self.exclude = ("participant_number_required", "default",) diff --git a/mooringlicensing/components/proposals/api.py b/mooringlicensing/components/proposals/api.py index 9110c0801..a440549d0 100755 --- a/mooringlicensing/components/proposals/api.py +++ b/mooringlicensing/components/proposals/api.py @@ -4,13 +4,19 @@ from django.db import transaction from django.core.exceptions import ValidationError from rest_framework import viewsets, serializers, status, views -from rest_framework.decorators import detail_route, list_route, renderer_classes +# from rest_framework.decorators import detail_route, list_route, renderer_classes +from rest_framework.decorators import renderer_classes +from rest_framework.decorators import action as detail_route +from rest_framework.decorators import action as list_route from rest_framework.response import Response from rest_framework.renderers import JSONRenderer from datetime import datetime -from ledger.settings_base import TIME_ZONE -from ledger.accounts.models import EmailUser, Address +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE +# from ledger.accounts.models import EmailUser, Address +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Address from mooringlicensing import settings +from mooringlicensing.components.organisations.models import Organisation from mooringlicensing.components.proposals.utils import ( save_proponent_data, ) @@ -301,12 +307,12 @@ def get(self, request, format=None): return Response({'payment_system_id': PAYMENT_SYSTEM_ID}) -class GetApplicantsDict(views.APIView): - renderer_classes = [JSONRenderer, ] - - def get(self, request, format=None): - applicants = EmailUser.objects.filter(mooringlicensing_proposals__in=Proposal.objects.all()).order_by('first_name', 'last_name').distinct() - return Response(EmailUserSerializer(applicants, many=True).data) +# class GetApplicantsDict(views.APIView): +# renderer_classes = [JSONRenderer, ] +# +# def get(self, request, format=None): +# applicants = EmailUser.objects.filter(mooringlicensing_proposals__in=Proposal.objects.all()).order_by('first_name', 'last_name').distinct() +# return Response(EmailUserSerializer(applicants, many=True).data) class GetApplicationTypeDict(views.APIView): @@ -372,7 +378,7 @@ def get(self, request, format=None): class VersionableModelViewSetMixin(viewsets.ModelViewSet): - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def history(self, request, *args, **kwargs): _object = self.get_object() _versions = Version.objects.get_for_object(_object) @@ -429,15 +435,19 @@ def filter_queryset(self, request, queryset, view): filter_query &= Q(site_licensee_email=request.user.email) else: filter_query &= ~Q(site_licensee_email=request.user.email) + # don't show discarded applications if not level == 'internal': filter_query &= ~Q(customer_status='discarded') queryset = queryset.filter(filter_query) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) #queryset = queryset.order_by(*ordering) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) + queryset = queryset.order_by(*ordering) if len(ordering): #for num, item in enumerate(ordering): # if item == 'lodgement_number': @@ -483,11 +493,14 @@ def get_queryset(self): if target_email_user_id: target_user = EmailUser.objects.get(id=target_email_user_id) user_orgs = [org.id for org in target_user.mooringlicensing_organisations.all()] - all = all.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=target_user) | Q(site_licensee_email=target_user.email)) + all = all.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=target_user.id) | Q(site_licensee_email=target_user.email)) return all elif is_customer(self.request): - user_orgs = [org.id for org in request_user.mooringlicensing_organisations.all()] - qs = all.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=request_user) | Q(site_licensee_email=request_user.email)) + orgs = Organisation.objects.filter(delegates__contains=[request_user.id]) + # user_orgs = [org.id for org in request_user.mooringlicensing_organisations.all()] + # user_orgs = [org.id for org in orgs] + # qs = all.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=request_user.id) | Q(site_licensee_email=request_user.email)) + qs = all.filter(Q(org_applicant__in=orgs) | Q(submitter=request_user.id) | Q(site_licensee_email=request_user.email)) return qs return Proposal.objects.none() @@ -516,7 +529,7 @@ def get_queryset(self): qs = AnnualAdmissionApplication.objects.all() return qs elif is_customer(self.request): - queryset = AnnualAdmissionApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user)) + queryset = AnnualAdmissionApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user.id)) return queryset logger.warn("User is neither customer nor internal user: {} <{}>".format(user.get_full_name(), user.email)) return AnnualAdmissionApplication.objects.none() @@ -525,7 +538,7 @@ def create(self, request, *args, **kwargs): proposal_type = ProposalType.objects.get(code=PROPOSAL_TYPE_NEW) obj = AnnualAdmissionApplication.objects.create( - submitter=request.user, + submitter=request.user.id, proposal_type=proposal_type ) serialized_obj = ProposalSerializer(obj.proposal) @@ -543,7 +556,7 @@ def get_queryset(self): qs = AuthorisedUserApplication.objects.all() return qs elif is_customer(self.request): - queryset = AuthorisedUserApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user)) + queryset = AuthorisedUserApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user.id)) return queryset logger.warn("User is neither customer nor internal user: {} <{}>".format(user.get_full_name(), user.email)) return AuthorisedUserApplication.objects.none() @@ -552,7 +565,7 @@ def create(self, request, *args, **kwargs): proposal_type = ProposalType.objects.get(code=PROPOSAL_TYPE_NEW) obj = AuthorisedUserApplication.objects.create( - submitter=request.user, + submitter=request.user.id, proposal_type=proposal_type ) serialized_obj = ProposalSerializer(obj.proposal) @@ -570,7 +583,7 @@ def get_queryset(self): qs = MooringLicenceApplication.objects.all() return qs elif is_customer(self.request): - queryset = MooringLicenceApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user)) + queryset = MooringLicenceApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user.id)) return queryset logger.warn("User is neither customer nor internal user: {} <{}>".format(user.get_full_name(), user.email)) return MooringLicenceApplication.objects.none() @@ -583,7 +596,7 @@ def create(self, request, *args, **kwargs): mooring = Mooring.objects.get(id=mooring_id) obj = MooringLicenceApplication.objects.create( - submitter=request.user, + submitter=request.user.id, proposal_type=proposal_type, allocated_mooring=mooring, ) @@ -602,7 +615,8 @@ def get_queryset(self): qs = WaitingListApplication.objects.all() return qs elif is_customer(self.request): - queryset = WaitingListApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user)) + # queryset = WaitingListApplication.objects.filter(Q(proxy_applicant_id=user.id) | Q(submitter=user.id)) + queryset = WaitingListApplication.objects.filter(Q(proxy_applicant=user.id) | Q(submitter=user.id)) return queryset logger.warn("User is neither customer nor internal user: {} <{}>".format(user.get_full_name(), user.email)) return WaitingListApplication.objects.none() @@ -611,7 +625,7 @@ def create(self, request, *args, **kwargs): proposal_type = ProposalType.objects.get(code=PROPOSAL_TYPE_NEW) obj = WaitingListApplication.objects.create( - submitter=request.user, + submitter=request.user.id, proposal_type=proposal_type ) serialized_obj = ProposalSerializer(obj.proposal) @@ -625,7 +639,7 @@ def get_object(self): uuid = self.kwargs.get('pk') return MooringLicenceApplication.objects.get(uuid=uuid) - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_mooring_report_document(self, request, *args, **kwargs): @@ -636,7 +650,7 @@ def process_mooring_report_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_written_proof_document(self, request, *args, **kwargs): @@ -647,7 +661,7 @@ def process_written_proof_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_signed_licence_agreement_document(self, request, *args, **kwargs): @@ -658,7 +672,7 @@ def process_signed_licence_agreement_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_proof_of_identity_document(self, request, *args, **kwargs): @@ -669,9 +683,9 @@ def process_proof_of_identity_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) - @basic_exception_handler + # @basic_exception_handler def submit(self, request, *args, **kwargs): instance = self.get_object() @@ -699,10 +713,14 @@ def get_queryset(self): qs = Proposal.objects.all() return qs elif is_customer(self.request): - user_orgs = [org.id for org in request_user.mooringlicensing_organisations.all()] - queryset = Proposal.objects.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=request_user) | Q(site_licensee_email=request_user.email)) + # user_orgs = [org.id for org in request_user.mooringlicensing_organisations.all()] + # queryset = Proposal.objects.filter(Q(org_applicant_id__in=user_orgs) | Q(submitter=request_user.id) | Q(site_licensee_email=request_user.email)) + user_orgs = [] # TODO array of organisations' id for this user + queryset = Proposal.objects.filter( + Q(org_applicant_id__in=user_orgs) | Q(submitter=request_user.id) + ).exclude(migrated=True) return queryset - logger.warn("User is neither customer nor internal user: {} <{}>".format(request_user.get_full_name(), request_user.email)) + logger.warning("User is neither customer nor internal user: {} <{}>".format(request_user.get_full_name(), request_user.email)) return Proposal.objects.none() def internal_serializer_class(self): @@ -721,7 +739,7 @@ def internal_serializer_class(self): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_electoral_roll_document(self, request, *args, **kwargs): @@ -732,7 +750,7 @@ def process_electoral_roll_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_hull_identification_number_document(self, request, *args, **kwargs): @@ -743,7 +761,7 @@ def process_hull_identification_number_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_insurance_certificate_document(self, request, *args, **kwargs): @@ -754,7 +772,7 @@ def process_insurance_certificate_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def compare_list(self, request, *args, **kwargs): """ Returns the reversion-compare urls --> list""" current_revision_id = Version.objects.get_for_object(self.get_object()).first().revision_id @@ -763,7 +781,7 @@ def compare_list(self, request, *args, **kwargs): urls = ['?version_id2={}&version_id1={}'.format(version_ids[0], version_ids[i+1]) for i in range(len(version_ids)-1)] return Response(urls) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def action_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -780,7 +798,7 @@ def action_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def comms_log(self, request, *args, **kwargs): try: instance = self.get_object() @@ -797,7 +815,7 @@ def comms_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def add_comms_log(self, request, *args, **kwargs): try: @@ -830,7 +848,7 @@ def add_comms_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def requirements(self, request, *args, **kwargs): try: instance = self.get_object() @@ -848,7 +866,7 @@ def requirements(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def amendment_request(self, request, *args, **kwargs): try: instance = self.get_object() @@ -866,13 +884,13 @@ def amendment_request(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def user_list(self, request, *args, **kwargs): qs = self.get_queryset().exclude(processing_status='discarded') serializer = ListProposalSerializer(qs,context={'request':request}, many=True) return Response(serializer.data) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def user_list_paginated(self, request, *args, **kwargs): """ Placing Paginator class here (instead of settings.py) allows specific method for desired behaviour), @@ -887,13 +905,13 @@ def user_list_paginated(self, request, *args, **kwargs): serializer = ListProposalSerializer(result_page, context={'request':request}, many=True) return paginator.get_paginated_response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def internal_proposal(self, request, *args, **kwargs): instance = self.get_object() serializer = InternalProposalSerializer(instance, context={'request': request}) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def assign_request_user(self, request, *args, **kwargs): instance = self.get_object() @@ -903,7 +921,7 @@ def assign_request_user(self, request, *args, **kwargs): return Response(serializer.data) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def assign_to(self, request, *args, **kwargs): instance = self.get_object() @@ -920,7 +938,7 @@ def assign_to(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def unassign(self, request, *args, **kwargs): instance = self.get_object() @@ -929,7 +947,7 @@ def unassign(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def switch_status(self, request, *args, **kwargs): instance = self.get_object() @@ -940,7 +958,7 @@ def switch_status(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def reissue_approval(self, request, *args, **kwargs): instance = self.get_object() @@ -955,7 +973,7 @@ def reissue_approval(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def renew_amend_approval_wrapper(self, request, *args, **kwargs): instance = self.get_object() @@ -991,7 +1009,7 @@ def renew_amend_approval_wrapper(self, request, *args, **kwargs): #return Response(serializer.data) return Response({"id":instance.id}) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def proposed_approval(self, request, *args, **kwargs): instance = self.get_object() @@ -1002,7 +1020,7 @@ def proposed_approval(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def approval_level_document(self, request, *args, **kwargs): instance = self.get_object() @@ -1010,7 +1028,7 @@ def approval_level_document(self, request, *args, **kwargs): serializer = InternalProposalSerializer(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def final_approval(self, request, *args, **kwargs): print('final_approval() in ProposalViewSet') @@ -1020,7 +1038,7 @@ def final_approval(self, request, *args, **kwargs): instance.final_approval(request, serializer.validated_data) return Response() - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def proposed_decline(self, request, *args, **kwargs): instance = self.get_object() @@ -1031,7 +1049,7 @@ def proposed_decline(self, request, *args, **kwargs): serializer = serializer_class(instance,context={'request':request}) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def final_decline(self, request, *args, **kwargs): instance = self.get_object() @@ -1042,7 +1060,7 @@ def final_decline(self, request, *args, **kwargs): serializer = serializer_class(instance, context={'request':request}) return Response(serializer.data) - @detail_route(methods=['post']) + @detail_route(methods=['post'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def draft(self, request, *args, **kwargs): @@ -1055,7 +1073,7 @@ def draft(self, request, *args, **kwargs): save_proponent_data(instance,request,self) return redirect(reverse('external')) - @detail_route(methods=['post']) + @detail_route(methods=['post'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def submit(self, request, *args, **kwargs): @@ -1068,7 +1086,7 @@ def submit(self, request, *args, **kwargs): save_proponent_data(instance,request,self) return Response() - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def get_max_vessel_length_for_main_component(self, request, *args, **kwargs): try: from mooringlicensing.components.payments_ml.models import FeeConstructor @@ -1093,7 +1111,7 @@ def get_max_vessel_length_for_main_component(self, request, *args, **kwargs): if hasattr(e,'message'): raise serializers.ValidationError(e.message) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def get_max_vessel_length_for_aa_component(self, request, *args, **kwargs): try: from mooringlicensing.components.payments_ml.models import FeeConstructor @@ -1153,7 +1171,7 @@ def get_max_vessel_length_for_aa_component(self, request, *args, **kwargs): # if hasattr(e,'message'): # raise serializers.ValidationError(e.message) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def fetch_vessel(self, request, *args, **kwargs): try: instance = self.get_object() @@ -1226,7 +1244,7 @@ def get_queryset(self): qs = ProposalRequirement.objects.all().exclude(is_deleted=True) return qs - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def move_up(self, request, *args, **kwargs): try: instance = self.get_object() @@ -1244,7 +1262,7 @@ def move_up(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def move_down(self, request, *args, **kwargs): try: instance = self.get_object() @@ -1262,7 +1280,7 @@ def move_down(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def discard(self, request, *args, **kwargs): try: instance = self.get_object() @@ -1280,7 +1298,7 @@ def discard(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def delete_document(self, request, *args, **kwargs): try: @@ -1360,9 +1378,11 @@ class AmendmentRequestViewSet(viewsets.ModelViewSet): def create(self, request, *args, **kwargs): data = request.data reason_id = request.data.get('reason_id') - proposal = request.data.get('proposal', None) + # proposal = request.data.get('proposal', None) + proposal = request.data.get('proposal') data['reason'] = reason_id data['proposal'] = proposal['id'] + # data['proposal'] = proposal_id serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception = True) instance = serializer.save() @@ -1425,7 +1445,7 @@ class VesselOwnershipViewSet(viewsets.ModelViewSet): queryset = VesselOwnership.objects.all().order_by('id') serializer_class = VesselOwnershipSerializer - @detail_route(methods=['POST']) + @detail_route(methods=['POST'], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def process_vessel_registration_document(self, request, *args, **kwargs): @@ -1436,7 +1456,7 @@ def process_vessel_registration_document(self, request, *args, **kwargs): else: return Response() - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def lookup_vessel_ownership(self, request, *args, **kwargs): vo = self.get_object() @@ -1455,7 +1475,7 @@ def lookup_vessel_ownership(self, request, *args, **kwargs): vessel_data["vessel_ownership"] = vessel_ownership_data return Response(vessel_data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def record_sale(self, request, *args, **kwargs): with transaction.atomic(): @@ -1532,7 +1552,7 @@ def record_sale(self, request, *args, **kwargs): raise serializers.ValidationError("Missing information: You must specify a sale date") return Response() - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def fetch_sale_date(self, request, *args, **kwargs): instance = self.get_object() @@ -1544,7 +1564,7 @@ class CompanyViewSet(viewsets.ModelViewSet): queryset = Company.objects.all().order_by('id') serializer_class = CompanySerializer - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def lookup_company_ownership(self, request, *args, **kwargs): company = self.get_object() @@ -1571,7 +1591,7 @@ class CompanyOwnershipViewSet(viewsets.ModelViewSet): queryset = CompanyOwnership.objects.all().order_by('id') serializer_class = CompanyOwnershipSerializer - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def lookup_company_ownership(self, request, *args, **kwargs): vessel = self.get_object() @@ -1601,7 +1621,7 @@ class VesselViewSet(viewsets.ModelViewSet): queryset = Vessel.objects.all().order_by('id') serializer_class = VesselSerializer - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_bookings(self, request, *args, **kwargs): vessel = self.get_object() @@ -1616,7 +1636,7 @@ def find_related_bookings(self, request, *args, **kwargs): data = get_bookings(booking_date=booking_date, rego_no=vessel.rego_no.upper(), mooring_id=None) return Response(data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_approvals(self, request, *args, **kwargs): vessel = self.get_object() @@ -1653,14 +1673,14 @@ def find_related_approvals(self, request, *args, **kwargs): serializer = LookupApprovalSerializer(approval_list, many=True) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def lookup_vessel_ownership(self, request, *args, **kwargs): vessel = self.get_object() serializer = VesselFullOwnershipSerializer(vessel.filtered_vesselownership_set.all(), many=True) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def comms_log(self, request, *args, **kwargs): instance = self.get_object() @@ -1668,7 +1688,7 @@ def comms_log(self, request, *args, **kwargs): serializer = VesselLogEntrySerializer(qs,many=True) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def add_comms_log(self, request, *args, **kwargs): @@ -1692,16 +1712,16 @@ def add_comms_log(self, request, *args, **kwargs): return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def action_log(self, request, *args, **kwargs): return Response([]) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def lookup_individual_ownership(self, request, *args, **kwargs): vessel = self.get_object() - owner_set = Owner.objects.filter(emailuser=request.user) + owner_set = Owner.objects.filter(emailuser=request.user.id) if owner_set: vo_set = vessel.filtered_vesselownership_set.filter(owner=owner_set[0], vessel=vessel, company_ownership=None) if vo_set: @@ -1711,13 +1731,13 @@ def lookup_individual_ownership(self, request, *args, **kwargs): return Response() return Response() - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def full_details(self, request, *args, **kwargs): vessel = self.get_object() return Response(VesselFullSerializer(vessel).data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def lookup_vessel(self, request, *args, **kwargs): vessel = self.get_object() @@ -1730,7 +1750,7 @@ def lookup_vessel(self, request, *args, **kwargs): # vessel_ownership vessel_ownership_data = {} # check if this emailuser has a matching record for this vessel - owner_qs = Owner.objects.filter(emailuser=request.user) + owner_qs = Owner.objects.filter(emailuser=request.user.id) if owner_qs: owner = owner_qs[0] vo_qs = vessel.vesselownership_set.filter(owner=owner) @@ -1742,7 +1762,7 @@ def lookup_vessel(self, request, *args, **kwargs): vessel_data["vessel_ownership"] = vessel_ownership_data return Response(vessel_data) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_internal(self, request, *args, **kwargs): search_text = request.GET.get('search[value]', '') @@ -1776,10 +1796,10 @@ def list_internal(self, request, *args, **kwargs): else: return Response([]) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_external(self, request, *args, **kwargs): search_text = request.GET.get('search[value]', '') - owner_qs = Owner.objects.filter(emailuser=request.user) + owner_qs = Owner.objects.filter(emailuser=request.user.id) if owner_qs: owner = owner_qs[0] vessel_ownership_list = owner.vesselownership_set.all() @@ -1812,7 +1832,7 @@ class MooringBayViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return MooringBay.objects.filter(active=True) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def lookup(self, request, *args, **kwargs): qs = self.get_queryset() response_data = [{"id":None,"name":"","mooring_bookings_id":None}] @@ -1835,9 +1855,11 @@ def filter_queryset(self, request, queryset, view): if filter_mooring_bay and not filter_mooring_bay.lower() == 'all': queryset = queryset.filter(mooring_bay_id=filter_mooring_bay) - getter = request.query_params.get - fields = self.get_fields(getter) - ordering = self.get_ordering(getter, fields) + # getter = request.query_params.get + # fields = self.get_fields(getter) + # ordering = self.get_ordering(getter, fields) + fields = self.get_fields(request) + ordering = self.get_ordering(request, view, fields) queryset = queryset.order_by(*ordering) if len(ordering): queryset = queryset.order_by(*ordering) @@ -1874,7 +1896,7 @@ def get_queryset(self): return qs - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) def list_internal(self, request, *args, **kwargs): qs = self.get_queryset() qs = self.filter_queryset(qs) @@ -1892,7 +1914,7 @@ class MooringViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return Mooring.objects.filter(active=True) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_bookings(self, request, *args, **kwargs): mooring = self.get_object() @@ -1906,7 +1928,7 @@ def find_related_bookings(self, request, *args, **kwargs): data = get_bookings(booking_date=booking_date, rego_no=None, mooring_id=mooring.mooring_bookings_id) return Response(data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @basic_exception_handler def find_related_approvals(self, request, *args, **kwargs): mooring = self.get_object() @@ -1924,7 +1946,7 @@ def find_related_approvals(self, request, *args, **kwargs): serializer = LookupApprovalSerializer(approval_list, many=True) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def comms_log(self, request, *args, **kwargs): instance = self.get_object() @@ -1932,7 +1954,7 @@ def comms_log(self, request, *args, **kwargs): serializer = MooringLogEntrySerializer(qs,many=True) return Response(serializer.data) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) @basic_exception_handler def add_comms_log(self, request, *args, **kwargs): @@ -1955,12 +1977,12 @@ def add_comms_log(self, request, *args, **kwargs): return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def action_log(self, request, *args, **kwargs): return Response([]) - @list_route(methods=['GET',]) + @list_route(methods=['GET',], detail=False) @basic_exception_handler def internal_list(self, request, *args, **kwargs): # add security @@ -1968,7 +1990,7 @@ def internal_list(self, request, *args, **kwargs): serializer = ListMooringSerializer(mooring_qs, many=True) return Response(serializer.data) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) @basic_exception_handler def fetch_mooring_name(self, request, *args, **kwargs): # add security diff --git a/mooringlicensing/components/proposals/email.py b/mooringlicensing/components/proposals/email.py index 8e59b2295..a5369eff6 100755 --- a/mooringlicensing/components/proposals/email.py +++ b/mooringlicensing/components/proposals/email.py @@ -1,18 +1,21 @@ import logging import mimetypes import pytz -from ledger.accounts.models import EmailUser -from ledger.payments.invoice.models import Invoice +import requests +# from ledger.accounts.models import EmailUser +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice from django.core.mail import EmailMultiAlternatives, EmailMessage from django.utils.encoding import smart_text -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse +from django.urls import reverse from django.conf import settings from django.core.files.storage import default_storage from django.core.files.base import ContentFile from django.core.exceptions import ValidationError -from mooringlicensing.components.approvals.email import log_mla_created_proposal_email, _log_approval_email, _log_org_email, _log_user_email +from mooringlicensing.components.approvals.email import log_mla_created_proposal_email, _log_approval_email, _log_org_email from mooringlicensing.components.compliances.email import _log_compliance_email from mooringlicensing.components.emails.emails import TemplateEmailBase from datetime import datetime @@ -20,6 +23,9 @@ from mooringlicensing.components.main.models import NumberOfDaysType, NumberOfDaysSetting from mooringlicensing.components.emails.utils import get_user_as_email_user, make_url_for_internal, get_public_url, \ make_url_for_external, make_http_https +from mooringlicensing.components.users.utils import _log_user_email +# from mooringlicensing.components.main.utils import _log_user_email +from mooringlicensing.ledger_api_utils import retrieve_email_userro, get_invoice_payment_status, get_invoice_url from mooringlicensing.settings import CODE_DAYS_FOR_SUBMIT_DOCUMENTS_MLA, CODE_DAYS_IN_PERIOD_MLA, \ PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_NEW, PROPOSAL_TYPE_RENEWAL @@ -36,9 +42,9 @@ def log_proposal_email(msg, proposal, sender, attachments=[]): _log_proposal_email(msg, proposal, sender=sender_user, attachments=attachments) if proposal.org_applicant: - _log_org_email(msg, proposal.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, proposal.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, proposal.submitter, proposal.submitter, sender=sender_user, attachments=attachments) + _log_user_email(msg, proposal.submitter_obj, proposal.submitter_obj, sender=sender_user, attachments=attachments) def _log_proposal_email(email_message, proposal, sender=None, file_bytes=None, filename=None, attachments=[]): @@ -64,13 +70,13 @@ def _log_proposal_email(email_message, proposal, sender=None, file_bytes=None, f else: text = smart_text(email_message) subject = '' - to = proposal.submitter.email + to = proposal.submitter_obj.email fromm = smart_text(sender) if sender else SYSTEM_NAME all_ccs = '' - customer = proposal.submitter + customer = proposal.submitter_obj - staff = sender + staff = sender.id kwargs = { 'subject': subject, @@ -140,58 +146,6 @@ def _log_org_email(email_message, organisation, customer ,sender=None): return email_entry -def _log_user_email(email_message, target_email_user, customer, sender=None, attachments=[]): - from ledger.accounts.models import EmailUserLogEntry - if isinstance(email_message, (EmailMultiAlternatives, EmailMessage,)): - # TODO this will log the plain text body, should we log the html instead - text = email_message.body - subject = email_message.subject - fromm = smart_text(sender) if sender else smart_text(email_message.from_email) - # the to email is normally a list - if isinstance(email_message.to, list): - to = ','.join(email_message.to) - else: - to = smart_text(email_message.to) - # we log the cc and bcc in the same cc field of the log entry as a ',' comma separated string - all_ccs = [] - if email_message.cc: - all_ccs += list(email_message.cc) - if email_message.bcc: - all_ccs += list(email_message.bcc) - all_ccs = ','.join(all_ccs) - - else: - text = smart_text(email_message) - subject = '' - to = customer - fromm = smart_text(sender) if sender else SYSTEM_NAME - all_ccs = '' - - customer = customer - - staff = sender - - kwargs = { - 'subject': subject, - 'text': text, - 'emailuser': target_email_user if target_email_user else customer, - 'customer': customer, - 'staff': staff, - 'to': to, - 'fromm': fromm, - 'cc': all_ccs - } - - email_entry = EmailUserLogEntry.objects.create(**kwargs) - - for attachment in attachments: - path_to_file = '{}/emailuser/{}/communications/{}'.format(settings.MEDIA_APP_DIR, target_email_user.id, attachment[0]) - path = default_storage.save(path_to_file, ContentFile(attachment[1])) - email_entry.documents.get_or_create(_file=path_to_file, name=attachment[0]) - - return email_entry - - def send_confirmation_email_upon_submit(request, proposal, payment_made, attachments=[]): # 1 email = TemplateEmailBase( @@ -205,10 +159,10 @@ def send_confirmation_email_upon_submit(request, proposal, payment_made, attachm 'public_url': get_public_url(request), 'dashboard_external_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'payment_made': payment_made, } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email cc = [] bcc = [] @@ -238,7 +192,7 @@ def send_notification_email_upon_submit_to_assessor(request, proposal, attachmen context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'proposal_internal_url': url, } to_address = proposal.assessor_recipients @@ -294,14 +248,14 @@ def send_amendment_email_notification(amendment_request, request, proposal): context = { 'public_url': get_public_url(request), - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'proposal': proposal, 'reason': reason, 'text': amendment_request.text, 'proposal_external_url': make_url_for_external(url), } - to = proposal.submitter.email + to = proposal.submitter_obj.email all_ccs = [] if proposal.org_applicant and proposal.org_applicant.email: cc_list = proposal.org_applicant.email @@ -313,9 +267,9 @@ def send_amendment_email_notification(amendment_request, request, proposal): _log_proposal_email(msg, proposal, sender=sender) if proposal.org_applicant: - _log_org_email(msg, proposal.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, proposal.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, proposal.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, proposal.submitter_obj, proposal.submitter_obj, sender=sender) def send_create_mooring_licence_application_email_notification(request, waiting_list_allocation, mooring_licence_application): @@ -360,7 +314,7 @@ def send_create_mooring_licence_application_email_notification(request, waiting_ bcc = request.data.get('cc_email') bcc_list = bcc.split(',') - msg = email.send(mooring_licence_application.submitter.email, bcc=bcc_list, attachments=attachments, context=context) + msg = email.send(retrieve_email_userro(mooring_licence_application.submitter).email, bcc=bcc_list, attachments=attachments, context=context) sender = settings.DEFAULT_FROM_EMAIL log_mla_created_proposal_email(msg, ria_generated_proposal, sender=sender_user) _log_user_email(msg, ria_generated_proposal.submitter, ria_generated_proposal.submitter, sender=sender_user, attachments=attachments) @@ -385,12 +339,12 @@ def send_documents_upload_for_mooring_licence_application_email(request, proposa context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'documents_upload_url': make_http_https(document_upload_url), 'proposal_external_url': make_http_https(url), 'num_of_days_to_submit_documents': days_setting.number_of_days, } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email cc = [] bcc = [] @@ -400,9 +354,9 @@ def send_documents_upload_for_mooring_licence_application_email(request, proposa sender = get_user_as_email_user(msg.from_email) _log_proposal_email(msg, proposal, sender=sender) if proposal.org_applicant: - _log_org_email(msg, proposal.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, proposal.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, proposal.submitter, proposal.submitter, sender=sender) + _log_user_email(msg, proposal.submitter_obj, proposal.submitter_obj, sender=sender) return msg @@ -424,7 +378,7 @@ def send_comppliance_due_date_notification(approval, compliance,): 'recipient': compliance.submitter, 'compliance_external_url': make_http_https(url), } - to_address = compliance.submitter.email + to_address = retrieve_email_userro(compliance.submitter).email cc = [] bcc = [] @@ -436,7 +390,7 @@ def send_comppliance_due_date_notification(approval, compliance,): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) return msg @@ -457,7 +411,7 @@ def send_comliance_overdue_notification(request, approval, compliance,): 'recipient': compliance.submitter, 'compliance_external_url': make_http_https(url), } - to_address = compliance.submitter.email + to_address = retrieve_email_userro(compliance.submitter).email cc = [] bcc = [] @@ -469,7 +423,7 @@ def send_comliance_overdue_notification(request, approval, compliance,): if compliance.proposal.org_applicant: _log_org_email(msg, compliance.proposal.org_applicant, compliance.submitter, sender=sender) else: - _log_user_email(msg, compliance.proposal.submitter, compliance.submitter, sender=sender) + _log_user_email(msg, compliance.proposal.submitter_obj, compliance.submitter, sender=sender) return msg # 10 @@ -489,12 +443,12 @@ def send_invitee_reminder_email(proposal, due_date, number_of_days, request=None context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'proposal_external_url': make_http_https(url), 'due_date': due_date, 'number_of_days': number_of_days, } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email cc = [] bcc = [] @@ -523,10 +477,10 @@ def send_expire_mooring_licence_application_email(proposal, reason, due_date,): context = { 'public_url': get_public_url(), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'dashboard_url': make_http_https(dashboard_url), } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email cc = [] bcc = [] @@ -556,10 +510,10 @@ def send_expire_mooring_licence_by_no_documents_email(proposal, reason, due_date context = { 'public_url': get_public_url(), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'dashboard_url': make_http_https(dashboard_url), } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email cc = [] bcc = [] @@ -584,10 +538,10 @@ def send_expire_mla_notification_to_assessor(proposal, reason, due_date): context = { 'public_url': get_public_url(), - 'applicant': proposal.submitter, + 'applicant': proposal.submitter_obj, 'due_date': due_date, 'mooring_name': mooring_name, - 'recipient': proposal.submitter + 'recipient': proposal.submitter_obj } to_address = proposal.assessor_recipients @@ -628,9 +582,9 @@ def send_endorser_reminder_email(proposal, request=None): context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'endorser': endorser, - 'applicant': proposal.submitter, + 'applicant': proposal.submitter_obj, 'endorse_url': make_http_https(endorse_url), 'decline_url': make_http_https(decline_url), 'proposal_url': make_http_https(proposal_url), @@ -669,7 +623,7 @@ def send_approval_renewal_email_notification(approval): 'approval': approval, #'vessel_rego_no': '(todo)', # TODO 'vessel_rego_no': proposal.vessel_details.vessel.rego_no, # TODO - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'expiry_date': approval.expiry_date, 'dashboard_external_url': make_http_https(url), } @@ -686,15 +640,15 @@ def send_approval_renewal_email_notification(approval): if attachment: attachments.append(attachment) - msg = email.send(proposal.submitter.email, cc=[], attachments=attachments, context=context) + msg = email.send(proposal.submitter_obj.email, cc=[], attachments=attachments, context=context) from mooringlicensing.components.approvals.models import Approval if isinstance(approval, Approval): _log_approval_email(msg, approval, sender=sender_user) if approval.org_applicant: - _log_org_email(msg, approval.org_applicant, proposal.submitter, sender=sender_user) + _log_org_email(msg, approval.org_applicant, proposal.submitter_obj, sender=sender_user) else: - _log_user_email(msg, approval.submitter, proposal.submitter, sender=sender_user, attachments=attachments) + _log_user_email(msg, approval.submitter_obj, proposal.submitter_obj, sender=sender_user, attachments=attachments) else: # TODO: log for DcvPermit??? pass @@ -720,7 +674,8 @@ def send_application_approved_or_declined_email(proposal, decision, request, sti if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + # if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): payment_required = True if payment_required: # 22 (22a, 22b, 22c) @@ -739,7 +694,7 @@ def send_application_approved_or_declined_email(proposal, decision, request, sti if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): payment_required = True if payment_required: # 25 @@ -790,7 +745,7 @@ def send_wla_approved_or_declined_email(proposal, decision, request): context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'proposal_type_code': proposal.proposal_type.code, 'decision': decision, 'details': details, @@ -802,7 +757,7 @@ def send_wla_approved_or_declined_email(proposal, decision, request): txt_template=txt_template, ) - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -859,7 +814,7 @@ def send_aaa_approved_or_declined_email(proposal, decision, request, stickers_to context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO???: if existing sticker needs to be replaced, assign sticker object here @@ -871,7 +826,7 @@ def send_aaa_approved_or_declined_email(proposal, decision, request, stickers_to txt_template=txt_template, ) - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -909,7 +864,7 @@ def send_aua_approved_or_declined_email_new_renewal(proposal, decision, request, if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): # Payment required payment_url = '{}/application_fee_existing/{}'.format(get_public_url(request), proposal.id) elif decision == 'approved_paid': @@ -943,14 +898,14 @@ def send_aua_approved_or_declined_email_new_renewal(proposal, decision, request, context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. 'payment_url': payment_url, } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1002,13 +957,13 @@ def send_aua_approved_or_declined_email_amendment_payment_not_required(proposal, context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1048,7 +1003,7 @@ def send_aua_approved_or_declined_email_amendment_payment_required(proposal, dec if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): # Payment required payment_url = '{}/application_fee_existing/{}'.format(get_public_url(request), proposal.id) elif decision == 'approved_paid': @@ -1082,14 +1037,14 @@ def send_aua_approved_or_declined_email_amendment_payment_required(proposal, dec context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. 'payment_url': make_http_https(payment_url), } - to_address = proposal.submitter.email + to_address = retrieve_email_userro(proposal.submitter).email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1100,7 +1055,7 @@ def send_aua_approved_or_declined_email_amendment_payment_required(proposal, dec def get_attachments(attach_invoice, attach_licence_doc, proposal, attach_au_summary_doc=False): - from mooringlicensing.components.payments_ml.invoice_pdf import create_invoice_pdf_bytes + # from mooringlicensing.components.payments_ml.invoice_pdf import create_invoice_pdf_bytes # proposal.refresh_from_db() # if proposal.approval: # For AU/ML new application, approval is not created yet before payments @@ -1109,9 +1064,16 @@ def get_attachments(attach_invoice, attach_licence_doc, proposal, attach_au_summ attachments = [] if attach_invoice and proposal.invoice: # Attach invoice - invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', proposal.invoice, ) - attachment = ('invoice#{}.pdf'.format(proposal.invoice.reference), invoice_bytes, 'application/pdf') - attachments.append(attachment) + # invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', proposal.invoice, ) + # url = get_invoice_url(proposal.invoice.reference) + url = f'{settings.LEDGER_API_URL}/ledgergw/invoice-pdf/{settings.LEDGER_API_KEY}/{proposal.invoice.reference}' + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = ('invoice#{}.pdf'.format(proposal.invoice.reference), invoice_pdf.content, 'application/pdf') + attachments.append(attachment) + else: + logger.error(f'Status code: {invoice_pdf.status_code}. Could not retrieve invoice_pdf for the invoice reference: {proposal.invoice.reference}') + if attach_licence_doc and proposal.approval and proposal.approval.licence_document: # Attach licence document licence_document = proposal.approval.licence_document._file @@ -1165,7 +1127,7 @@ def send_au_summary_to_ml_holder(mooring_licence, request, au_proposal): 'url_for_au_dashboard_page': get_public_url(request), # Do we have AU dashboard page for external??? } - to_address = mooring_licence.submitter.email + to_address = retrieve_email_userro(mooring_licence.submitter).email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=[], bcc=[],) @@ -1206,7 +1168,7 @@ def send_mla_approved_or_declined_email_new_renewal(proposal, decision, request, if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): # Payment required payment_url = '{}/application_fee_existing/{}'.format(get_public_url(request), proposal.id) elif decision == 'approved_paid': @@ -1242,14 +1204,14 @@ def send_mla_approved_or_declined_email_new_renewal(proposal, decision, request, context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. 'payment_url': make_http_https(payment_url), } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1305,13 +1267,13 @@ def send_mla_approved_or_declined_email_amendment_payment_not_required(proposal, context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1350,7 +1312,7 @@ def send_mla_approved_or_declined_email_amendment_payment_required(proposal, dec if proposal.application_fees.count(): application_fee = proposal.get_main_application_fee() invoice = Invoice.objects.get(reference=application_fee.invoice_reference) - if invoice.payment_status not in ('paid', 'over_paid'): + if get_invoice_payment_status(invoice.id) not in ('paid', 'over_paid'): # Payment required payment_url = '{}/application_fee_existing/{}'.format(get_public_url(request), proposal.id) elif decision == 'approved_paid': @@ -1387,14 +1349,14 @@ def send_mla_approved_or_declined_email_amendment_payment_required(proposal, dec 'public_url': get_public_url(request), 'proposal': proposal, 'approval': proposal.approval, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'decision': decision, 'details': details, 'stickers_to_be_returned': stickers_to_be_returned, # TODO: if existing sticker needs to be replaced, assign sticker object here. 'payment_url': make_http_https(payment_url), } - to_address = proposal.submitter.email + to_address = proposal.submitter_obj.email # Send email msg = email.send(to_address, context=context, attachments=attachments, cc=all_ccs, bcc=all_bccs,) @@ -1437,9 +1399,9 @@ def send_other_documents_submitted_notification_email(request, proposal): sender = get_user_as_email_user(msg.from_email) log_proposal_email(msg, proposal, sender, attachments=attachments) if proposal.org_applicant: - _log_org_email(msg, proposal.org_applicant, proposal.submitter, sender=sender) + _log_org_email(msg, proposal.org_applicant, proposal.submitter_obj, sender=sender) else: - _log_user_email(msg, proposal.submitter, proposal.submitter, sender=sender, attachments=attachments) + _log_user_email(msg, proposal.submitter_obj, proposal.submitter_obj, sender=sender, attachments=attachments) return msg @@ -1516,9 +1478,9 @@ def send_endorsement_of_authorised_user_application_email(request, proposal): context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'endorser': endorser, - 'applicant': proposal.submitter, + 'applicant': proposal.submitter_obj, 'endorse_url': make_http_https(endorse_url), 'decline_url': make_http_https(decline_url), 'proposal_url': make_http_https(proposal_url), @@ -1556,7 +1518,7 @@ def send_proposal_approver_sendback_email_notification(request, proposal): context = { 'public_url': get_public_url(request), 'proposal': proposal, - 'recipient': proposal.submitter, + 'recipient': proposal.submitter_obj, 'url': url, 'approver_comment': approver_comment } diff --git a/mooringlicensing/components/proposals/fixtures/fee_fix.json b/mooringlicensing/components/proposals/fixtures/fee_fix.json new file mode 100644 index 000000000..86539dbd6 --- /dev/null +++ b/mooringlicensing/components/proposals/fixtures/fee_fix.json @@ -0,0 +1,169 @@ +[ +{ + "model": "mooringlicensing.agegroup", + "pk": 1, + "fields": { + "code": "adult" + } +}, +{ + "model": "mooringlicensing.agegroup", + "pk": 2, + "fields": { + "code": "child" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 1, + "fields": { + "fee_season": 1, + "name": "Once off application fee", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 2, + "fields": { + "fee_season": 2, + "name": "Period 1 Full fee", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 3, + "fields": { + "fee_season": 2, + "name": "Period 2 50% fee", + "start_date": "2022-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 4, + "fields": { + "fee_season": 3, + "name": "Period 1 Full fee", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 5, + "fields": { + "fee_season": 3, + "name": "Period 2 50% fee", + "start_date": "2022-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 6, + "fields": { + "fee_season": 4, + "name": "Period 1 Full feee", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 7, + "fields": { + "fee_season": 4, + "name": "Period 2 50% fee", + "start_date": "2022-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 8, + "fields": { + "fee_season": 5, + "name": "DCV Permit", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 9, + "fields": { + "fee_season": 6, + "name": "DCV admission fee", + "start_date": "2021-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 10, + "fields": { + "fee_season": 7, + "name": "Period 1 Full fee", + "start_date": "2022-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 11, + "fields": { + "fee_season": 7, + "name": "Period 2 50% fee", + "start_date": "2023-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 12, + "fields": { + "fee_season": 8, + "name": "Period 1 Full fee", + "start_date": "2022-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 13, + "fields": { + "fee_season": 8, + "name": "Period 2 50% fee", + "start_date": "2023-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 14, + "fields": { + "fee_season": 9, + "name": "Period 1 Full fee", + "start_date": "2022-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 15, + "fields": { + "fee_season": 9, + "name": "Period 2 50% fee", + "start_date": "2023-04-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 16, + "fields": { + "fee_season": 10, + "name": "period1", + "start_date": "2022-09-01" + } +}, +{ + "model": "mooringlicensing.feeperiod", + "pk": 17, + "fields": { + "fee_season": 11, + "name": "period1", + "start_date": "2022-09-01" + } +} +] diff --git a/mooringlicensing/components/proposals/forms.py b/mooringlicensing/components/proposals/forms.py index 156939134..e58b23f44 100755 --- a/mooringlicensing/components/proposals/forms.py +++ b/mooringlicensing/components/proposals/forms.py @@ -1,6 +1,6 @@ from django import forms from django.core.exceptions import ValidationError -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser from mooringlicensing.components.proposals.models import HelpPage #ProposalAssessorGroup,ProposalApproverGroup from mooringlicensing.components.main.models import SystemMaintenance from ckeditor.widgets import CKEditorWidget diff --git a/mooringlicensing/components/proposals/models.py b/mooringlicensing/components/proposals/models.py index 16c7d4308..caae3b94b 100644 --- a/mooringlicensing/components/proposals/models.py +++ b/mooringlicensing/components/proposals/models.py @@ -8,35 +8,46 @@ import pytz import uuid -from ledger.settings_base import TIME_ZONE -from ledger.payments.pdf import create_invoice_pdf_bytes + +from mooringlicensing.ledger_api_utils import retrieve_email_userro, get_invoice_payment_status, get_invoice_url +# from mooringlicensing.components.payments_ml.utils import get_invoice_payment_status +# from mooringlicensing.components.main.utils import retrieve_email_user +# from ledger.settings_base import TIME_ZONE +from mooringlicensing.settings import TIME_ZONE +# from ledger.payments.pdf import create_invoice_pdf_bytes +# from ledger_api_client.pdf import create_invoice_pdf_bytes from django.db import models, transaction from django.dispatch import receiver from django.db.models.signals import pre_delete -from django.utils.encoding import python_2_unicode_compatible +# from django.utils.encoding import python_2_unicode_compatible from django.core.exceptions import ValidationError, ObjectDoesNotExist, ImproperlyConfigured from django.contrib.postgres.fields.jsonb import JSONField from django.contrib.postgres.fields import ArrayField from django.contrib.auth.models import Group from django.utils import timezone -from django.conf import settings -from django.core.urlresolvers import reverse -from ledger.accounts.models import EmailUser, RevisionedMixin -from mooringlicensing import exceptions +# from django.conf import settings +# from django.core.urlresolvers import reverse +from django.urls import reverse +# from ledger.accounts.models import EmailUser, RevisionedMixin +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, EmailUserRO +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import Invoice +from mooringlicensing import exceptions, settings from mooringlicensing.components.organisations.models import Organisation from mooringlicensing.components.main.models import ( CommunicationsLogEntry, UserAction, - Document, ApplicationType, NumberOfDaysType, NumberOfDaysSetting, + Document, ApplicationType, NumberOfDaysType, NumberOfDaysSetting, RevisionedMixin, ) +import requests +import ledger_api_client from mooringlicensing.components.main.decorators import ( basic_exception_handler, timeit, query_debugger ) -from ledger.checkout.utils import createCustomBasket -from ledger.payments.invoice.models import Invoice -from ledger.payments.invoice.utils import CreateInvoiceBasket +# from ledger.checkout.utils import createCustomBasket +# from ledger.payments.invoice.utils import CreateInvoiceBasket from mooringlicensing.components.proposals.email import ( send_application_approved_or_declined_email, @@ -59,10 +70,11 @@ import logging from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PAYMENT_SYSTEM_ID, \ - PAYMENT_SYSTEM_PREFIX, PROPOSAL_TYPE_NEW, CODE_DAYS_FOR_ENDORSER_AUA + LEDGER_SYSTEM_ID, PROPOSAL_TYPE_NEW, CODE_DAYS_FOR_ENDORSER_AUA logger = logging.getLogger(__name__) -logger_for_payment = logging.getLogger('mooringlicensing') +# logger_for_payment = logging.getLogger('mooringlicensing') +logger_for_payment = logging.getLogger(__name__) def update_proposal_doc_filename(instance, filename): @@ -157,6 +169,16 @@ class Meta: app_label = 'mooringlicensing' +# class ProposalApplicantDetails(models.Model): +# ''' +# This model is for storing the historical applicant details +# ''' +# details = models.JSONField(blank=True, null=True) +# +# class Meta: +# app_label = 'mooringlicensing' + + class Proposal(DirtyFieldsMixin, RevisionedMixin): APPLICANT_TYPE_ORGANISATION = 'ORG' APPLICANT_TYPE_PROXY = 'PRX' @@ -256,11 +278,15 @@ class Proposal(DirtyFieldsMixin, RevisionedMixin): lodgement_sequence = models.IntegerField(blank=True, default=0) lodgement_date = models.DateTimeField(blank=True, null=True) - proxy_applicant = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proxy', on_delete=models.SET_NULL) # not currently used by ML - submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals', on_delete=models.SET_NULL) + # proxy_applicant = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proxy', on_delete=models.SET_NULL) # not currently used by ML + proxy_applicant = models.IntegerField(blank=True, null=True) # not currently used by ML + # submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals', on_delete=models.SET_NULL) + submitter = models.IntegerField(blank=True, null=True) - assigned_officer = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals_assigned', on_delete=models.SET_NULL) - assigned_approver = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals_approvals', on_delete=models.SET_NULL) + # assigned_officer = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals_assigned', on_delete=models.SET_NULL) + assigned_officer = models.IntegerField(blank=True, null=True) + # assigned_approver = models.ForeignKey(EmailUser, blank=True, null=True, related_name='mooringlicensing_proposals_approvals', on_delete=models.SET_NULL) + assigned_approver = models.IntegerField(blank=True, null=True) processing_status = models.CharField('Processing Status', max_length=40, choices=PROCESSING_STATUS_CHOICES, default=PROCESSING_STATUS_CHOICES[0][0]) prev_processing_status = models.CharField(max_length=40, blank=True, null=True) @@ -282,10 +308,11 @@ class Proposal(DirtyFieldsMixin, RevisionedMixin): vessel_id = models.IntegerField(null=True,blank=True) vessel_type = models.CharField(max_length=20, choices=VESSEL_TYPES, blank=True) vessel_name = models.CharField(max_length=400, blank=True) - vessel_length = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') # does not exist in MB - vessel_draft = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - vessel_beam = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - vessel_weight = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') # tonnage + # vessel_length = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') # does not exist in MB + vessel_length = models.DecimalField(max_digits=8, decimal_places=2, null=True) # does not exist in MB + vessel_draft = models.DecimalField(max_digits=8, decimal_places=2, null=True) + vessel_beam = models.DecimalField(max_digits=8, decimal_places=2, null=True) + vessel_weight = models.DecimalField(max_digits=8, decimal_places=2, null=True) # tonnage berth_mooring = models.CharField(max_length=200, blank=True) # only for draft status proposals, otherwise retrieve from within vessel_ownership dot_name = models.CharField(max_length=200, blank=True, null=True) @@ -322,11 +349,13 @@ class Proposal(DirtyFieldsMixin, RevisionedMixin): listed_vessels = models.ManyToManyField('VesselOwnership', 'listed_on_proposals') keep_existing_vessel = models.BooleanField(default=True) - fee_season = models.ForeignKey('FeeSeason', null=True, blank=True) # In some case, proposal doesn't have any fee related objects. Which results in the impossibility to retrieve season, start_date, end_date, etc. + fee_season = models.ForeignKey('FeeSeason', null=True, blank=True, on_delete=models.SET_NULL) # In some case, proposal doesn't have any fee related objects. Which results in the impossibility to retrieve season, start_date, end_date, etc. # To prevent that, fee_season is used in order to store those data. auto_approve = models.BooleanField(default=False) null_vessel_on_create = models.BooleanField(default=True) + personal_details = models.JSONField(null=True, blank=True) + class Meta: app_label = 'mooringlicensing' verbose_name = "Application" @@ -335,6 +364,10 @@ class Meta: def __str__(self): return str(self.lodgement_number) + @property + def submitter_obj(self): + return retrieve_email_userro(self.submitter) if self.submitter else None + def get_fee_amount_adjusted(self, fee_item_being_applied, vessel_length, max_amount_paid): """ Retrieve all the fee_items for this vessel @@ -724,7 +757,7 @@ def save(self, *args, **kwargs): kwargs.pop('version_comment', None) kwargs['no_revision'] = True self.update_customer_status() - super(Proposal, self).save(*args,**kwargs) + super(Proposal, self).save(*args, **kwargs) if type(self) == Proposal: self.child_obj.refresh_from_db() @@ -781,7 +814,8 @@ def editable_vessel_details(self): @property def fee_paid(self): - if (self.invoice and self.invoice.payment_status in ['paid', 'over_paid']) or self.proposal_type==PROPOSAL_TYPE_AMENDMENT: + # if (self.invoice and self.invoice.payment_status in ['paid', 'over_paid']) or self.proposal_type==PROPOSAL_TYPE_AMENDMENT: + if (self.invoice and get_invoice_payment_status(self.invoice.id) in ['paid', 'over_paid']) or self.proposal_type==PROPOSAL_TYPE_AMENDMENT: return True return False @@ -798,51 +832,70 @@ def reversion_ids(self): @property def applicant(self): + from mooringlicensing.ledger_api_utils import retrieve_email_userro + if self.org_applicant: return self.org_applicant.organisation.name elif self.proxy_applicant: + applicant = retrieve_email_userro(self.proxy_applicant) return "{} {}".format( - self.proxy_applicant.first_name, - self.proxy_applicant.last_name) + applicant.first_name, + applicant.last_name) else: + applicant = retrieve_email_userro(self.submitter) return "{} {}".format( - self.submitter.first_name, - self.submitter.last_name) + applicant.first_name, + applicant.last_name) @property def applicant_email(self): + from mooringlicensing.ledger_api_utils import retrieve_email_userro + if self.org_applicant and hasattr(self.org_applicant.organisation, 'email') and self.org_applicant.organisation.email: return self.org_applicant.organisation.email elif self.proxy_applicant: - return self.proxy_applicant.email + applicant = retrieve_email_userro(self.proxy_applicant) + return applicant.email else: - return self.submitter.email + # return self.submitter.email + from mooringlicensing.ledger_api_utils import retrieve_email_userro + return retrieve_email_userro(self.submitter).email @property def applicant_details(self): + from mooringlicensing.ledger_api_utils import retrieve_email_userro + if self.org_applicant: return '{} \n{}'.format( self.org_applicant.organisation.name, self.org_applicant.address) elif self.proxy_applicant: + applicant = retrieve_email_userro(self.proxy_applicant) return "{} {}\n{}".format( - self.proxy_applicant.first_name, - self.proxy_applicant.last_name, - self.proxy_applicant.addresses.all().first()) + applicant.first_name, + applicant.last_name, + applicant.addresses.all().first()) else: + applicant = retrieve_email_userro(self.submitter) + test = applicant.addresses.all() return "{} {}\n{}".format( - self.submitter.first_name, - self.submitter.last_name, - self.submitter.addresses.all().first()) + applicant.first_name, + applicant.last_name, + # applicant.addresses.all().first()) + applicant.residential_address) @property def applicant_address(self): + from mooringlicensing.ledger_api_utils import retrieve_email_userro + if self.org_applicant: return self.org_applicant.address elif self.proxy_applicant: - return self.proxy_applicant.residential_address + applicant = retrieve_email_userro(self.proxy_applicant) + return applicant.residential_address else: - return self.submitter.residential_address + applicant = retrieve_email_userro(self.submitter) + return applicant.residential_address @property def applicant_id(self): @@ -917,19 +970,26 @@ def allowed_assessors(self): group = self.__approver_group() else: group = self.__assessor_group() - return group.user_set.all() if group else [] + # return group.user_set.all() if group else [] + ids = group.get_system_group_member_ids() if group else [] + users = EmailUserRO.objects.filter(id__in=ids) + return users @property def compliance_assessors(self): group = self.__assessor_group() - return group.user_set.all() if group else [] + # return group.user_set.all() if group else [] + ids = group.get_system_group_member_ids() if group else [] + users = EmailUserRO.objects.filter(id__in=ids) + return users def allowed_assessors_user(self, request): if self.processing_status == 'with_approver': group = self.__approver_group() else: group = self.__assessor_group() - return True if group and group.user_set.filter(id=request.user.id).values_list('id', flat=True) else False + # return True if group and group.user_set.filter(id=request.user.id).values_list('id', flat=True) else False + return True if group and request.user.id in group.get_system_group_member_ids() else False @property def can_officer_process(self): @@ -994,10 +1054,14 @@ def approver_recipients(self): #Check if the user is member of assessor group for the Proposal def is_assessor(self, user): + if isinstance(user, EmailUserRO): + user = user.id return self.child_obj.is_assessor(user) #Check if the user is member of assessor group for the Proposal def is_approver(self, user): + if isinstance(user, EmailUserRO): + user = user.id return self.child_obj.is_approver(user) def can_assess(self, user): @@ -1023,7 +1087,7 @@ def has_assessor_mode(self,user): def log_user_action(self, action, request=None): if request: - return ProposalUserAction.log_action(self, action, request.user) + return ProposalUserAction.log_action(self, action, request.user.id) else: return ProposalUserAction.log_action(self, action) @@ -1056,7 +1120,7 @@ def assign_officer(self, request, officer): self.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_APPROVER.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_APPROVER.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_APPROVER.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) else: if officer != self.assigned_officer: self.assigned_officer = officer @@ -1065,7 +1129,7 @@ def assign_officer(self, request, officer): self.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_ASSESSOR.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_ASSESSOR.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_ASSIGN_TO_ASSESSOR.format(self.id,'{}({})'.format(officer.get_full_name(),officer.email)),request) except: raise @@ -1091,7 +1155,7 @@ def assing_approval_level_document(self, request): self.log_user_action(ProposalUserAction.ACTION_APPROVAL_LEVEL_DOCUMENT.format(self.id),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_APPROVAL_LEVEL_DOCUMENT.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_APPROVAL_LEVEL_DOCUMENT.format(self.id),request) return self except: raise @@ -1109,7 +1173,7 @@ def unassign(self,request): self.log_user_action(ProposalUserAction.ACTION_UNASSIGN_APPROVER.format(self.id),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_UNASSIGN_APPROVER.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_UNASSIGN_APPROVER.format(self.id),request) else: if self.assigned_officer: self.assigned_officer = None @@ -1118,7 +1182,7 @@ def unassign(self,request): self.log_user_action(ProposalUserAction.ACTION_UNASSIGN_ASSESSOR.format(self.id),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_UNASSIGN_ASSESSOR.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_UNASSIGN_ASSESSOR.format(self.id),request) except: raise @@ -1214,7 +1278,7 @@ def proposed_decline(self,request,details): ProposalDeclinedDetails.objects.update_or_create( proposal=self, defaults={ - 'officer': request.user, + 'officer': request.user.id, 'reason': reason, 'cc_email': details.get('cc_email', None) } @@ -1226,7 +1290,7 @@ def proposed_decline(self,request,details): self.log_user_action(ProposalUserAction.ACTION_PROPOSED_DECLINE.format(self.id), request) # Log entry for organisation applicant_field = getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_PROPOSED_DECLINE.format(self.id), request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_PROPOSED_DECLINE.format(self.id), request) send_approver_approve_decline_email_notification(request, self) except: @@ -1250,7 +1314,7 @@ def final_decline(self, request, details): proposal_decline, success = ProposalDeclinedDetails.objects.update_or_create( proposal=self, defaults={ - 'officer': request.user, + 'officer': request.user.id, 'reason': details.get('reason', ''), 'cc_email': details.get('cc_email',None) } @@ -1262,7 +1326,7 @@ def final_decline(self, request, details): self.log_user_action(ProposalUserAction.ACTION_DECLINE.format(self.id),request) # Log entry for organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_DECLINE.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_DECLINE.format(self.id),request) # update WLA internal_status ## ML if type(self.child_obj) == MooringLicenceApplication and self.waiting_list_allocation: @@ -1290,7 +1354,7 @@ def on_hold(self,request): self.log_user_action(ProposalUserAction.ACTION_PUT_ONHOLD.format(self.id),request) # Log entry for organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_PUT_ONHOLD.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_PUT_ONHOLD.format(self.id),request) except: raise @@ -1309,7 +1373,7 @@ def on_hold_remove(self,request): self.log_user_action(ProposalUserAction.ACTION_REMOVE_ONHOLD.format(self.id),request) # Log entry for organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_REMOVE_ONHOLD.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_REMOVE_ONHOLD.format(self.id),request) except: raise @@ -1347,7 +1411,7 @@ def proposed_approval(self, request, details): self.log_user_action(ProposalUserAction.ACTION_PROPOSED_APPROVAL.format(self.id), request) # Log entry for organisation applicant_field = getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_PROPOSED_APPROVAL.format(self.id), request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_PROPOSED_APPROVAL.format(self.id), request) send_approver_approve_decline_email_notification(request, self) return self @@ -1479,11 +1543,11 @@ def final_approval_for_WLA_AAA(self, request, details=None): if details: # When not auto-approve self.log_user_action(ProposalUserAction.ACTION_APPROVED.format(self.id), request) - applicant_field.log_user_action(ProposalUserAction.ACTION_APPROVED.format(self.id), 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),) - applicant_field.log_user_action(ProposalUserAction.ACTION_AUTO_APPROVED.format(self.id),) + # applicant_field.log_user_action(ProposalUserAction.ACTION_AUTO_APPROVED.format(self.id),) # set proposal status to approved - can change later after manage_stickers self.processing_status = Proposal.PROCESSING_STATUS_APPROVED @@ -1586,7 +1650,7 @@ def final_approval_for_AUA_MLA(self, request=None, details=None): self.save() else: ## prepare invoice - from mooringlicensing.components.payments_ml.utils import create_fee_lines, make_serializable + # from mooringlicensing.components.payments_ml.utils import create_fee_lines, make_serializable from mooringlicensing.components.payments_ml.models import FeeConstructor, ApplicationFee # create fee lines tells us whether a payment is required @@ -1615,15 +1679,62 @@ def final_approval_for_AUA_MLA(self, request=None, details=None): try: logger.info('Creating invoice for the application: {}'.format(self)) - basket = createCustomBasket(line_items, self.submitter, PAYMENT_SYSTEM_ID) - order = CreateInvoiceBasket(payment_method='other', system=PAYMENT_SYSTEM_PREFIX).create_invoice_and_order( - basket, 0, None, None, user=self.submitter, invoice_text='Payment Invoice') - invoice = Invoice.objects.get(order_number=order.number) + # Following two lines are for future invoicing. + # However because we need to segregate 'ledger' from this system, we cannot use these two functions. + # TODO: Review and rewrite to create an invoice with ledger_client_api. + # basket = createCustomBasket(line_items, self.submitter, PAYMENT_SYSTEM_ID) + # order = CreateInvoiceBasket(payment_method='other', system=PAYMENT_SYSTEM_PREFIX).create_invoice_and_order(basket, 0, None, None, user=self.submitter, invoice_text='Payment Invoice') + # invoice = Invoice.objects.get(order_number=order.number) + + ### Future Invoice ### + invoice_text = 'Payment Invoice' + basket_params = { + 'products': line_items, + 'vouchers': [], + 'system': settings.PAYMENT_SYSTEM_ID, + 'custom_basket': True, + # 'booking_reference': 'PB-' + str(booking_id), + # 'booking_reference_link': str(old_booking_id), + 'no_payment': True, + # 'organisation': 7, + 'tax_override': True, + } + # basket_user_id = customer_id + # basket_hash = utils_ledger_api_client.create_basket_session( + from ledger_api_client.utils import create_basket_session, process_create_future_invoice + # basket_hash = create_basket_session(request, request.user.id, basket_params) + basket_hash = create_basket_session(request, self.submitter, basket_params) + + #checkouthash = hashlib.sha256('TEST'.encode('utf-8')).hexdigest() + #checkouthash = request.session.get('checkouthash','') + #basket, basket_hash = use_existing_basket_from_invoice('00193349270') + # notification url for when payment is received. + # return_preload_url = settings.PARKSTAY_EXTERNAL_URL + '/api/complete_booking/9819873279821398732198737981298/' + str(booking_id) + '/' + application_fee = ApplicationFee.objects.create( proposal=self, - invoice_reference=invoice.reference, + # invoice_reference=invoice.reference, payment_type=ApplicationFee.PAYMENT_TYPE_TEMPORARY, ) + # return_preload_url = request.build_absolute_uri(reverse("ledger-api-success-callback", kwargs={"uuid": application_fee.uuid})) + 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 + ) + + application_fee.invoice_reference = pcfi['data']['invoice'] + application_fee.save() + ### END: Future Invoice ### + + + # application_fee = ApplicationFee.objects.create( + # proposal=self, + # invoice_reference=invoice.reference, + # payment_type=ApplicationFee.PAYMENT_TYPE_TEMPORARY, + # ) logger.info('ApplicationFee.id: {} has been created for the Proposal: {}'.format(application_fee.id, self)) # Link between ApplicationFee and FeeItem(s) @@ -1806,7 +1917,7 @@ def amend_approval(self,request): try: proposal = clone_proposal_with_status_reset(self) proposal.proposal_type = ProposalType.objects.get(code=PROPOSAL_TYPE_AMENDMENT) - proposal.submitter = request.user + proposal.submitter = request.user.id proposal.previous_application = self req=self.requirements.all().exclude(is_deleted=True) from copy import deepcopy @@ -1822,7 +1933,7 @@ def amend_approval(self,request): self.log_user_action(ProposalUserAction.ACTION_AMEND_PROPOSAL.format(self.id),request) # Create a log entry for the organisation applicant_field=getattr(self, self.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_AMEND_PROPOSAL.format(self.id),request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_AMEND_PROPOSAL.format(self.id),request) #Log entry for approval from mooringlicensing.components.approvals.models import ApprovalUserAction self.approval.log_user_action(ApprovalUserAction.ACTION_AMEND_APPROVAL.format(self.approval.id),request) @@ -1998,7 +2109,6 @@ def vessel_on_proposal(self): return vessel_exists - def update_sticker_doc_filename(instance, filename): return '{}/stickers/batch/{}'.format(settings.MEDIA_APP_DIR, filename) @@ -2203,7 +2313,7 @@ def create_fee_lines(self): @property def assessor_group(self): - return Group.objects.get(name="Mooring Licensing - Assessors: Waiting List") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Assessors: Waiting List") @property def approver_group(self): @@ -2211,21 +2321,27 @@ def approver_group(self): @property def assessor_recipients(self): - return [i.email for i in self.assessor_group.user_set.all()] + return [retrieve_email_userro(id).email for id in self.assessor_group.get_system_group_member_ids()] @property def approver_recipients(self): return [] def is_assessor(self, user): - return user in self.assessor_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + # return user in self.assessor_group.user_set.all() + return user in self.assessor_group.get_system_group_member_ids() #def is_approver(self, user): # return False def is_approver(self, user): + if isinstance(user, EmailUserRO): + user = user.id #return user in self.approver_group.user_set.all() - return user in self.assessor_group.user_set.all() + # return user in self.assessor_group.user_set.all() + return user in self.assessor_group.get_system_group_member_ids() def save(self, *args, **kwargs): super(WaitingListApplication, self).save(*args, **kwargs) @@ -2238,9 +2354,14 @@ def save(self, *args, **kwargs): def send_emails_after_payment_success(self, request): attachments = [] if self.invoice: - invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', self.invoice,) - attachment = ('invoice#{}.pdf'.format(self.invoice.reference), invoice_bytes, 'application/pdf') - attachments.append(attachment) + # invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', self.invoice,) + # api_key = settings.LEDGER_API_KEY + # url = settings.LEDGER_API_URL + '/ledgergw/invoice-pdf/' + api_key + '/' + self.invoice.reference + url = get_invoice_url(self.invoice.reference, request) + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = ('invoice#{}.pdf'.format(self.invoice.reference), invoice_pdf.content, 'application/pdf') + attachments.append(attachment) ret_value = send_confirmation_email_upon_submit(request, self, True, attachments) if not self.auto_approve: send_notification_email_upon_submit_to_assessor(request, self, attachments) @@ -2389,7 +2510,8 @@ def create_fee_lines(self): @property def assessor_group(self): - return Group.objects.get(name="Mooring Licensing - Assessors: Annual Admission") + # return Group.objects.get(name="Mooring Licensing - Assessors: Annual Admission") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Assessors: Annual Admission") @property def approver_group(self): @@ -2397,21 +2519,27 @@ def approver_group(self): @property def assessor_recipients(self): - return [i.email for i in self.assessor_group.user_set.all()] + # return [i.email for i in self.assessor_group.user_set.all()] + return [retrieve_email_userro(id).email for id in self.assessor_group.get_system_group_member_ids()] @property def approver_recipients(self): return [] def is_assessor(self, user): - return user in self.assessor_group.user_set.all() + # return user in dself.assessor_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.assessor_group.get_system_group_member_ids() #def is_approver(self, user): # return False def is_approver(self, user): - #return user in self.approver_group.user_set.all() - return user in self.assessor_group.user_set.all() + # return user in self.assessor_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.assessor_group.get_system_group_member_ids() def save(self, *args, **kwargs): #application_type_acronym = self.application_type.acronym if self.application_type else None @@ -2425,9 +2553,14 @@ def save(self, *args, **kwargs): def send_emails_after_payment_success(self, request): attachments = [] if self.invoice: - invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', self.invoice,) - attachment = ('invoice#{}.pdf'.format(self.invoice.reference), invoice_bytes, 'application/pdf') - attachments.append(attachment) + # invoice_bytes = create_invoice_pdf_bytes('invoice.pdf', self.invoice,) + # attachment = ('invoice#{}.pdf'.format(self.invoice.reference), invoice_bytes, 'application/pdf') + # attachments.append(attachment) + url = get_invoice_url(self.invoice.reference, request) + invoice_pdf = requests.get(url=url) + if invoice_pdf.status_code == 200: + attachment = (f'invoice#{self.invoice.reference}', invoice_pdf.content, 'application/pdf') + attachments.append(attachment) ret_value = send_confirmation_email_upon_submit(request, self, True, attachments) if not self.auto_approve: send_notification_email_upon_submit_to_assessor(request, self, attachments) @@ -2595,25 +2728,34 @@ def get_due_date_for_endorsement_by_target_date(self, target_date=timezone.local @property def assessor_group(self): - return Group.objects.get(name="Mooring Licensing - Assessors: Authorised User") + # return Group.objects.get(name="Mooring Licensing - Assessors: Authorised User") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Assessors: Authorised User") @property def approver_group(self): - return Group.objects.get(name="Mooring Licensing - Approvers: Authorised User") + # return Group.objects.get(name="Mooring Licensing - Approvers: Authorised User") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Approvers: Authorised User") @property def assessor_recipients(self): - return [i.email for i in self.assessor_group.user_set.all()] + # return [i.email for i in self.assessor_group.user_set.all()] + return [retrieve_email_userro(i).email for i in self.assessor_group.get_system_group_member_ids()] @property def approver_recipients(self): - return [i.email for i in self.approver_group.user_set.all()] + # return [i.email for i in self.approver_group.user_set.all()] + return [retrieve_email_userro(i).email for i in self.approver_group.get_system_group_member_ids()] def is_assessor(self, user): - return user in self.assessor_group.user_set.all() + # return user in self.assessor_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.assessor_group.get_system_group_member_ids() def is_approver(self, user): - return user in self.approver_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.approver_group.get_system_group_member_ids() def save(self, *args, **kwargs): super(AuthorisedUserApplication, self).save(*args, **kwargs) @@ -2994,25 +3136,41 @@ def get_document_upload_url(self, request): @property def assessor_group(self): - return Group.objects.get(name="Mooring Licensing - Assessors: Mooring Licence") + # return Group.objects.get(name="Mooring Licensing - Assessors: Mooring Licence") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Assessors: Mooring Licence") @property def approver_group(self): - return Group.objects.get(name="Mooring Licensing - Approvers: Mooring Licence") + # return Group.objects.get(name="Mooring Licensing - Approvers: Mooring Licence") + return ledger_api_client.managed_models.SystemGroup.objects.get(name="Mooring Licensing - Approvers: Mooring Licence") @property def assessor_recipients(self): - return [i.email for i in self.assessor_group.user_set.all()] + # return [i.email for i in self.assessor_group.user_set.all()] + emails = [] + for id in self.assessor_group.get_system_group_member_ids(): + emails.append(retrieve_email_userro(id).email) + return emails @property def approver_recipients(self): - return [i.email for i in self.approver_group.user_set.all()] + # return [i.email for i in self.approver_group.user_set.all()] + emails = [] + for id in self.approver_group.get_system_group_member_ids(): + emails.append(retrieve_email_userro(id).email) + return emails def is_assessor(self, user): - return user in self.assessor_group.user_set.all() + # return user in self.assessor_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.assessor_group.get_system_group_member_ids() def is_approver(self, user): - return user in self.approver_group.user_set.all() + # return user in self.approver_group.user_set.all() + if isinstance(user, EmailUserRO): + user = user.id + return user in self.approver_group.get_system_group_member_ids() def save(self, *args, **kwargs): super(MooringLicenceApplication, self).save(*args, **kwargs) @@ -3230,8 +3388,18 @@ class Meta: app_label = 'mooringlicensing' +class ProposalLogEntryManager(models.Manager): + def create(self, *args, **kwargs): + if 'customer' in kwargs and isinstance(kwargs['customer'], EmailUserRO): + kwargs['customer'] = kwargs['customer'].id + if 'staff' in kwargs and isinstance(kwargs['staff'], EmailUserRO): + kwargs['staff'] = kwargs['staff'].id + return super(ProposalLogEntryManager, self).create(*args, **kwargs) + + class ProposalLogEntry(CommunicationsLogEntry): proposal = models.ForeignKey(Proposal, related_name='comms_logs', on_delete=models.CASCADE) + objects = ProposalLogEntryManager() def __str__(self): return '{} - {}'.format(self.reference, self.subject) @@ -3328,7 +3496,7 @@ def specification_display(self): return self.get_mooring_bookings_mooring_specification_display() def log_user_action(self, action, request): - return MooringUserAction.log_action(self, action, request.user) + return MooringUserAction.log_action(self, action, request.user.id) @property def status(self): @@ -3383,7 +3551,7 @@ class Meta: def log_action(cls, mooring, action, user): return cls.objects.create( mooring=mooring, - who=user, + who=user.id if user else None, what=str(action) ) @@ -3528,9 +3696,9 @@ class VesselDetails(RevisionedMixin): # ManyToManyField link in Proposal vessel = models.ForeignKey(Vessel, on_delete=models.CASCADE) vessel_name = models.CharField(max_length=400) vessel_length = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') # does not exist in MB - vessel_draft = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') + vessel_draft = models.DecimalField(max_digits=8, decimal_places=2) vessel_beam = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') - vessel_weight = models.DecimalField(max_digits=8, decimal_places=2, default='0.00') # tonnage + vessel_weight = models.DecimalField(max_digits=8, decimal_places=2) # tonnage berth_mooring = models.CharField(max_length=200, blank=True) created = models.DateTimeField(default=timezone.now) updated = models.DateTimeField(auto_now=True) @@ -3548,7 +3716,7 @@ def __str__(self): @property def vessel_applicable_length(self): - return self.vessel_length + return float(self.vessel_length) class CompanyOwnership(RevisionedMixin): @@ -3681,7 +3849,8 @@ class Meta: class Owner(RevisionedMixin): - emailuser = models.OneToOneField(EmailUser, on_delete=models.CASCADE) + # emailuser = models.OneToOneField(EmailUser, on_delete=models.CASCADE) + emailuser = models.IntegerField(unique=True) # unique=True keeps the OneToOne relation # add on approval only vessels = models.ManyToManyField(Vessel, through=VesselOwnership) # these owner/vessel association @@ -3689,8 +3858,17 @@ class Meta: verbose_name_plural = "Owners" app_label = 'mooringlicensing' + @property + def emailuser_obj(self): + return retrieve_email_userro(self.emailuser) + def __str__(self): - return self.emailuser.get_full_name() + if self.emailuser: + from mooringlicensing.ledger_api_utils import retrieve_email_userro + return retrieve_email_userro(self.emailuser).get_full_name() + else: + return '' + # return self.emailuser.get_full_name() class Company(RevisionedMixin): @@ -3802,7 +3980,8 @@ class ProposalRequest(models.Model): proposal = models.ForeignKey(Proposal, related_name='proposalrequest_set', on_delete=models.CASCADE) subject = models.CharField(max_length=200, blank=True) text = models.TextField(blank=True) - officer = models.ForeignKey(EmailUser, null=True, on_delete=models.SET_NULL) + # officer = models.ForeignKey(EmailUser, null=True, on_delete=models.SET_NULL) + officer = models.IntegerField(null=True, blank=True) def __str__(self): return '{} - {}'.format(self.subject, self.text) @@ -3855,7 +4034,7 @@ def generate_amendment(self,request): proposal.log_user_action(ProposalUserAction.ACTION_ID_REQUEST_AMENDMENTS, request) # Create a log entry for the organisation applicant_field = getattr(proposal, proposal.applicant_field) - applicant_field.log_user_action(ProposalUserAction.ACTION_ID_REQUEST_AMENDMENTS, request) + # applicant_field.log_user_action(ProposalUserAction.ACTION_ID_REQUEST_AMENDMENTS, request) # send email @@ -3868,7 +4047,8 @@ def generate_amendment(self,request): class ProposalDeclinedDetails(models.Model): proposal = models.OneToOneField(Proposal, null=True, on_delete=models.SET_NULL) - officer = models.ForeignKey(EmailUser, null=True, on_delete=models.SET_NULL) + # officer = models.ForeignKey(EmailUser, null=True, on_delete=models.SET_NULL) + officer = models.IntegerField(null=True, blank=True) reason = models.TextField(blank=True) cc_email = models.TextField(null=True) @@ -3876,7 +4056,7 @@ class Meta: app_label = 'mooringlicensing' -@python_2_unicode_compatible +# @python_2_unicode_compatible class ProposalStandardRequirement(RevisionedMixin): text = models.TextField() code = models.CharField(max_length=10, unique=True) @@ -3967,11 +4147,12 @@ class Meta: def log_action(cls, proposal, action, user=None): return cls.objects.create( proposal=proposal, - who=user, + who=user.id if isinstance(user, EmailUserRO) else user, what=str(action) ) - who = models.ForeignKey(EmailUser, null=True, blank=True, on_delete=models.SET_NULL) + # who = models.ForeignKey(EmailUser, null=True, blank=True, on_delete=models.SET_NULL) + who = models.IntegerField(null=True, blank=True) when = models.DateTimeField(null=False, blank=False, auto_now_add=True) what = models.TextField(blank=False) proposal = models.ForeignKey(Proposal, related_name='action_logs', on_delete=models.CASCADE) diff --git a/mooringlicensing/components/proposals/serializers.py b/mooringlicensing/components/proposals/serializers.py index 1286d970e..6ecce37c2 100644 --- a/mooringlicensing/components/proposals/serializers.py +++ b/mooringlicensing/components/proposals/serializers.py @@ -1,15 +1,18 @@ import logging -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE import pytz from datetime import datetime from decimal import Decimal from math import ceil from django.conf import settings -from ledger.accounts.models import EmailUser,Address -from ledger.payments.invoice.models import Invoice +# from ledger.accounts.models import EmailUser,Address +# from ledger.payments.invoice.models import Invoice +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Invoice, Address from mooringlicensing.components.main.models import ApplicationType +# from mooringlicensing.components.main.utils import retrieve_email_user from mooringlicensing.components.payments_ml.models import FeeConstructor from mooringlicensing.components.proposals.models import ( Proposal, @@ -30,27 +33,21 @@ ProposalType, Company, CompanyOwnership, - Mooring, MooringLicenceApplication, AuthorisedUserApplication, AnnualAdmissionApplication, + Mooring, MooringLicenceApplication, AuthorisedUserApplication ) -from mooringlicensing.settings import PROPOSAL_TYPE_AMENDMENT, PROPOSAL_TYPE_RENEWAL, PROPOSAL_TYPE_NEW -from mooringlicensing.components.approvals.models import MooringLicence, MooringOnApproval, AuthorisedUserPermit, \ - AnnualAdmissionPermit -from mooringlicensing.components.main.serializers import CommunicationLogEntrySerializer, InvoiceSerializer +from mooringlicensing.ledger_api_utils import retrieve_email_userro, get_invoice_payment_status +from mooringlicensing.components.approvals.models import MooringLicence, MooringOnApproval +from mooringlicensing.components.main.serializers import CommunicationLogEntrySerializer, InvoiceSerializer, \ + EmailUserSerializer from mooringlicensing.components.users.serializers import UserSerializer -from mooringlicensing.components.users.serializers import UserAddressSerializer, DocumentSerializer +from mooringlicensing.components.users.serializers import UserAddressSerializer from rest_framework import serializers -from django.db.models import Q -from reversion.models import Version from mooringlicensing.helpers import is_internal -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) -class EmailUserSerializer(serializers.ModelSerializer): - class Meta: - model = EmailUser - fields = ('id','email','first_name','last_name','title','organisation') - class EmailUserAppViewSerializer(serializers.ModelSerializer): residential_address = UserAddressSerializer() @@ -134,9 +131,9 @@ class Meta: class BaseProposalSerializer(serializers.ModelSerializer): readonly = serializers.SerializerMethodField(read_only=True) documents_url = serializers.SerializerMethodField() - allowed_assessors = EmailUserSerializer(many=True) - submitter = EmailUserSerializer() - + # allowed_assessors = EmailUserSerializer(many=True) + allowed_assessors = serializers.SerializerMethodField() + # submitter = EmailUserSerializer() get_history = serializers.ReadOnlyField() application_type_code = serializers.SerializerMethodField() application_type_text = serializers.SerializerMethodField() @@ -243,6 +240,10 @@ class Meta: ) read_only_fields=('documents',) + def get_allowed_assessors(self, obj): + serializer = EmailUserSerializer(obj.allowed_assessors, many=True) + return serializer.data + def get_vessel_on_proposal(self, obj): return obj.vessel_on_proposal() @@ -524,7 +525,8 @@ def get_invoices(self, obj): class ListProposalSerializer(BaseProposalSerializer): - submitter = EmailUserSerializer() + # submitter = EmailUserSerializer() + submitter = serializers.SerializerMethodField(read_only=True) applicant = serializers.CharField(read_only=True) processing_status = serializers.SerializerMethodField() customer_status = serializers.SerializerMethodField() @@ -591,17 +593,31 @@ class Meta: 'invoice_links', ) + def get_submitter(self, obj): + if obj.submitter: + from mooringlicensing.ledger_api_utils import retrieve_email_userro + email_user = retrieve_email_userro(obj.submitter) + return EmailUserSerializer(email_user).data + else: + return "" + def get_invoice_links(self, proposal): links = "" # pdf for invoice in proposal.invoices_display(): - links += "
#{}
".format( - invoice.reference, invoice.reference) + # links += "
#{}
".format( + # invoice.reference, invoice.reference) + # api_key = settings.LEDGER_API_KEY + # url = settings.LEDGER_API_URL + '/ledgergw/invoice-pdf/' + settings.LEDGER_API_KEY + '/' + invoice.reference + # url = get_invoice_url(invoice.reference) + url = f'/ledger-toolkit-api/invoice-pdf/{invoice.reference}/' + links += f"
#{invoice.reference}
" if self.context.get('request') and is_internal(self.context.get('request')) and proposal.application_fees.count(): # paid invoices url invoices_str='' for inv in proposal.invoices_display(): - if inv.payment_status == 'paid': + payment_status = get_invoice_payment_status(inv.id) + if payment_status == 'paid': invoices_str += 'invoice={}&'.format(inv.reference) if invoices_str: invoices_str = invoices_str[:-1] @@ -696,6 +712,9 @@ class Meta: def validate(self, data): custom_errors = {} + if self.instance.proposal_type_id != 1: + if self.instance.previous_application.preferred_bay_id != data.get('preferred_bay_id'): + custom_errors["Preferred bay"] = "You can not change a preferred bay" if self.context.get("action") == 'submit': if not data.get("preferred_bay_id"): custom_errors["Mooring Details"] = "You must choose a mooring bay" @@ -843,7 +862,7 @@ def validate(self, data): # check that the site_licensee_email matches the Mooring Licence holder if mooring_id and Mooring.objects.get(id=mooring_id): mooring_licence = Mooring.objects.get(id=mooring_id).mooring_licence - if mooring_licence.submitter.email.lower().strip() != site_licensee_email.lower().strip(): + if mooring_licence.submitter_obj.email.lower().strip() != site_licensee_email.lower().strip(): custom_errors["Site Licensee Email"] = "This site licensee email does not hold the licence for the selected mooring" if custom_errors.keys(): raise serializers.ValidationError(custom_errors) @@ -885,7 +904,8 @@ class InternalProposalSerializer(BaseProposalSerializer): applicant = serializers.CharField(read_only=True) processing_status = serializers.SerializerMethodField(read_only=True) customer_status = serializers.SerializerMethodField(read_only=True) - submitter = UserSerializer() + # submitter = UserSerializer() + submitter = serializers.SerializerMethodField() proposaldeclineddetails = ProposalDeclinedDetailsSerializer() assessor_mode = serializers.SerializerMethodField() current_assessor = serializers.SerializerMethodField() @@ -1010,6 +1030,16 @@ class Meta: 'requirements', ) + def get_submitter(self, obj): + if obj.submitter: + from mooringlicensing.ledger_api_utils import retrieve_email_userro + email_user = retrieve_email_userro(obj.submitter) + # return EmailUserSerializer(email_user).data + return UserSerializer(email_user).data + else: + return "" + + def get_vessel_on_proposal(self, obj): return obj.vessel_on_proposal() @@ -1076,23 +1106,26 @@ def get_authorised_user_moorings(self, obj): moorings = [] if type(obj.child_obj) == AuthorisedUserApplication and obj.approval: for moa in obj.approval.mooringonapproval_set.all(): - suitable_for_mooring = True - # only do check if vessel details exist - if obj.vessel_details: - suitable_for_mooring = moa.mooring.suitable_vessel(obj.vessel_details) - color = '#000000' if suitable_for_mooring else '#FF0000' - moorings.append({ - "id": moa.id, - "mooring_name": '{}'.format(color, moa.mooring.name), - "bay": '{}'.format(color, str(moa.mooring.mooring_bay)), - "site_licensee": 'RIA Allocated'.format(color) if not moa.site_licensee else - 'User Requested'.format(color), - "status": 'Current'.format(color) if not moa.end_date else - 'Historical'.format(color), - "checked": True if suitable_for_mooring and not moa.end_date else False, - "suitable_for_mooring": suitable_for_mooring, - "mooring_licence_current": moa.mooring.mooring_licence.status in ['current', 'suspended'], - }) + if moa.mooring.mooring_licence is not None: + suitable_for_mooring = True + # only do check if vessel details exist + if obj.vessel_details: + suitable_for_mooring = moa.mooring.suitable_vessel(obj.vessel_details) + color = '#000000' if suitable_for_mooring else '#FF0000' + #import ipdb; ipdb.set_trace() + moorings.append({ + "id": moa.id, + "mooring_name": '{}'.format(color, moa.mooring.name), + "bay": '{}'.format(color, str(moa.mooring.mooring_bay)), + "site_licensee": 'RIA Allocated'.format(color) if not moa.site_licensee else + 'User Requested'.format(color), + "status": 'Current'.format(color) if not moa.end_date else + 'Historical'.format(color), + "checked": True if suitable_for_mooring and not moa.end_date else False, + "suitable_for_mooring": suitable_for_mooring, + #"mooring_licence_current": moa.mooring.mooring_licence.status in ['current', 'suspended'] if moa.mooring.mooring_licence else False, + "mooring_licence_current": moa.mooring.mooring_licence.status in ['current', 'suspended'], + }) return moorings def get_mooring_licence_vessels(self, obj): @@ -1159,7 +1192,9 @@ def get_assessor_data(self,obj): return obj.assessor_data def get_fee_invoice_url(self,obj): - url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = '/payments/invoice-pdf/{}'.format(obj.invoice.reference) if obj.fee_paid else None + # url = get_invoice_url(obj.invoice.reference) if obj.invoice else '' + url = f'/ledger-toolkit-api/invoice-pdf/{obj.invoice.reference}/' if obj.invoice else '' return url @@ -1173,7 +1208,7 @@ class Meta: def get_who(self, obj): ret_name = 'System' if obj.who: - name = obj.who.get_full_name() + name = retrieve_email_userro(obj.who).get_full_name() name = name.strip() if name: ret_name = name @@ -1392,7 +1427,7 @@ class Meta: ) def get_emailuser(self, obj): - serializer = EmailUserSerializer(obj.owner.emailuser) + serializer = EmailUserSerializer(obj.owner.emailuser_obj) return serializer.data def get_vessel_details(self, obj): @@ -1506,10 +1541,13 @@ class Meta: ) def get_action_link(self, obj): - return '/internal/person/{}'.format(obj.owner.emailuser.id) + # return '/internal/person/{}'.format(obj.owner.emailuser.id) + return '/internal/person/{}'.format(obj.owner.emailuser) def get_owner_full_name(self, obj): - return obj.owner.emailuser.get_full_name() + # return obj.owner.emailuser.get_full_name() + owner = retrieve_email_userro(obj.owner.emailuser) + return owner.get_full_name() def get_applicable_percentage(self, obj): if obj.company_ownership: @@ -1530,7 +1568,9 @@ def get_end_date(self, obj): return end_date_str def get_owner_phone_number(self, obj): - return obj.owner.emailuser.phone_number if obj.owner.emailuser.phone_number else obj.owner.emailuser.mobile_number + # return obj.owner.emailuser.phone_number if obj.owner.emailuser.phone_number else obj.owner.emailuser.mobile_number + owner = retrieve_email_userro(obj.owner.emailuser) + return owner.phone_number if owner.phone_number else owner.mobile_number def get_individual_owner(self, obj): individual_owner = True diff --git a/mooringlicensing/components/proposals/utils.py b/mooringlicensing/components/proposals/utils.py index 858e666d6..c05f66dda 100644 --- a/mooringlicensing/components/proposals/utils.py +++ b/mooringlicensing/components/proposals/utils.py @@ -2,7 +2,8 @@ from decimal import Decimal from django.db import transaction -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser +from ledger_api_client.ledger_models import EmailUserRO as EmailUser from mooringlicensing import settings import json @@ -49,6 +50,8 @@ WaitingListAllocation, AuthorisedUserPermit, Approval ) +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 import traceback import os @@ -60,7 +63,8 @@ import logging -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) def create_data_from_form(schema, post_data, file_data, post_data_index=None,special_fields=[],assessor_data=False): @@ -349,6 +353,12 @@ def save_proponent_data(instance, request, viewset): elif type(instance.child_obj) == MooringLicenceApplication: save_proponent_data_mla(instance, request, viewset) + # Save request.user details in a JSONField not to overwrite the details of it. + serializer = UserSerializer(request.user, context={'request':request}) + if instance: + instance.personal_details = serializer.data + instance.save() + def save_proponent_data_aaa(instance, request, viewset): print(request.data) @@ -373,7 +383,8 @@ def save_proponent_data_aaa(instance, request, viewset): serializer.is_valid(raise_exception=True) instance = serializer.save() if viewset.action == 'submit': - if instance.invoice and instance.invoice.payment_status in ['paid', 'over_paid']: + # if instance.invoice and instance.invoice.payment_status in ['paid', 'over_paid']: + if instance.invoice and get_invoice_payment_status(instance.id) in ['paid', 'over_paid']: # Save + Submit + Paid ==> We have to update the status # Probably this is the case that assessor put back this application to external and then external submit this. logger.info('Proposal {} has been submitted but already paid. Update the status of it to {}'.format(instance.lodgement_number, Proposal.PROCESSING_STATUS_WITH_ASSESSOR)) @@ -403,7 +414,8 @@ def save_proponent_data_wla(instance, request, viewset): serializer.is_valid(raise_exception=True) instance = serializer.save() if viewset.action == 'submit': - if instance.invoice and instance.invoice.payment_status in ['paid', 'over_paid']: + # if instance.invoice and instance.invoice.payment_status in ['paid', 'over_paid']: + if instance.invoice and get_invoice_payment_status(instance.invoice.id) in ['paid', 'over_paid']: # Save + Submit + Paid ==> We have to update the status # Probably this is the case that assessor put back this application to external and then external submit this. logger.info('Proposal {} has been submitted but already paid. Update the status of it to {}'.format(instance.lodgement_number, Proposal.PROCESSING_STATUS_WITH_ASSESSOR)) @@ -437,6 +449,7 @@ def save_proponent_data_mla(instance, request, viewset): if viewset.action == 'submit': instance.child_obj.process_after_submit(request) + instance.refresh_from_db() def save_proponent_data_aua(instance, request, viewset): @@ -464,6 +477,7 @@ def save_proponent_data_aua(instance, request, viewset): serializer.save() if viewset.action == 'submit': instance.child_obj.process_after_submit(request) + instance.refresh_from_db() # draft and submit @@ -821,7 +835,7 @@ def store_vessel_ownership(request, vessel, instance=None): else: vessel_ownership_data['company_ownership'] = None vessel_ownership_data['vessel'] = vessel.id - owner, created = Owner.objects.get_or_create(emailuser=request.user) + owner, created = Owner.objects.get_or_create(emailuser=request.user.id) vessel_ownership_data['owner'] = owner.id vessel_ownership, created = VesselOwnership.objects.get_or_create( diff --git a/mooringlicensing/components/users/api.py b/mooringlicensing/components/users/api.py index f45ce849a..46aba30fc 100755 --- a/mooringlicensing/components/users/api.py +++ b/mooringlicensing/components/users/api.py @@ -1,63 +1,68 @@ import traceback -import base64 -import geojson -from six.moves.urllib.parse import urlparse -from wsgiref.util import FileWrapper +# import base64 +# import geojson +# from six.moves.urllib.parse import urlparse +# from wsgiref.util import FileWrapper from django.db.models import Q, Min, CharField, Value from django.db.models.functions import Concat from django.db import transaction -from django.http import HttpResponse -from django.core.files.base import ContentFile +# from django.http import HttpResponse +# from django.core.files.base import ContentFile from django.core.exceptions import ValidationError from django.conf import settings -from django.contrib import messages -from django.views.decorators.http import require_http_methods -from django.views.decorators.csrf import csrf_exempt -from django.utils import timezone +# from django.contrib import messages +# from django.views.decorators.http import require_http_methods +# from django.views.decorators.csrf import csrf_exempt +# from django.utils import timezone from django_countries import countries from rest_framework import viewsets, serializers, status, generics, views -from rest_framework.decorators import detail_route, list_route,renderer_classes +# from rest_framework.decorators import detail_route, list_route,renderer_classes +from rest_framework.decorators import action as detail_route +# from rest_framework.decorators import action as list_route +from rest_framework.decorators import renderer_classes from rest_framework.response import Response from rest_framework.renderers import JSONRenderer -from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission -from rest_framework.pagination import PageNumberPagination -from datetime import datetime, timedelta -from collections import OrderedDict +# from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, BasePermission +# from rest_framework.pagination import PageNumberPagination +# from datetime import datetime, timedelta +# from collections import OrderedDict from django.core.cache import cache -from ledger.accounts.models import EmailUser,Address, Profile, EmailIdentity, EmailUserAction -from ledger.address.models import Country -from datetime import datetime,timedelta, date -from mooringlicensing.components.main.decorators import ( - basic_exception_handler, - timeit, - query_debugger - ) -from mooringlicensing.components.organisations.models import ( - Organisation, - ) +# from ledger.accounts.models import EmailUser,Address, Profile, EmailIdentity, EmailUserAction +from ledger_api_client.ledger_models import EmailUserRO as EmailUser, Address +# from ledger.address.models import Country +# from datetime import datetime,timedelta, date +# from mooringlicensing.components.main.decorators import ( +# basic_exception_handler, +# timeit, +# query_debugger +# ) +# from mooringlicensing.components.organisations.models import ( +# Organisation, +# ) from mooringlicensing.components.proposals.serializers import EmailUserAppViewSerializer - -from mooringlicensing.components.users.serializers import ( - UserSerializer, - UserFilterSerializer, - UserAddressSerializer, - PersonalSerializer, - ContactSerializer, - EmailUserActionSerializer, - EmailUserCommsSerializer, - EmailUserLogEntrySerializer, - UserSystemSettingsSerializer, - ) +from mooringlicensing.components.users.models import EmailUserLogEntry +from mooringlicensing.components.users.serializers import ( + UserSerializer, + UserFilterSerializer, + UserAddressSerializer, + PersonalSerializer, + ContactSerializer, + # EmailUserActionSerializer, + EmailUserCommsSerializer, + EmailUserLogEntrySerializer, + UserSystemSettingsSerializer, +) from mooringlicensing.components.organisations.serializers import ( OrganisationRequestDTSerializer, ) from mooringlicensing.components.main.models import UserSystemSettings -from mooringlicensing.components.main.process_document import ( - process_generic_document, - ) +# from mooringlicensing.components.main.process_document import ( +# process_generic_document, +# ) import logging -logger = logging.getLogger('mooringlicensing') +# logger = logging.getLogger('mooringlicensing') +logger = logging.getLogger(__name__) class GetCountries(views.APIView): @@ -152,7 +157,7 @@ class UserViewSet(viewsets.ModelViewSet): queryset = EmailUser.objects.all() serializer_class = UserSerializer - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def update_personal(self, request, *args, **kwargs): try: instance = self.get_object() @@ -171,7 +176,7 @@ def update_personal(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def update_contact(self, request, *args, **kwargs): try: instance = self.get_object() @@ -190,7 +195,7 @@ def update_contact(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def update_address(self, request, *args, **kwargs): try: with transaction.atomic(): @@ -247,7 +252,7 @@ def update_address(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def update_system_settings(self, request, *args, **kwargs): try: instance = self.get_object() @@ -270,15 +275,18 @@ def update_system_settings(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) def upload_id(self, request, *args, **kwargs): try: instance = self.get_object() instance.upload_identification(request) with transaction.atomic(): instance.save() - instance.log_user_action(EmailUserAction.ACTION_ID_UPDATE.format( - '{} {} ({})'.format(instance.first_name, instance.last_name, instance.email)), request) + + # TODO: log user action + # instance.log_user_action(EmailUserAction.ACTION_ID_UPDATE.format( + # '{} {} ({})'.format(instance.first_name, instance.last_name, instance.email)), request) + serializer = UserSerializer(instance, partial=True) return Response(serializer.data) except serializers.ValidationError: @@ -291,7 +299,7 @@ def upload_id(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET', ]) + @detail_route(methods=['GET', ], detail=True) def pending_org_requests(self, request, *args, **kwargs): try: instance = self.get_object() @@ -311,13 +319,15 @@ def pending_org_requests(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET', ]) + @detail_route(methods=['GET', ], detail=True) def action_log(self, request, *args, **kwargs): try: instance = self.get_object() qs = instance.action_logs.all() - serializer = EmailUserActionSerializer(qs, many=True) - return Response(serializer.data) + + # TODO: return a list of user actions + # serializer = EmailUserActionSerializer(qs, many=True) + # return Response(serializer.data) except serializers.ValidationError: print(traceback.print_exc()) raise @@ -328,11 +338,13 @@ def action_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['GET',]) + @detail_route(methods=['GET',], detail=True) def comms_log(self, request, *args, **kwargs): try: instance = self.get_object() - qs = instance.comms_logs.all() + # qs = instance.comms_logs.all() + qs = EmailUserLogEntry.objects.filter(email_user_id=instance.id) + # qs = EmailUserLogEntry.objects.filter(email_user_id=132848) serializer = EmailUserCommsSerializer(qs, many=True) return Response(serializer.data) except serializers.ValidationError: @@ -345,7 +357,7 @@ def comms_log(self, request, *args, **kwargs): print(traceback.print_exc()) raise serializers.ValidationError(str(e)) - @detail_route(methods=['POST',]) + @detail_route(methods=['POST',], detail=True) @renderer_classes((JSONRenderer,)) def add_comms_log(self, request, *args, **kwargs): try: diff --git a/mooringlicensing/components/users/models.py b/mooringlicensing/components/users/models.py new file mode 100644 index 000000000..e2fd87e3b --- /dev/null +++ b/mooringlicensing/components/users/models.py @@ -0,0 +1,43 @@ +from __future__ import unicode_literals + +from django.db import models + +from mooringlicensing import settings +from mooringlicensing.components.main.models import CommunicationsLogEntry, Document + + +class EmailUserLogEntry(CommunicationsLogEntry): + # emailuser = models.ForeignKey(EmailUser, related_name='comms_logs') + email_user_id = models.IntegerField(null=True, blank=True) + + def __init__(self, *args, **kwargs): + if 'customer' in kwargs and not isinstance(kwargs.get('customer', 0), int): + kwargs['customer'] = kwargs['customer'].id + if 'staff' in kwargs and not isinstance(kwargs.get('staff', 0), int): + kwargs['staff'] = kwargs['staff'].id + if 'email_user_id' in kwargs and not isinstance(kwargs.get('email_user_id', 0), int): + kwargs['email_user_id'] = kwargs['email_user_id'].id + super(EmailUserLogEntry, self).__init__(*args, **kwargs) + + def save(self, **kwargs): + # save the request id if the reference not provided + if not self.reference: + # self.reference = self.emailuser.id + self.reference = self.email_user_id + + super(EmailUserLogEntry, self).save(**kwargs) + + class Meta: + app_label = 'mooringlicensing' + + +def update_emailuser_comms_log_filename(instance, filename): + return '{}/emailusers/{}/communications/{}/{}'.format(settings.MEDIA_APP_DIR, instance.log_entry.emailuser.id, instance.id, filename) + + +class EmailUserLogDocument(Document): + log_entry = models.ForeignKey('EmailUserLogEntry',related_name='documents', on_delete=models.CASCADE) + _file = models.FileField(upload_to=update_emailuser_comms_log_filename, max_length=512) + + class Meta: + app_label = 'mooringlicensing' diff --git a/mooringlicensing/components/users/serializers.py b/mooringlicensing/components/users/serializers.py index c1505ef05..4bcfba0ff 100755 --- a/mooringlicensing/components/users/serializers.py +++ b/mooringlicensing/components/users/serializers.py @@ -1,6 +1,6 @@ from django.conf import settings -from ledger.accounts.models import EmailUser,Address, Profile,EmailIdentity, EmailUserAction, EmailUserLogEntry, CommunicationsLogEntry - +# from ledger.accounts.models import EmailUser,Address, Profile,EmailIdentity, EmailUserAction, EmailUserLogEntry, CommunicationsLogEntry +from ledger_api_client.ledger_models import EmailUserRO, Address from mooringlicensing.components.main.serializers import CommunicationLogEntrySerializer from mooringlicensing.components.organisations.models import ( Organisation, @@ -8,10 +8,13 @@ from mooringlicensing.components.main.models import UserSystemSettings, Document#, ApplicationType from mooringlicensing.components.proposals.models import Proposal from mooringlicensing.components.organisations.utils import can_admin_org, is_consultant -from mooringlicensing.helpers import is_mooringlicensing_admin from rest_framework import serializers -from ledger.accounts.utils import in_dbca_domain -from ledger.payments.helpers import is_payment_admin + +from mooringlicensing.components.users.models import EmailUserLogEntry +# from ledger.accounts.utils import in_dbca_domain +from mooringlicensing.helpers import is_mooringlicensing_admin, in_dbca_domain +# from ledger.payments.helpers import is_payment_admin +from ledger_api_client.helpers import is_payment_admin class DocumentSerializer(serializers.ModelSerializer): @@ -57,15 +60,15 @@ class Meta: ) def get_is_admin(self, obj): - user = EmailUser.objects.get(id=self.context.get('user_id')) + user = EmailUserRO.objects.get(id=self.context.get('user_id')) return can_admin_org(obj, user) def get_is_consultant(self, obj): - user = EmailUser.objects.get(id=self.context.get('user_id')) + user = EmailUserRO.objects.get(id=self.context.get('user_id')) return is_consultant(obj, user) def get_email(self, obj): - email = EmailUser.objects.get(id=self.context.get('user_id')).email + email = EmailUserRO.objects.get(id=self.context.get('user_id')).email return email @@ -73,7 +76,7 @@ class UserFilterSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField() class Meta: - model = EmailUser + model = EmailUserRO fields = ( 'id', 'last_name', @@ -104,7 +107,7 @@ class UserSerializer(serializers.ModelSerializer): readonly_dob = serializers.SerializerMethodField() class Meta: - model = EmailUser + model = EmailUserRO fields = ( 'id', 'last_name', @@ -169,10 +172,15 @@ def get_full_name(self, obj): return obj.get_full_name() def get_is_department_user(self, obj): + # if obj.email: + # return in_dbca_domain(obj) + # else: + # return False if obj.email: - return in_dbca_domain(obj) - else: - return False + request = self.context["request"] if self.context else None + if request: + return in_dbca_domain(request) + return False def get_is_payment_admin(self, obj): return is_payment_admin(obj) @@ -196,7 +204,7 @@ def get_is_mooringlicensing_admin(self, obj): class PersonalSerializer(serializers.ModelSerializer): dob = serializers.DateField(format="%d/%m/%Y",input_formats=['%d/%m/%Y'],required=False,allow_null=True) class Meta: - model = EmailUser + model = EmailUserRO fields = ( 'id', 'last_name', @@ -206,7 +214,7 @@ class Meta: class ContactSerializer(serializers.ModelSerializer): class Meta: - model = EmailUser + model = EmailUserRO fields = ( 'id', 'email', @@ -226,25 +234,27 @@ def validate(self, obj): raise serializers.ValidationError('You must provide a mobile/phone number') return obj -class EmailUserActionSerializer(serializers.ModelSerializer): - who = serializers.CharField(source='who.get_full_name') - - class Meta: - model = EmailUserAction - fields = '__all__' +# class EmailUserActionSerializer(serializers.ModelSerializer): +# who = serializers.CharField(source='who.get_full_name') +# +# class Meta: +# model = EmailUserAction +# fields = '__all__' # class EmailUserCommsSerializer(serializers.ModelSerializer): # class Meta: # model = EmailUserLogEntry # fields = '__all__' + class EmailUserCommsSerializer(CommunicationLogEntrySerializer): + # TODO: implement documents = serializers.SerializerMethodField() - type = serializers.CharField(source='log_type') - + # type = serializers.CharField(source='log_type') + # class Meta: model = EmailUserLogEntry - # fields = '__all__' + # # fields = '__all__' fields = ( 'id', 'customer', @@ -257,45 +267,52 @@ class Meta: 'text', 'created', 'staff', - 'emailuser', + # 'emailuser', + 'email_user_id', 'documents', ) read_only_fields = ( 'customer', ) + class CommunicationLogEntrySerializer(serializers.ModelSerializer): - customer = serializers.PrimaryKeyRelatedField(queryset=EmailUser.objects.all(),required=False) - documents = serializers.SerializerMethodField() - class Meta: - model = CommunicationsLogEntry - fields = ( - 'id', - 'customer', - 'to', - 'fromm', - 'cc', - 'log_type', - 'reference', - 'subject' - 'text', - 'created', - 'staff', - 'emailuser', - 'documents' - ) + # TODO: implement + pass +# customer = serializers.PrimaryKeyRelatedField(queryset=EmailUser.objects.all(),required=False) +# documents = serializers.SerializerMethodField() +# class Meta: +# model = CommunicationsLogEntry +# fields = ( +# 'id', +# 'customer', +# 'to', +# 'fromm', +# 'cc', +# 'log_type', +# 'reference', +# 'subject' +# 'text', +# 'created', +# 'staff', +# 'emailuser', +# 'documents' +# ) +# +# def get_documents(self,obj): +# return [[d.name,d._file.url] for d in obj.documents.all()] - def get_documents(self,obj): - return [[d.name,d._file.url] for d in obj.documents.all()] class EmailUserLogEntrySerializer(CommunicationLogEntrySerializer): - documents = serializers.SerializerMethodField() - class Meta: - model = EmailUserLogEntry - fields = '__all__' - read_only_fields = ( - 'customer', - ) - - def get_documents(self,obj): - return [[d.name,d._file.url] for d in obj.documents.all()] + # TODO: implement + pass +# documents = serializers.SerializerMethodField() +# class Meta: +# model = EmailUserLogEntry +# fields = '__all__' +# read_only_fields = ( +# 'customer', +# ) +# +# def get_documents(self,obj): +# return [[d.name,d._file.url] for d in obj.documents.all()] diff --git a/mooringlicensing/components/users/utils.py b/mooringlicensing/components/users/utils.py new file mode 100644 index 000000000..e75e4961f --- /dev/null +++ b/mooringlicensing/components/users/utils.py @@ -0,0 +1,63 @@ +import logging + +from django.conf import settings +from django.core.files.base import ContentFile +from django.core.files.storage import default_storage +from django.core.mail import EmailMultiAlternatives, EmailMessage +from django.utils.encoding import smart_text + +from mooringlicensing.components.users.models import EmailUserLogEntry + + +def _log_user_email(email_message, target_email_user, customer, sender=None, attachments=[]): + # from ledger.accounts.models import EmailUserLogEntry + if isinstance(email_message, (EmailMultiAlternatives, EmailMessage,)): + # TODO this will log the plain text body, should we log the html instead + text = email_message.body + subject = email_message.subject + fromm = smart_text(sender) if sender else smart_text(email_message.from_email) + # the to email is normally a list + if isinstance(email_message.to, list): + to = ','.join(email_message.to) + else: + to = smart_text(email_message.to) + # we log the cc and bcc in the same cc field of the log entry as a ',' comma separated string + all_ccs = [] + if email_message.cc: + all_ccs += list(email_message.cc) + if email_message.bcc: + all_ccs += list(email_message.bcc) + all_ccs = ','.join(all_ccs) + + else: + text = smart_text(email_message) + subject = '' + to = customer + fromm = smart_text(sender) if sender else settings.SYSTEM_NAME_SHORT + ' Automated Message' + all_ccs = '' + + customer = customer + + staff = sender + + kwargs = { + 'subject': subject, + 'text': text, + # 'emailuser': target_email_user if target_email_user else customer, + 'email_user_id': target_email_user if target_email_user else customer, + 'customer': customer, + 'staff': staff, + 'to': to, + 'fromm': fromm, + 'cc': all_ccs + } + + email_entry = EmailUserLogEntry.objects.create(**kwargs) + + for attachment in attachments: + path_to_file = '{}/emailuser/{}/communications/{}'.format(settings.MEDIA_APP_DIR, target_email_user, attachment[0]) + path = default_storage.save(path_to_file, ContentFile(attachment[1])) + email_entry.documents.get_or_create(_file=path_to_file, name=attachment[0]) + + # return email_entry + return None diff --git a/mooringlicensing/context_processors.py b/mooringlicensing/context_processors.py index 3a54674f4..904232ace 100644 --- a/mooringlicensing/context_processors.py +++ b/mooringlicensing/context_processors.py @@ -1,8 +1,17 @@ +from mooringlicensing import settings + + def mooringlicensing_processor(request): ret_dict = {} web_url = request.META.get('HTTP_HOST', None) - return {'public_url': web_url} + return { + 'public_url': web_url, + # 'template_group': 'parksv2', + 'template_group': 'ria', + 'LEDGER_UI_URL': f'{settings.LEDGER_UI_URL}', + 'LEDGER_SYSTEM_ID': f'{settings.LEDGER_SYSTEM_ID}', + } diff --git a/mooringlicensing/doctopdf.py b/mooringlicensing/doctopdf.py index 4054a581e..72a09d0f2 100644 --- a/mooringlicensing/doctopdf.py +++ b/mooringlicensing/doctopdf.py @@ -4,7 +4,6 @@ from django.conf import settings from docxtpl import DocxTemplate from mooringlicensing.components.main.models import GlobalSettings -from mooringlicensing.components.proposals.models import Proposal def create_dcv_permit_pdf_tytes(dcv_permit): diff --git a/mooringlicensing/forms.py b/mooringlicensing/forms.py index 425a03d0e..8e0802683 100755 --- a/mooringlicensing/forms.py +++ b/mooringlicensing/forms.py @@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model from django.forms import Form, ModelForm, CharField, ValidationError, EmailField -from ledger.accounts.models import Profile, Address, Organisation +# from ledger.accounts.models import Profile, Address, Organisation User = get_user_model() @@ -14,110 +14,110 @@ class LoginForm(Form): email = EmailField(max_length=254) -class PersonalForm(ModelForm): - - class Meta: - model = User - fields = ['first_name', 'last_name'] - - def __init__(self, *args, **kwargs): - super(ProfileForm, self).__init__(*args, **kwargs) - self.helper = BaseFormHelper(self) - self.helper.form_id = 'id_form_emailuserprofile_update' - self.helper.attrs = {'novalidate': ''} - # Define the form layout. - self.helper.layout = Layout( - 'first_name', 'last_name', - FormActions( - Submit('save', 'Save', css_class='btn-lg'), - Submit('cancel', 'Cancel') - ) - ) - -class ContactForm(ModelForm): - - class Meta: - model = User - fields = ['email', 'phone_number','mobile_number'] - - def __init__(self, *args, **kwargs): - super(ProfileForm, self).__init__(*args, **kwargs) - self.helper = BaseFormHelper(self) - self.helper.form_id = 'id_form_emailuserprofile_update' - self.helper.attrs = {'novalidate': ''} - # Define the form layout. - self.helper.layout = Layout( - 'phone_number', 'mobile_number','email', - FormActions( - Submit('save', 'Save', css_class='btn-lg'), - Submit('cancel', 'Cancel') - ) - ) - - -class AddressForm(ModelForm): - - class Meta: - model = Address - fields = ['line1', 'line2', 'locality', 'state', 'postcode'] - - def __init__(self, *args, **kwargs): - super(AddressForm, self).__init__(*args, **kwargs) - self.helper = BaseFormHelper(self) - self.helper.form_id = 'id_form_address' - self.helper.attrs = {'novalidate': ''} - self.helper.add_input(Submit('save', 'Save', css_class='btn-lg')) - self.helper.add_input(Submit('cancel', 'Cancel')) - - -class OrganisationAdminForm(ModelForm): - """ModelForm that is used in the Django admin site. - """ - model = Organisation - - def clean_abn(self): - data = self.cleaned_data['abn'] - # If it's changed, check for any existing organisations with the same ABN. - if data and self.instance.abn != data and Organisation.objects.filter(abn=data).exists(): - raise ValidationError('An organisation with this ABN already exists.') - return data - - -class OrganisationForm(OrganisationAdminForm): - - class Meta: - model = Organisation - fields = ['name', 'abn', 'identification'] - - def __init__(self, *args, **kwargs): - super(OrganisationForm, self).__init__(*args, **kwargs) - self.fields['name'].label = 'Company name' - self.fields['identification'].label = 'Certificate of incorporation' - self.fields['identification'].help_text = 'Electronic copy of current certificate (e.g. image/PDF)' - self.helper = BaseFormHelper(self) - self.helper.form_id = 'id_form_organisation' - self.helper.attrs = {'novalidate': ''} - self.helper.add_input(Submit('save', 'Save', css_class='btn-lg')) - self.helper.add_input(Submit('cancel', 'Cancel')) - - -class DelegateAccessForm(Form): - - def __init__(self, *args, **kwargs): - super(DelegateAccessForm, self).__init__(*args, **kwargs) - self.helper = BaseFormHelper(self) - self.helper.add_input(Submit('confirm', 'Confirm', css_class='btn-lg')) - self.helper.add_input(Submit('cancel', 'Cancel')) - - -class UnlinkDelegateForm(ModelForm): - - class Meta: - model = Organisation - exclude = ['name', 'abn', 'identification', 'postal_address', 'billing_address', 'delegates'] - - def __init__(self, *args, **kwargs): - super(UnlinkDelegateForm, self).__init__(*args, **kwargs) - self.helper = BaseFormHelper(self) - self.helper.add_input(Submit('unlink', 'Unlink user', css_class='btn-lg btn-danger')) - self.helper.add_input(Submit('cancel', 'Cancel')) +# class PersonalForm(ModelForm): +# +# class Meta: +# model = User +# fields = ['first_name', 'last_name'] +# +# def __init__(self, *args, **kwargs): +# super(ProfileForm, self).__init__(*args, **kwargs) +# self.helper = BaseFormHelper(self) +# self.helper.form_id = 'id_form_emailuserprofile_update' +# self.helper.attrs = {'novalidate': ''} +# # Define the form layout. +# self.helper.layout = Layout( +# 'first_name', 'last_name', +# FormActions( +# Submit('save', 'Save', css_class='btn-lg'), +# Submit('cancel', 'Cancel') +# ) +# ) +# +# class ContactForm(ModelForm): +# +# class Meta: +# model = User +# fields = ['email', 'phone_number','mobile_number'] +# +# def __init__(self, *args, **kwargs): +# super(ProfileForm, self).__init__(*args, **kwargs) +# self.helper = BaseFormHelper(self) +# self.helper.form_id = 'id_form_emailuserprofile_update' +# self.helper.attrs = {'novalidate': ''} +# # Define the form layout. +# self.helper.layout = Layout( +# 'phone_number', 'mobile_number','email', +# FormActions( +# Submit('save', 'Save', css_class='btn-lg'), +# Submit('cancel', 'Cancel') +# ) +# ) +# +# +# class AddressForm(ModelForm): +# +# class Meta: +# model = Address +# fields = ['line1', 'line2', 'locality', 'state', 'postcode'] +# +# def __init__(self, *args, **kwargs): +# super(AddressForm, self).__init__(*args, **kwargs) +# self.helper = BaseFormHelper(self) +# self.helper.form_id = 'id_form_address' +# self.helper.attrs = {'novalidate': ''} +# self.helper.add_input(Submit('save', 'Save', css_class='btn-lg')) +# self.helper.add_input(Submit('cancel', 'Cancel')) +# +# +# class OrganisationAdminForm(ModelForm): +# """ModelForm that is used in the Django admin site. +# """ +# model = Organisation +# +# def clean_abn(self): +# data = self.cleaned_data['abn'] +# # If it's changed, check for any existing organisations with the same ABN. +# if data and self.instance.abn != data and Organisation.objects.filter(abn=data).exists(): +# raise ValidationError('An organisation with this ABN already exists.') +# return data +# +# +# class OrganisationForm(OrganisationAdminForm): +# +# class Meta: +# model = Organisation +# fields = ['name', 'abn', 'identification'] +# +# def __init__(self, *args, **kwargs): +# super(OrganisationForm, self).__init__(*args, **kwargs) +# self.fields['name'].label = 'Company name' +# self.fields['identification'].label = 'Certificate of incorporation' +# self.fields['identification'].help_text = 'Electronic copy of current certificate (e.g. image/PDF)' +# self.helper = BaseFormHelper(self) +# self.helper.form_id = 'id_form_organisation' +# self.helper.attrs = {'novalidate': ''} +# self.helper.add_input(Submit('save', 'Save', css_class='btn-lg')) +# self.helper.add_input(Submit('cancel', 'Cancel')) +# +# +# class DelegateAccessForm(Form): +# +# def __init__(self, *args, **kwargs): +# super(DelegateAccessForm, self).__init__(*args, **kwargs) +# self.helper = BaseFormHelper(self) +# self.helper.add_input(Submit('confirm', 'Confirm', css_class='btn-lg')) +# self.helper.add_input(Submit('cancel', 'Cancel')) +# +# +# class UnlinkDelegateForm(ModelForm): +# +# class Meta: +# model = Organisation +# exclude = ['name', 'abn', 'identification', 'postal_address', 'billing_address', 'delegates'] +# +# def __init__(self, *args, **kwargs): +# super(UnlinkDelegateForm, self).__init__(*args, **kwargs) +# self.helper = BaseFormHelper(self) +# self.helper.add_input(Submit('unlink', 'Unlink user', css_class='btn-lg btn-danger')) +# self.helper.add_input(Submit('cancel', 'Cancel')) diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue b/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue index 5155af146..351b7f5b1 100755 --- a/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue +++ b/mooringlicensing/frontend/mooringlicensing/src/components/common/applicant.vue @@ -9,7 +9,7 @@ - + + + @@ -88,15 +106,61 @@ export default { system_name: api_endpoints.system_name, allApprovalTypeFilter: ['ml', 'aap', 'aup'], wlaApprovalTypeFilter: ['wla',], + + components_ordered: [], + components: { + 'ApplicationsTable': { + type: 'ApplicationsTable', + approvalTypeFilter: [], + formCollapse: true, + label: "Applications", + // subtitle: "View existing applications and lodge new ones", + subtitle: "View unapproved applications or lodge new ones", + Index: "applications", + subtitle_class_name: "subtitle-l", + }, + 'WaitingListTable': { + type: 'WaitingListTable', + approvalTypeFilter: ['wla',], + formCollapse: true, + label: "Waiting List", + subtitle: "- View and amend your waiting list allocation", + Index: "waiting_list", + }, + 'LicencesAndPermitsTable': { + type: 'LicencesAndPermitsTable', + approvalTypeFilter: ['ml', 'aap', 'aup'], + formCollapse: true, + label: "Licences and Permits", + subtitle: "- View existing licences / permits and renew them", + Index: "licences_and_permits", + }, + 'CompliancesTable': { + type: 'CompliancesTable', + approvalTypeFilter: [], + formCollapse: true, + label: "Compliances", + subtitle: "- View submitted Compliances and submit new ones", + Index: "compliances", + }, + 'AuthorisedUserApplicationsTable': { + type: 'AuthorisedUserApplicationsTable', + approvalTypeFilter: [], + formCollapse: true, + label: "Authorised User Applications for my Endorsement", + subtitle: "", + Index: "authorised_user_applications_for_my_endorsement", + }, + } } }, components:{ - FormSection, - ApplicationsTable, - WaitingListTable, - LicencesAndPermitsTable, - CompliancesTable, - AuthorisedUserApplicationsTable, + 'FormSection': FormSection, + 'ApplicationsTable': ApplicationsTable, + 'WaitingListTable': WaitingListTable, + 'LicencesAndPermitsTable': LicencesAndPermitsTable, + 'CompliancesTable': CompliancesTable, + 'AuthorisedUserApplicationsTable': AuthorisedUserApplicationsTable, }, watch: { @@ -112,8 +176,14 @@ export default { mounted: function () { }, - created: function() { - + created: async function() { + const res = await this.$http.get('/api/external_dashboard_sections_list/') + console.log({res}) + // let name_ordered = ['LicencesAndPermitsTable', 'ApplicationsTable', 'CompliancesTable', 'WaitingListTable', 'AuthorisedUserApplicationsTable', ] + let name_ordered = res.body + for (let name of name_ordered){ + this.components_ordered.push(this.components[name]) + } }, } diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal.vue b/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal.vue index 5d05e10ed..75c88360c 100755 --- a/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal.vue +++ b/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal.vue @@ -114,16 +114,17 @@ @@ -326,9 +327,9 @@ export default { save_applicant_data:function(){ if(this.proposal.applicant_type == 'SUB') { - this.proposal_refs().$refs.profile.updatePersonal(); - this.proposal_refs().$refs.profile.updateAddress(); - this.proposal_refs().$refs.profile.updateContact(); + // this.proposal_refs().$refs.profile.updatePersonal(); + // this.proposal_refs().$refs.profile.updateAddress(); + // this.proposal_refs().$refs.profile.updateContact(); } }, @@ -498,9 +499,7 @@ export default { //await this.post_and_redirect(this.application_fee_url, {'auto_approve': true, 'csrfmiddlewaretoken' : this.csrf_token}); await this.post_and_redirect(this.application_fee_url, {'csrfmiddlewaretoken' : this.csrf_token}); } else if (['wla', 'aaa'].includes(this.proposal.application_type_code)) { - console.log('aho1') await this.post_and_redirect(this.application_fee_url, {'csrfmiddlewaretoken' : this.csrf_token}); - console.log('aho2') } else { await this.post_and_redirect(this.confirmation_url, {'csrfmiddlewaretoken' : this.csrf_token}); //this.$router.push({ @@ -509,7 +508,6 @@ export default { } }); } catch(err) { - console.log('aho3') console.log(err) console.log(typeof(err.body)) await swal({ @@ -751,7 +749,6 @@ export default { this.$nextTick(async () => { try { await this.save_and_pay(); - console.log('aho5') } catch (err) { console.log(err) await swal({ @@ -786,7 +783,6 @@ export default { var formElement = $(postFormStr); $('body').append(formElement); $(formElement).submit(); - console.log('aho4') }, }, diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal_apply.vue b/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal_apply.vue index 8de16056b..a98336bc2 100644 --- a/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal_apply.vue +++ b/mooringlicensing/frontend/mooringlicensing/src/components/external/proposal_apply.vue @@ -225,7 +225,7 @@ export default { selectedApplication: {}, selectedCurrentProposal: null, //selected_application_name: '', - application_types: [], + application_types_and_licences: [], wlaChoices: [], aaaChoices: [], auaChoices: [], @@ -280,7 +280,7 @@ export default { }, methods: { parseApprovals: function() { - this.application_types.forEach(app => { + this.application_types_and_licences.forEach(app => { if (app.code === 'wla' && app.lodgement_number) { this.wlaApprovals.push({ lodgement_number: app.lodgement_number, @@ -306,9 +306,9 @@ export default { }, parseWla: function() { if (this.wlaApprovals.length>1) { - // new app - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { if (app.code === 'wla' && !app.approval_id) { + // new app this.wlaMultiple.push(app) } } @@ -321,7 +321,7 @@ export default { }) } else { // add wla approval to wlaChoices - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { if (app.code === 'wla' && (this.newWlaAllowed || app.approval_id)) { this.wlaChoices.push(app); } @@ -331,7 +331,7 @@ export default { parseAaa: function() { if (this.aaaApprovals.length>1) { // new app - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { if (['aaa','aap'].includes(app.code) && !app.approval_id) { //if (app.code === 'wla' && !app.approval_id) { this.aaaMultiple.push(app) @@ -346,7 +346,7 @@ export default { }) } else { // add wla approval to wlaChoices - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { //if (app.code === 'wla') { if (['aaa','aap'].includes(app.code)) { this.aaaChoices.push(app); @@ -357,7 +357,7 @@ export default { parseAua: function() { if (this.auaApprovals.length>1) { // new app - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { if (['aua','aup'].includes(app.code) && !app.approval_id) { //if (app.code === 'wla' && !app.approval_id) { this.auaMultiple.push(app) @@ -372,7 +372,7 @@ export default { }) } else { // add wla approval to wlaChoices - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { //if (app.code === 'wla') { if (['aua','aup'].includes(app.code)) { this.auaChoices.push(app); @@ -384,7 +384,7 @@ export default { if (this.mlApprovals.length>1) { /* // new app - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { if (['aua','aup'].includes(app.code) && !app.approval_id) { //if (app.code === 'wla' && !app.approval_id) { this.auaMultiple.push(app) @@ -400,7 +400,7 @@ export default { }) } else { // add wla approval to wlaChoices - for (let app of this.application_types) { + for (let app of this.application_types_and_licences) { //if (app.code === 'wla') { if (app.code==="ml") { this.mlChoices.push(app); @@ -417,9 +417,17 @@ export default { }, submit: function() { //let vm = this; + let title_verb = 'Create' + let text_verb = 'create' + if (this.selectedApplication.approval_id){ + title_verb = 'Amend or renew' + text_verb = 'amend or renew' + } swal({ - title: "Create " + this.selectedApplication.description, - text: "Are you sure you want to create " + this.alertText + "?", + // title: "Create " + this.selectedApplication.description, + // text: "Are you sure you want to create " + this.alertText + "?", + title: title_verb + " " + this.selectedApplication.description, + text: "Are you sure you want to " + text_verb + " " + this.alertText + "?", type: "question", showCancelButton: true, confirmButtonText: 'Accept' @@ -502,13 +510,13 @@ export default { fetchApplicationTypes: async function(){ const response = await this.$http.get(api_endpoints.application_types_dict+'?apply_page=True'); for (let app_type of response.body) { - this.application_types.push(app_type) + this.application_types_and_licences.push(app_type) } }, fetchExistingLicences: async function(){ const response = await this.$http.get(api_endpoints.existing_licences); for (let l of response.body) { - this.application_types.push(l) + this.application_types_and_licences.push(l) } }, fetchWlaAllowed: async function(){ @@ -519,12 +527,13 @@ export default { }, mounted: async function() { this.applicationsLoading = true; - //let vm = this; + await this.fetchApplicationTypes(); - //await this.fetchExistingMooringLicences(); - await this.fetchExistingLicences(); + await this.fetchExistingLicences(); // application_types_and_licences has all the application types and the existing licences + await this.fetchWlaAllowed(); - this.parseApprovals(); + + this.parseApprovals(); // wlaApprovals, aaaApprovals, auaApprovals and ml Approvals this.parseWla(); this.parseAaa(); this.parseAua(); diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/form_aaa.vue b/mooringlicensing/frontend/mooringlicensing/src/components/form_aaa.vue index b44f33a57..38cf1d575 100755 --- a/mooringlicensing/frontend/mooringlicensing/src/components/form_aaa.vue +++ b/mooringlicensing/frontend/mooringlicensing/src/components/form_aaa.vue @@ -37,7 +37,7 @@
- + /> -->
@@ -188,6 +188,13 @@ Profile, }, computed:{ + mooring_readonly: function(){ + let readonly = true + if (this.proposal.proposal_type.code == 'new'){ + readonly = false + } + return readonly + }, profileVar: function() { if (this.is_external) { return this.profile; diff --git a/mooringlicensing/frontend/mooringlicensing/src/components/forms/section_toggle.vue b/mooringlicensing/frontend/mooringlicensing/src/components/forms/section_toggle.vue index 6d0aeadf8..622aa948e 100644 --- a/mooringlicensing/frontend/mooringlicensing/src/components/forms/section_toggle.vue +++ b/mooringlicensing/frontend/mooringlicensing/src/components/forms/section_toggle.vue @@ -1,7 +1,7 @@ @@ -487,7 +495,7 @@ export default { var input = $(vm.$refs.uploadedFile)[0]; if (input.files && input.files[0]) { var reader = new FileReader(); - reader.readAsDataURL(input.files[0]); + reader.readAsDataURL(input.files[0]); reader.onload = function(e) { _file = e.target.result; }; @@ -563,7 +571,7 @@ export default { }); } }, - + uploadID: function() { let vm = this; console.log('uploading id'); @@ -606,7 +614,7 @@ export default { }); } }, - + updateContact: function() { let vm = this; vm.missing_fields = []; @@ -739,8 +747,8 @@ export default { fetchOrgRequestList: function() { //Fetch all the Organisation requests submitted by user which are pending for approval. let vm = this; vm.$http.get(helpers.add_endpoint_json(api_endpoints.organisation_requests,'get_pending_requests')).then((response) => { - - vm.orgRequest_list=response.body; + + vm.orgRequest_list=response.body; }, (error) => { console.log(error); }); @@ -775,7 +783,7 @@ export default { }else { swal( 'Validate Pins', - 'The pins you entered were incorrect', + 'The pins you entered were incorrect', 'error' ) } @@ -949,7 +957,7 @@ export default { ) }); },(error) => { - }); + }); }, fetchProfile: async function(){ let response = null; diff --git a/mooringlicensing/helpers.py b/mooringlicensing/helpers.py index c02fc25ec..ab1bbb6e1 100755 --- a/mooringlicensing/helpers.py +++ b/mooringlicensing/helpers.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser from django.conf import settings +from django.core.cache import cache import logging @@ -14,7 +15,22 @@ def belongs_to(user, group_name): :param group_name: :return: """ - return user.groups.filter(name=group_name).exists() + # return user.groups.filter(name=group_name).exists() + belongs_to_value = cache.get( + "User-belongs_to" + str(user.id) + "group_name:" + group_name + ) + if belongs_to_value: + print( + "From Cache - User-belongs_to" + str(user.id) + "group_name:" + group_name + ) + if belongs_to_value is None: + belongs_to_value = user.groups().filter(name=group_name).exists() + cache.set( + "User-belongs_to" + str(user.id) + "group_name:" + group_name, + belongs_to_value, + 3600, + ) + return belongs_to_value def is_model_backend(request): # Return True if user logged in via single sign-on (i.e. an internal) @@ -25,27 +41,31 @@ def is_email_auth_backend(request): return 'EmailAuth' in request.session.get('_auth_user_backend') def is_mooringlicensing_admin(request): - return request.user.is_authenticated() and is_model_backend(request) and in_dbca_domain(request) and (belongs_to(request.user, settings.ADMIN_GROUP)) + # return request.user.is_authenticated() and is_model_backend(request) and in_dbca_domain(request) and (belongs_to(request.user, settings.ADMIN_GROUP)) + return request.user.is_authenticated and is_model_backend(request) and in_dbca_domain(request) and (belongs_to(request.user, settings.ADMIN_GROUP)) def in_dbca_domain(request): - user = request.user - domain = user.email.split('@')[1] - if domain in settings.DEPT_DOMAINS: - if not user.is_staff: - # hack to reset department user to is_staff==True, if the user logged in externally (external departmentUser login defaults to is_staff=False) - user.is_staff = True - user.save() - return True - return False + return request.user.is_staff + # user = request.user + # domain = user.email.split('@')[1] + # if domain in settings.DEPT_DOMAINS: + # if not user.is_staff: + # # hack to reset department user to is_staff==True, if the user logged in externally (external departmentUser login defaults to is_staff=False) + # user.is_staff = True + # user.save() + # return True + # return False def is_in_organisation_contacts(request, organisation): return request.user.email in organisation.contacts.all().values_list('email', flat=True) def is_departmentUser(request): - return request.user.is_authenticated() and is_model_backend(request) and in_dbca_domain(request) + # return request.user.is_authenticated() and is_model_backend(request) and in_dbca_domain(request) + return request.user.is_authenticated and is_model_backend(request) and in_dbca_domain(request) def is_customer(request): - return request.user.is_authenticated() and (is_model_backend(request) or is_email_auth_backend(request)) + # return request.user.is_authenticated() and (is_model_backend(request) or is_email_auth_backend(request)) + return request.user.is_authenticated and (is_model_backend(request) or is_email_auth_backend(request)) def is_internal(request): return is_departmentUser(request) diff --git a/mooringlicensing/ledger_api_utils.py b/mooringlicensing/ledger_api_utils.py new file mode 100644 index 000000000..769bf0f84 --- /dev/null +++ b/mooringlicensing/ledger_api_utils.py @@ -0,0 +1,25 @@ +from ledger_api_client import utils +from ledger_api_client.ledger_models import EmailUserRO + +from mooringlicensing import settings +from mooringlicensing.components.main.decorators import basic_exception_handler + + +@basic_exception_handler +def retrieve_email_userro(email_user_id): + return EmailUserRO.objects.get(id=email_user_id) + + +def get_invoice_payment_status(invoice_id): + inv_props = utils.get_invoice_properties(invoice_id) + invoice_payment_status = inv_props['data']['invoice']['payment_status'] + return invoice_payment_status + + +def get_invoice_url(invoice_reference, request): + # return f'{settings.LEDGER_API_URL}/ledgergw/invoice-pdf/{settings.LEDGER_API_KEY}/{invoice_reference}' + # return f'/ledger-toolkit-api/invoice-pdf/{invoice_reference}/' + + # We shouldn't use the URL below for the front-end, because it includes the LEDGER_API_KEY, rather access it via ledger-toolkit-api/invoice-pdf + # f'{settings.LEDGER_API_URL}/ledgergw/invoice-pdf/{settings.LEDGER_API_KEY}/{invoice_reference}' + return request.build_absolute_uri(f'/ledger-toolkit-api/invoice-pdf/{invoice_reference}/') diff --git a/mooringlicensing/management/commands/expire_mooring_licence_application_due_to_no_documents.py b/mooringlicensing/management/commands/expire_mooring_licence_application_due_to_no_documents.py index ab91fb55f..900bce7f3 100755 --- a/mooringlicensing/management/commands/expire_mooring_licence_application_due_to_no_documents.py +++ b/mooringlicensing/management/commands/expire_mooring_licence_application_due_to_no_documents.py @@ -1,5 +1,5 @@ from datetime import timedelta -from ledger.accounts.models import EmailUser +# from ledger.accounts.models import EmailUser from django.utils import timezone from django.core.management.base import BaseCommand diff --git a/mooringlicensing/management/commands/export_and_email_sticker_data.py b/mooringlicensing/management/commands/export_and_email_sticker_data.py index 7be0fbb17..921003486 100644 --- a/mooringlicensing/management/commands/export_and_email_sticker_data.py +++ b/mooringlicensing/management/commands/export_and_email_sticker_data.py @@ -11,7 +11,10 @@ class Command(BaseCommand): help = 'Export and email sticker data' def handle(self, *args, **options): + # 1. Export sticker details as a spreadsheet file updates, errors = sticker_export() + + # 2. Email the file generated above to the sticker company success_filenames, error_filenames = email_stickers_document() cmd_name = __name__.split('.')[-1].replace('_', ' ').upper() diff --git a/mooringlicensing/management/commands/ml_migration_script.py b/mooringlicensing/management/commands/ml_migration_script.py index 5ee3d9482..6a5fb46dc 100644 --- a/mooringlicensing/management/commands/ml_migration_script.py +++ b/mooringlicensing/management/commands/ml_migration_script.py @@ -1,13 +1,5 @@ from django.core.management.base import BaseCommand -from django.utils import timezone from django.conf import settings -from django.core.mail import send_mail -from pathlib import Path -from mooringlicensing.utils.excel_export import mla_to_excel -from mooringlicensing.utils.excel_export import authusers_to_excel -from mooringlicensing.utils.excel_export import dcvpermits_to_excel -from mooringlicensing.utils.excel_export import wla_to_excel -from mooringlicensing.utils.excel_export import annualadmission_to_excel from mooringlicensing.utils.mooring_licence_migrate_pd import MooringLicenceReader import time @@ -26,19 +18,19 @@ def handle(self, *args, **options): path = options['path'] t_start = time.time() -# mlr=MooringLicenceReader('PersonDets.txt', 'MooringDets.txt', 'VesselDets.txt', 'UserDets.txt', 'ApplicationDets.txt','annual_admissions_booking_report.csv', path=path) -# -# mlr.create_users() -# mlr.create_vessels() -# mlr.create_mooring_licences() -# mlr.create_authuser_permits() -# mlr.create_waiting_list() -# mlr.create_dcv() -# mlr.create_annual_admissions() - -# MooringLicenceReader.create_pdf_ml() -# MooringLicenceReader.create_pdf_aup() -# MooringLicenceReader.create_pdf_wl() + mlr=MooringLicenceReader('PersonDets.txt', 'MooringDets.txt', 'VesselDets.txt', 'UserDets.txt', 'ApplicationDets.txt','annual_admissions_booking_report.csv', path='shared/clean/clean_22Dec2022/') + + mlr.create_users() + mlr.create_vessels() + mlr.create_mooring_licences() + mlr.create_authuser_permits() + mlr.create_waiting_list() + mlr.create_dcv() + mlr.create_annual_admissions() + + MooringLicenceReader.create_pdf_ml() + MooringLicenceReader.create_pdf_aup() + MooringLicenceReader.create_pdf_wl() MooringLicenceReader.create_pdf_dcv() MooringLicenceReader.create_pdf_aa() diff --git a/mooringlicensing/management/commands/update_approval_status.py b/mooringlicensing/management/commands/update_approval_status.py index abc0ed0d4..8816623b6 100755 --- a/mooringlicensing/management/commands/update_approval_status.py +++ b/mooringlicensing/management/commands/update_approval_status.py @@ -5,7 +5,7 @@ from mooringlicensing import settings from mooringlicensing.components.approvals.models import ( Approval, - ApprovalUserAction, + ApprovalUserAction, WaitingListAllocation, ) from mooringlicensing.components.proposals.models import ProposalUserAction from ledger.accounts.models import EmailUser @@ -83,6 +83,10 @@ def handle(self, *args, **options): a.status = Approval.APPROVAL_STATUS_CANCELLED a.set_to_cancel = False a.save() + + if hasattr(a, 'child_obj') and type(self.child_obj) == WaitingListAllocation: + self.child_obj.processes_after_cancel() + send_approval_cancel_email_notification(a) proposal = a.current_proposal @@ -138,11 +142,15 @@ def handle(self, *args, **options): if a.cancellation_date and a.set_to_cancel: if a.cancellation_date <= today: try: - a.status = Approval.STATUS_CANCELLED + a.status = Approval.APPROVAL_STATUS_CANCELLED a.set_to_cancel = False a.save() + if hasattr(a, 'child_obj') and type(self.child_obj) == WaitingListAllocation: + self.child_obj.processes_after_cancel() + send_approval_cancel_email_notification(a) + proposal = a.current_proposal ApprovalUserAction.log_action(a, ApprovalUserAction.ACTION_CANCEL_APPROVAL.format(a.id), user) ProposalUserAction.log_action(proposal, ProposalUserAction.ACTION_CANCEL_APPROVAL.format(proposal.lodgement_number), user) diff --git a/mooringlicensing/management/default_data_manager.py b/mooringlicensing/management/default_data_manager.py index fe85cf2ee..bb45a6941 100644 --- a/mooringlicensing/management/default_data_manager.py +++ b/mooringlicensing/management/default_data_manager.py @@ -20,6 +20,7 @@ Proposal, StickerPrintingContact ) +import ledger_api_client logger = logging.getLogger(__name__) @@ -71,7 +72,8 @@ def __init__(self): obj._file.save(os.path.basename(GlobalSettings.default_values[item[0]]), File(doc_file), save=True) obj.save() else: - obj.value = item[1] + # obj.value = item[1] + obj.value = GlobalSettings.default_values[item[0]] obj.save() logger.info("Created {}: {}".format(item[0], item[1])) except Exception as e: @@ -105,21 +107,33 @@ def __init__(self): except Exception as e: logger.error('{}, AdmissionType: {}'.format(e, item[1])) - # Groups + # # Groups + # for group_name in settings.CUSTOM_GROUPS: + # try: + # group, created = Group.objects.get_or_create(name=group_name) + # if created: + # logger.info("Created group: {}".format(group_name)) + # except Exception as e: + # logger.error('{}, Group name: {}'.format(e, group_name)) + + # SystemGroup # For the segregated system, use the SystemGroup. for group_name in settings.CUSTOM_GROUPS: try: - group, created = Group.objects.get_or_create(name=group_name) + group, created = ledger_api_client.managed_models.SystemGroup.objects.get_or_create(name=group_name) if created: - logger.info("Created group: {}".format(group_name)) + logger.info("Created SystemGroup: {}".format(group_name)) except Exception as e: - logger.error('{}, Group name: {}'.format(e, group_name)) + logger.error('{}, SystemGroup name: {}'.format(e, group_name)) # Types of configurable number of days for item in settings.TYPES_OF_CONFIGURABLE_NUMBER_OF_DAYS: try: types_to_be_deleted = NumberOfDaysType.objects.filter(code__isnull=True) types_to_be_deleted.delete() # Delete left overs + except Exception as e: + logger.error('{}'.format(e)) + try: myType, created = NumberOfDaysType.objects.get_or_create(code=item['code']) if created: # Save description @@ -139,7 +153,8 @@ def __init__(self): ) except Exception as e: - logger.error('{}, Number of days type: {}'.format(e, myType.name)) + # logger.error('{}, Number of days type: {}'.format(e, myType.name)) + logger.error('{}'.format(e)) # Oracle account codes today = datetime.datetime.now(pytz.timezone(settings.TIME_ZONE)).date() diff --git a/mooringlicensing/middleware.py b/mooringlicensing/middleware.py index 42f9fa5a3..a851b4d76 100755 --- a/mooringlicensing/middleware.py +++ b/mooringlicensing/middleware.py @@ -1,4 +1,5 @@ -from django.core.urlresolvers import reverse +# from django.core.urlresolvers import reverse +from django.urls import reverse from django.shortcuts import redirect from django.utils.http import urlquote_plus @@ -11,25 +12,46 @@ from reversion.middleware import RevisionMiddleware from reversion.views import _request_creates_revision +import logging + +logger = logging.getLogger(__name__) + CHECKOUT_PATH = re.compile('^/ledger/checkout/checkout') class FirstTimeNagScreenMiddleware(object): - def process_request(self, request): - if request.user.is_authenticated() and request.method == 'GET' and 'api' not in request.path and 'admin' not in request.path and 'static' not in request.path: - if (not request.user.first_name or not request.user.last_name): - path_ft = reverse('first_time') - path_logout = reverse('accounts:logout') - if request.path not in (path_ft, path_logout): - return redirect(reverse('first_time')+"?next="+urlquote_plus(request.get_full_path())) + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if request.user.is_authenticated and request.method == 'GET' and 'api' not in request.path and 'admin' not in request.path and 'static' not in request.path: + # if not request.user.first_name or not request.user.last_name: + if not request.user.first_name or not request.user.last_name or not request.user.residential_address_id or not request.user.postal_address_id: + path_first_time = reverse('account') + path_logout = reverse('logout') + if request.path not in (path_first_time, path_logout): + logger.info('redirect') + return redirect(path_first_time + "?next=" + urlquote_plus(request.get_full_path())) + else: + # We don't want to redirect the suer when the user is accessing the firsttime page or logout page. + pass + response = self.get_response(request) + return response + class CacheControlMiddleware(object): - def process_response(self, request, response): + def __init__(self, get_response): + self.get_response = get_response + + # def process_response(self, request, response): + def __call__(self, request): + response = self.get_response(request) if request.path[:5] == '/api/' or request.path == '/': response['Cache-Control'] = 'private, no-store' elif request.path[:8] == '/static/': response['Cache-Control'] = 'public, max-age=86400' return response + class RevisionOverrideMiddleware(RevisionMiddleware): """ diff --git a/mooringlicensing/migrations.original/0001_initial.py b/mooringlicensing/migrations.original/0001_initial.py new file mode 100644 index 000000000..c1f341260 --- /dev/null +++ b/mooringlicensing/migrations.original/0001_initial.py @@ -0,0 +1,918 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2021-03-02 08:32 +from __future__ import unicode_literals + +import ckeditor.fields +import dirtyfields.dirtyfields +from django.conf import settings +import django.contrib.postgres.fields.jsonb +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import mooringlicensing.components.approvals.models +import mooringlicensing.components.compliances.models +import mooringlicensing.components.organisations.models +import mooringlicensing.components.proposals.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('sites', '0002_alter_domain_unique'), + ('accounts', '0024_organisation_email'), + ] + + operations = [ + migrations.CreateModel( + name='AmendmentReason', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.CharField(max_length=125, verbose_name='Reason')), + ], + options={ + 'verbose_name': 'Application Amendment Reason', + 'verbose_name_plural': 'Application Amendment Reasons', + }, + ), + migrations.CreateModel( + name='ApplicationType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(choices=[('Waiting List Application', 'Waiting List Application'), ('Annual Admission Application', 'Annual Admission Application'), ('Authorised User Application', 'Authorised User Application'), ('Mooring License Application', 'Mooring License Application')], max_length=64, verbose_name='Application Type name')), + ('order', models.PositiveSmallIntegerField(default=0)), + ('visible', models.BooleanField(default=True)), + ('application_fee', models.DecimalField(decimal_places=2, max_digits=6)), + ('oracle_code_application', models.CharField(max_length=50)), + ('is_gst_exempt', models.BooleanField(default=True)), + ], + options={ + 'ordering': ['order', 'name'], + }, + ), + migrations.CreateModel( + name='Approval', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('lodgement_number', models.CharField(blank=True, default='', max_length=9)), + ('status', models.CharField(choices=[('current', 'Current'), ('expired', 'Expired'), ('cancelled', 'Cancelled'), ('surrendered', 'Surrendered'), ('suspended', 'Suspended'), ('extended', 'extended'), ('awaiting_payment', 'Awaiting Payment')], default='current', max_length=40)), + ('renewal_sent', models.BooleanField(default=False)), + ('issue_date', models.DateTimeField()), + ('original_issue_date', models.DateField(auto_now_add=True)), + ('start_date', models.DateField()), + ('expiry_date', models.DateField()), + ('surrender_details', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('suspension_details', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('extracted_fields', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('cancellation_details', models.TextField(blank=True)), + ('extend_details', models.TextField(blank=True)), + ('cancellation_date', models.DateField(blank=True, null=True)), + ('set_to_cancel', models.BooleanField(default=False)), + ('set_to_suspend', models.BooleanField(default=False)), + ('set_to_surrender', models.BooleanField(default=False)), + ('renewal_count', models.PositiveSmallIntegerField(default=0, verbose_name='Number of times an Approval has been renewed')), + ('migrated', models.BooleanField(default=False)), + ('extended', models.BooleanField(default=False)), + ('expiry_notice_sent', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='ApprovalDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_approval_doc_filename)), + ('can_delete', models.BooleanField(default=True)), + ], + ), + migrations.CreateModel( + name='ApprovalLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, null=True, upload_to=mooringlicensing.components.approvals.models.update_approval_comms_log_filename)), + ], + ), + migrations.CreateModel( + name='ApprovalUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ], + options={ + 'ordering': ('-when',), + }, + ), + migrations.CreateModel( + name='ChecklistQuestion', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.TextField()), + ('list_type', models.CharField(choices=[('assessor_list', 'Assessor Checklist'), ('referral_list', 'Referral Checklist')], default='assessor_list', max_length=30, verbose_name='Checklist type')), + ('answer_type', models.CharField(choices=[('yes_no', 'Yes/No type'), ('free_text', 'Free text type')], default='yes_no', max_length=30, verbose_name='Answer type')), + ('obsolete', models.BooleanField(default=False)), + ('order', models.PositiveSmallIntegerField(default=1)), + ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ], + ), + migrations.CreateModel( + name='CommunicationsLogEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('to', models.TextField(blank=True, verbose_name='To')), + ('fromm', models.CharField(blank=True, max_length=200, verbose_name='From')), + ('cc', models.TextField(blank=True, verbose_name='cc')), + ('type', models.CharField(choices=[('email', 'Email'), ('phone', 'Phone Call'), ('mail', 'Mail'), ('person', 'In Person'), ('onhold', 'On Hold'), ('onhold_remove', 'Remove On Hold'), ('with_qaofficer', 'With QA Officer'), ('with_qaofficer_completed', 'QA Officer Completed'), ('referral_complete', 'Referral Completed')], default='email', max_length=35)), + ('reference', models.CharField(blank=True, max_length=100)), + ('subject', models.CharField(blank=True, max_length=200, verbose_name='Subject / Description')), + ('text', models.TextField(blank=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ], + ), + migrations.CreateModel( + name='Compliance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('lodgement_number', models.CharField(blank=True, default='', max_length=9)), + ('due_date', models.DateField()), + ('text', models.TextField(blank=True)), + ('num_participants', models.SmallIntegerField(blank=True, null=True, verbose_name='Number of participants')), + ('processing_status', models.CharField(choices=[('due', 'Due'), ('future', 'Future'), ('with_assessor', 'With Assessor'), ('approved', 'Approved'), ('discarded', 'Discarded')], max_length=20)), + ('customer_status', models.CharField(choices=[('due', 'Due'), ('future', 'Future'), ('with_assessor', 'Under Review'), ('approved', 'Approved'), ('discarded', 'Discarded')], default='future', max_length=20)), + ('lodgement_date', models.DateTimeField(blank=True, null=True)), + ('reminder_sent', models.BooleanField(default=False)), + ('post_reminder_sent', models.BooleanField(default=False)), + ('fee_invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), + ], + ), + migrations.CreateModel( + name='ComplianceAmendmentReason', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.CharField(max_length=125, verbose_name='Reason')), + ], + ), + migrations.CreateModel( + name='ComplianceDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_proposal_complaince_filename)), + ('can_delete', models.BooleanField(default=True)), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Compliance')), + ], + ), + migrations.CreateModel( + name='ComplianceLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_compliance_comms_log_filename)), + ], + ), + migrations.CreateModel( + name='ComplianceUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Compliance')), + ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='CompRequest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(blank=True, max_length=200)), + ('text', models.TextField(blank=True)), + ], + ), + migrations.CreateModel( + name='GlobalSettings', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(choices=[('', '')], max_length=255)), + ('value', models.CharField(max_length=255)), + ], + options={ + 'verbose_name_plural': 'Global Settings', + }, + ), + migrations.CreateModel( + name='HelpPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', ckeditor.fields.RichTextField()), + ('description', models.CharField(blank=True, max_length=256, null=True)), + ('help_type', models.SmallIntegerField(choices=[(1, 'External'), (2, 'Internal')], default=1, verbose_name='Help Type')), + ('version', models.SmallIntegerField(default=1)), + ('application_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ], + ), + migrations.CreateModel( + name='Organisation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('admin_pin_one', models.CharField(blank=True, max_length=50)), + ('admin_pin_two', models.CharField(blank=True, max_length=50)), + ('user_pin_one', models.CharField(blank=True, max_length=50)), + ('user_pin_two', models.CharField(blank=True, max_length=50)), + ('bpay_allowed', models.BooleanField(default=False, verbose_name='BPAY Allowed')), + ('monthly_invoicing_allowed', models.BooleanField(default=False)), + ('monthly_invoicing_period', models.SmallIntegerField(default=0, verbose_name='Monthly Invoicing Period (in #days from beginning of the following month)')), + ('monthly_payment_due_period', models.SmallIntegerField(default=20, verbose_name='Monthly Payment Due Period (in #days from Invoicing Date)')), + ('apply_application_discount', models.BooleanField(default=False)), + ('application_discount', models.FloatField(default=0.0, validators=[django.core.validators.MinValueValidator(0.0)])), + ('apply_licence_discount', models.BooleanField(default=False)), + ('licence_discount', models.FloatField(default=0.0, validators=[django.core.validators.MinValueValidator(0.0)])), + ('event_training_completed', models.BooleanField(default=False)), + ('event_training_date', models.DateField(blank=True, null=True)), + ], + ), + migrations.CreateModel( + name='OrganisationAccessGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ('site', models.OneToOneField(default='1', on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ], + options={ + 'verbose_name_plural': 'Organisation access group', + }, + ), + migrations.CreateModel( + name='OrganisationAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Organisation')), + ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='OrganisationContact', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_status', models.CharField(choices=[('draft', 'Draft'), ('pending', 'Pending'), ('active', 'Active'), ('declined', 'Declined'), ('unlinked', 'Unlinked'), ('suspended', 'Suspended')], default='draft', max_length=40, verbose_name='Status')), + ('user_role', models.CharField(choices=[('organisation_admin', 'Organisation Admin'), ('organisation_user', 'Organisation User'), ('consultant', 'Consultant')], default='organisation_user', max_length=40, verbose_name='Role')), + ('is_admin', models.BooleanField(default=False)), + ('email', models.EmailField(max_length=254)), + ('first_name', models.CharField(max_length=128, verbose_name='Given name(s)')), + ('last_name', models.CharField(max_length=128)), + ('phone_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='phone number')), + ('mobile_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='mobile number')), + ('fax_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='fax number')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='mooringlicensing.Organisation')), + ], + ), + migrations.CreateModel( + name='OrganisationLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_comms_log_filename)), + ], + ), + migrations.CreateModel( + name='OrganisationRequest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128)), + ('abn', models.CharField(blank=True, max_length=50, null=True, verbose_name='ABN')), + ('identification', models.FileField(blank=True, max_length=512, null=True, upload_to='organisation/requests/%Y/%m/%d')), + ('status', models.CharField(choices=[('with_assessor', 'With Assessor'), ('approved', 'Approved'), ('declined', 'Declined')], default='with_assessor', max_length=100)), + ('lodgement_date', models.DateTimeField(auto_now_add=True)), + ('role', models.CharField(choices=[('employee', 'Employee'), ('consultant', 'Consultant')], default='employee', max_length=100)), + ('assigned_officer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='org_request_assignee', to=settings.AUTH_USER_MODEL)), + ('requester', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='OrganisationRequestDeclinedDetails', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.TextField(blank=True)), + ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.OrganisationRequest')), + ], + ), + migrations.CreateModel( + name='OrganisationRequestLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_request_comms_log_filename)), + ], + ), + migrations.CreateModel( + name='OrganisationRequestUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.OrganisationRequest')), + ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Proposal', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('proposal_type', models.CharField(choices=[('new_proposal', 'New Application'), ('amendment', 'Amendment'), ('renewal', 'Renewal'), ('external', 'External')], default='new_proposal', max_length=40, verbose_name='Application Status Type')), + ('data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('assessor_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('comment_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('schema', django.contrib.postgres.fields.jsonb.JSONField()), + ('proposed_issuance_approval', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('customer_status', models.CharField(choices=[('temp', 'Temporary'), ('draft', 'Draft'), ('with_assessor', 'Under Review'), ('amendment_required', 'Amendment Required'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('partially_approved', 'Partially Approved'), ('partially_declined', 'Partially Declined'), ('awaiting_payment', 'Awaiting Payment')], default='draft', max_length=40, verbose_name='Customer Status')), + ('lodgement_number', models.CharField(blank=True, default='', max_length=9)), + ('lodgement_sequence', models.IntegerField(blank=True, default=0)), + ('lodgement_date', models.DateTimeField(blank=True, null=True)), + ('processing_status', models.CharField(choices=[('temp', 'Temporary'), ('draft', 'Draft'), ('with_assessor', 'With Assessor'), ('with_district_assessor', 'With District Assessor'), ('on_hold', 'On Hold'), ('with_qa_officer', 'With QA Officer'), ('with_referral', 'With Referral'), ('with_assessor_requirements', 'With Assessor (Requirements)'), ('with_approver', 'With Approver'), ('renewal', 'Renewal'), ('licence_amendment', 'Licence Amendment'), ('awaiting_applicant_respone', 'Awaiting Applicant Response'), ('awaiting_assessor_response', 'Awaiting Assessor Response'), ('awaiting_responses', 'Awaiting Responses'), ('ready_for_conditions', 'Ready for Conditions'), ('ready_to_issue', 'Ready to Issue'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('partially_approved', 'Partially Approved'), ('partially_declined', 'Partially Declined'), ('awaiting_payment', 'Awaiting Payment')], default='draft', max_length=30, verbose_name='Processing Status')), + ('prev_processing_status', models.CharField(blank=True, max_length=30, null=True)), + ('proposed_decline_status', models.BooleanField(default=False)), + ('title', models.CharField(blank=True, max_length=255, null=True)), + ('approval_level', models.CharField(blank=True, max_length=255, null=True, verbose_name='Activity matrix approval level')), + ('approval_comment', models.TextField(blank=True)), + ('migrated', models.BooleanField(default=False)), + ('application_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ], + options={ + 'verbose_name': 'Application', + 'verbose_name_plural': 'Applications', + }, + bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model), + ), + migrations.CreateModel( + name='ProposalApproverGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Application Approver Group', + 'verbose_name_plural': 'Application Approver Group', + }, + ), + migrations.CreateModel( + name='ProposalAssessment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('completed', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assessment', to='mooringlicensing.Proposal')), + ('submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='proposal_assessment', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ProposalAssessmentAnswer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('answer', models.NullBooleanField()), + ('text_answer', models.CharField(blank=True, max_length=256, null=True)), + ('assessment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='mooringlicensing.ProposalAssessment')), + ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='mooringlicensing.ChecklistQuestion')), + ], + options={ + 'verbose_name': 'Assessment answer', + 'verbose_name_plural': 'Assessment answers', + }, + ), + migrations.CreateModel( + name='ProposalAssessorGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Application Assessor Group', + 'verbose_name_plural': 'Application Assessor Group', + }, + ), + migrations.CreateModel( + name='ProposalDeclinedDetails', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.TextField(blank=True)), + ('cc_email', models.TextField(null=True)), + ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal')), + ], + ), + migrations.CreateModel( + name='ProposalDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_doc_filename)), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Proposal')), + ], + options={ + 'verbose_name': 'Application Document', + }, + ), + migrations.CreateModel( + name='ProposalLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_comms_log_filename)), + ], + ), + migrations.CreateModel( + name='ProposalOnHold', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('comment', models.TextField(blank=True)), + ('documents', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='onhold_documents', to='mooringlicensing.ProposalDocument')), + ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal')), + ], + ), + migrations.CreateModel( + name='ProposalRequest', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('subject', models.CharField(blank=True, max_length=200)), + ('text', models.TextField(blank=True)), + ], + ), + migrations.CreateModel( + name='ProposalRequirement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('order', models.PositiveIntegerField(db_index=True, editable=False)), + ('free_requirement', models.TextField(blank=True, null=True)), + ('standard', models.BooleanField(default=True)), + ('due_date', models.DateField(blank=True, null=True)), + ('recurrence', models.BooleanField(default=False)), + ('recurrence_pattern', models.SmallIntegerField(choices=[(1, 'Weekly'), (2, 'Monthly'), (3, 'Yearly')], default=1)), + ('recurrence_schedule', models.IntegerField(blank=True, null=True)), + ('is_deleted', models.BooleanField(default=False)), + ('copied_for_renewal', models.BooleanField(default=False)), + ('require_due_date', models.BooleanField(default=False)), + ('copied_from', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.ProposalRequirement')), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirements', to='mooringlicensing.Proposal')), + ], + ), + migrations.CreateModel( + name='ProposalStandardRequirement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.TextField()), + ('code', models.CharField(max_length=10, unique=True)), + ('obsolete', models.BooleanField(default=False)), + ('participant_number_required', models.BooleanField(default=False)), + ('default', models.BooleanField(default=False)), + ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ], + options={ + 'verbose_name': 'Application Standard Requirement', + 'verbose_name_plural': 'Application Standard Requirements', + }, + ), + migrations.CreateModel( + name='ProposalUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Proposal')), + ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ('-when',), + }, + ), + migrations.CreateModel( + name='Question', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('question_text', models.TextField()), + ('answer_one', models.CharField(blank=True, max_length=200)), + ('answer_two', models.CharField(blank=True, max_length=200)), + ('answer_three', models.CharField(blank=True, max_length=200)), + ('answer_four', models.CharField(blank=True, max_length=200)), + ('correct_answer', models.CharField(choices=[('answer_one', 'Answer one'), ('answer_two', 'Answer two'), ('answer_three', 'Answer three'), ('answer_four', 'Answer four')], default='answer_one', max_length=40, verbose_name='Correct Answer')), + ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ], + ), + migrations.CreateModel( + name='RequirementDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_requirement_doc_filename)), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('visible', models.BooleanField(default=True)), + ('requirement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirement_documents', to='mooringlicensing.ProposalRequirement')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SystemMaintenance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField()), + ('start_date', models.DateTimeField()), + ('end_date', models.DateTimeField()), + ], + options={ + 'verbose_name_plural': 'System maintenance', + }, + ), + migrations.CreateModel( + name='TemporaryDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=255, upload_to='')), + ], + ), + migrations.CreateModel( + name='TemporaryDocumentCollection', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + ), + migrations.CreateModel( + name='UserDelegation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Organisation')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='UserSystemSettings', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='system_settings', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'User System Settings', + }, + ), + migrations.CreateModel( + name='Vessel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('nominated_vessel', models.CharField(blank=True, max_length=200)), + ('spv_no', models.CharField(blank=True, max_length=200)), + ('hire_rego', models.CharField(blank=True, max_length=200)), + ('craft_no', models.CharField(blank=True, max_length=200)), + ('size', models.CharField(blank=True, max_length=200)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vessels', to='mooringlicensing.Proposal')), + ], + ), + migrations.CreateModel( + name='AmendmentRequest', + fields=[ + ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), + ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), + ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.AmendmentReason')), + ], + bases=('mooringlicensing.proposalrequest',), + ), + migrations.CreateModel( + name='ApprovalLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='Assessment', + fields=[ + ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), + ('status', models.CharField(choices=[('awaiting_assessment', 'Awaiting Assessment'), ('assessed', 'Assessed'), ('assessment_expired', 'Assessment Period Expired')], default='awaiting_assessment', max_length=20, verbose_name='Status')), + ('date_last_reminded', models.DateField(blank=True, null=True)), + ('comment', models.TextField(blank=True)), + ('purpose', models.TextField(blank=True)), + ('assigned_assessor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + bases=('mooringlicensing.proposalrequest',), + ), + migrations.CreateModel( + name='ComplianceAmendmentRequest', + fields=[ + ('comprequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CompRequest')), + ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), + ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ComplianceAmendmentReason')), + ], + bases=('mooringlicensing.comprequest',), + ), + migrations.CreateModel( + name='ComplianceLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='ComplianceRequest', + fields=[ + ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), + ('reason', models.CharField(choices=[('outstanding', 'There are currently outstanding returns for the previous licence'), ('other', 'Other')], default='outstanding', max_length=30, verbose_name='Reason')), + ], + bases=('mooringlicensing.proposalrequest',), + ), + migrations.CreateModel( + name='OrganisationLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='OrganisationRequestLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.OrganisationRequest')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='PreviewTempApproval', + fields=[ + ('approval_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.Approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='ProposalLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.AddField( + model_name='temporarydocument', + name='temp_document_collection', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.TemporaryDocumentCollection'), + ), + migrations.AddField( + model_name='proposalrequirement', + name='standard_requirement', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ProposalStandardRequirement'), + ), + migrations.AddField( + model_name='proposalrequest', + name='officer', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='proposalrequest', + name='proposal', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proposalrequest_set', to='mooringlicensing.Proposal'), + ), + migrations.AddField( + model_name='proposal', + name='approval', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='proposal', + name='approval_level_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approval_level_document', to='mooringlicensing.ProposalDocument'), + ), + migrations.AddField( + model_name='proposal', + name='assigned_approver', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mooringlicensing_proposals_approvals', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='proposal', + name='assigned_officer', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mooringlicensing_proposals_assigned', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='proposal', + name='org_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='org_applications', to='mooringlicensing.Organisation'), + ), + migrations.AddField( + model_name='proposal', + name='previous_application', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal'), + ), + migrations.AddField( + model_name='proposal', + name='proxy_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_proxy', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='proposal', + name='submitter', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_proposals', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='organisation', + name='delegates', + field=models.ManyToManyField(blank=True, related_name='mooringlicensing_organisations', through='mooringlicensing.UserDelegation', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='organisation', + name='organisation', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.Organisation'), + ), + migrations.AddField( + model_name='comprequest', + name='compliance', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Compliance'), + ), + migrations.AddField( + model_name='comprequest', + name='officer', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='compliance', + name='approval', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='compliances', to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='compliance', + name='assigned_to', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_compliance_assignments', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='compliance', + name='proposal', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='compliances', to='mooringlicensing.Proposal'), + ), + migrations.AddField( + model_name='compliance', + name='requirement', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='compliance_requirement', to='mooringlicensing.ProposalRequirement'), + ), + migrations.AddField( + model_name='compliance', + name='submitter', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_compliances', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='communicationslogentry', + name='customer', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='communicationslogentry', + name='staff', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='approvaluseraction', + name='approval', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='approvaluseraction', + name='who', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='approvaldocument', + name='approval', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='approval', + name='cover_letter_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cover_letter_document', to='mooringlicensing.ApprovalDocument'), + ), + migrations.AddField( + model_name='approval', + name='current_proposal', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approvals', to='mooringlicensing.Proposal'), + ), + migrations.AddField( + model_name='approval', + name='licence_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='licence_document', to='mooringlicensing.ApprovalDocument'), + ), + migrations.AddField( + model_name='approval', + name='org_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='org_approvals', to='mooringlicensing.Organisation'), + ), + migrations.AddField( + model_name='approval', + name='proxy_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='proxy_approvals', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='approval', + name='renewal_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='renewal_document', to='mooringlicensing.ApprovalDocument'), + ), + migrations.AddField( + model_name='approval', + name='replaced_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='approval', + name='submitter', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='mooringlicensing_approvals', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterUniqueTogether( + name='userdelegation', + unique_together=set([('organisation', 'user')]), + ), + migrations.AddField( + model_name='proposallogentry', + name='proposal', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Proposal'), + ), + migrations.AddField( + model_name='proposallogdocument', + name='log_entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ProposalLogEntry'), + ), + migrations.AlterUniqueTogether( + name='proposalassessment', + unique_together=set([('proposal',)]), + ), + migrations.AddField( + model_name='organisationrequestlogdocument', + name='log_entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.OrganisationRequestLogEntry'), + ), + migrations.AddField( + model_name='organisationlogentry', + name='organisation', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Organisation'), + ), + migrations.AddField( + model_name='organisationlogdocument', + name='log_entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.OrganisationLogEntry'), + ), + migrations.AlterUniqueTogether( + name='organisationcontact', + unique_together=set([('organisation', 'email')]), + ), + migrations.AlterUniqueTogether( + name='helppage', + unique_together=set([('application_type', 'help_type', 'version')]), + ), + migrations.AddField( + model_name='compliancelogentry', + name='compliance', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Compliance'), + ), + migrations.AddField( + model_name='compliancelogdocument', + name='log_entry', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ComplianceLogEntry'), + ), + migrations.AddField( + model_name='approvallogentry', + name='approval', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Approval'), + ), + migrations.AddField( + model_name='approvallogdocument', + name='log_entry', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ApprovalLogEntry'), + ), + migrations.AlterUniqueTogether( + name='approval', + unique_together=set([('lodgement_number', 'issue_date')]), + ), + ] diff --git a/mooringlicensing/migrations/0002_auto_20210302_1631.py b/mooringlicensing/migrations.original/0002_auto_20210302_1631.py similarity index 100% rename from mooringlicensing/migrations/0002_auto_20210302_1631.py rename to mooringlicensing/migrations.original/0002_auto_20210302_1631.py diff --git a/mooringlicensing/migrations/0002_waitinglistapplication.py b/mooringlicensing/migrations.original/0002_waitinglistapplication.py similarity index 100% rename from mooringlicensing/migrations/0002_waitinglistapplication.py rename to mooringlicensing/migrations.original/0002_waitinglistapplication.py diff --git a/mooringlicensing/migrations/0003_auto_20210309_1156.py b/mooringlicensing/migrations.original/0003_auto_20210309_1156.py similarity index 100% rename from mooringlicensing/migrations/0003_auto_20210309_1156.py rename to mooringlicensing/migrations.original/0003_auto_20210309_1156.py diff --git a/mooringlicensing/migrations/0004_auto_20210309_1157.py b/mooringlicensing/migrations.original/0004_auto_20210309_1157.py similarity index 100% rename from mooringlicensing/migrations/0004_auto_20210309_1157.py rename to mooringlicensing/migrations.original/0004_auto_20210309_1157.py diff --git a/mooringlicensing/migrations/0005_auto_20210309_1241.py b/mooringlicensing/migrations.original/0005_auto_20210309_1241.py similarity index 100% rename from mooringlicensing/migrations/0005_auto_20210309_1241.py rename to mooringlicensing/migrations.original/0005_auto_20210309_1241.py diff --git a/mooringlicensing/migrations/0005_merge_20210309_1424.py b/mooringlicensing/migrations.original/0005_merge_20210309_1424.py similarity index 100% rename from mooringlicensing/migrations/0005_merge_20210309_1424.py rename to mooringlicensing/migrations.original/0005_merge_20210309_1424.py diff --git a/mooringlicensing/migrations/0006_merge_20210309_1431.py b/mooringlicensing/migrations.original/0006_merge_20210309_1431.py similarity index 100% rename from mooringlicensing/migrations/0006_merge_20210309_1431.py rename to mooringlicensing/migrations.original/0006_merge_20210309_1431.py diff --git a/mooringlicensing/migrations/0007_auto_20210309_1431.py b/mooringlicensing/migrations.original/0007_auto_20210309_1431.py similarity index 100% rename from mooringlicensing/migrations/0007_auto_20210309_1431.py rename to mooringlicensing/migrations.original/0007_auto_20210309_1431.py diff --git a/mooringlicensing/migrations/0008_annualadmissionapplication_authoriseduserapplication_mooringlicenseapplication.py b/mooringlicensing/migrations.original/0008_annualadmissionapplication_authoriseduserapplication_mooringlicenseapplication.py similarity index 100% rename from mooringlicensing/migrations/0008_annualadmissionapplication_authoriseduserapplication_mooringlicenseapplication.py rename to mooringlicensing/migrations.original/0008_annualadmissionapplication_authoriseduserapplication_mooringlicenseapplication.py diff --git a/mooringlicensing/migrations/0009_auto_20210312_1346.py b/mooringlicensing/migrations.original/0009_auto_20210312_1346.py similarity index 100% rename from mooringlicensing/migrations/0009_auto_20210312_1346.py rename to mooringlicensing/migrations.original/0009_auto_20210312_1346.py diff --git a/mooringlicensing/migrations/0010_remove_helppage_application_type.py b/mooringlicensing/migrations.original/0010_remove_helppage_application_type.py similarity index 100% rename from mooringlicensing/migrations/0010_remove_helppage_application_type.py rename to mooringlicensing/migrations.original/0010_remove_helppage_application_type.py diff --git a/mooringlicensing/migrations/0011_auto_20210312_1441.py b/mooringlicensing/migrations.original/0011_auto_20210312_1441.py similarity index 100% rename from mooringlicensing/migrations/0011_auto_20210312_1441.py rename to mooringlicensing/migrations.original/0011_auto_20210312_1441.py diff --git a/mooringlicensing/migrations/0012_auto_20210312_1442.py b/mooringlicensing/migrations.original/0012_auto_20210312_1442.py similarity index 100% rename from mooringlicensing/migrations/0012_auto_20210312_1442.py rename to mooringlicensing/migrations.original/0012_auto_20210312_1442.py diff --git a/mooringlicensing/migrations/0013_waitinglistallocation.py b/mooringlicensing/migrations.original/0013_waitinglistallocation.py similarity index 100% rename from mooringlicensing/migrations/0013_waitinglistallocation.py rename to mooringlicensing/migrations.original/0013_waitinglistallocation.py diff --git a/mooringlicensing/migrations/0014_annualadmissionpermit_authoriseduserpermit_mooringlicence.py b/mooringlicensing/migrations.original/0014_annualadmissionpermit_authoriseduserpermit_mooringlicence.py similarity index 100% rename from mooringlicensing/migrations/0014_annualadmissionpermit_authoriseduserpermit_mooringlicence.py rename to mooringlicensing/migrations.original/0014_annualadmissionpermit_authoriseduserpermit_mooringlicence.py diff --git a/mooringlicensing/migrations/0015_auto_20210315_1642.py b/mooringlicensing/migrations.original/0015_auto_20210315_1642.py similarity index 100% rename from mooringlicensing/migrations/0015_auto_20210315_1642.py rename to mooringlicensing/migrations.original/0015_auto_20210315_1642.py diff --git a/mooringlicensing/migrations/0015_auto_20210316_1025.py b/mooringlicensing/migrations.original/0015_auto_20210316_1025.py similarity index 100% rename from mooringlicensing/migrations/0015_auto_20210316_1025.py rename to mooringlicensing/migrations.original/0015_auto_20210316_1025.py diff --git a/mooringlicensing/migrations/0016_merge_20210316_1028.py b/mooringlicensing/migrations.original/0016_merge_20210316_1028.py similarity index 100% rename from mooringlicensing/migrations/0016_merge_20210316_1028.py rename to mooringlicensing/migrations.original/0016_merge_20210316_1028.py diff --git a/mooringlicensing/migrations/0017_auto_20210318_1541.py b/mooringlicensing/migrations.original/0017_auto_20210318_1541.py similarity index 100% rename from mooringlicensing/migrations/0017_auto_20210318_1541.py rename to mooringlicensing/migrations.original/0017_auto_20210318_1541.py diff --git a/mooringlicensing/migrations/0018_vesseldetails_vessel_type.py b/mooringlicensing/migrations.original/0018_vesseldetails_vessel_type.py similarity index 100% rename from mooringlicensing/migrations/0018_vesseldetails_vessel_type.py rename to mooringlicensing/migrations.original/0018_vesseldetails_vessel_type.py diff --git a/mooringlicensing/migrations/0019_electoralrolldocument.py b/mooringlicensing/migrations.original/0019_electoralrolldocument.py similarity index 100% rename from mooringlicensing/migrations/0019_electoralrolldocument.py rename to mooringlicensing/migrations.original/0019_electoralrolldocument.py diff --git a/mooringlicensing/migrations/0020_auto_20210323_1104.py b/mooringlicensing/migrations.original/0020_auto_20210323_1104.py similarity index 100% rename from mooringlicensing/migrations/0020_auto_20210323_1104.py rename to mooringlicensing/migrations.original/0020_auto_20210323_1104.py diff --git a/mooringlicensing/migrations/0021_auto_20210323_1152.py b/mooringlicensing/migrations.original/0021_auto_20210323_1152.py similarity index 100% rename from mooringlicensing/migrations/0021_auto_20210323_1152.py rename to mooringlicensing/migrations.original/0021_auto_20210323_1152.py diff --git a/mooringlicensing/migrations/0022_auto_20210323_1158.py b/mooringlicensing/migrations.original/0022_auto_20210323_1158.py similarity index 100% rename from mooringlicensing/migrations/0022_auto_20210323_1158.py rename to mooringlicensing/migrations.original/0022_auto_20210323_1158.py diff --git a/mooringlicensing/migrations/0022_auto_20210324_0917.py b/mooringlicensing/migrations.original/0022_auto_20210324_0917.py similarity index 100% rename from mooringlicensing/migrations/0022_auto_20210324_0917.py rename to mooringlicensing/migrations.original/0022_auto_20210324_0917.py diff --git a/mooringlicensing/migrations/0023_merge_20210324_0948.py b/mooringlicensing/migrations.original/0023_merge_20210324_0948.py similarity index 100% rename from mooringlicensing/migrations/0023_merge_20210324_0948.py rename to mooringlicensing/migrations.original/0023_merge_20210324_0948.py diff --git a/mooringlicensing/migrations/0024_auto_20210324_0949.py b/mooringlicensing/migrations.original/0024_auto_20210324_0949.py similarity index 100% rename from mooringlicensing/migrations/0024_auto_20210324_0949.py rename to mooringlicensing/migrations.original/0024_auto_20210324_0949.py diff --git a/mooringlicensing/migrations/0025_vesselownership_berth_mooring.py b/mooringlicensing/migrations.original/0025_vesselownership_berth_mooring.py similarity index 100% rename from mooringlicensing/migrations/0025_vesselownership_berth_mooring.py rename to mooringlicensing/migrations.original/0025_vesselownership_berth_mooring.py diff --git a/mooringlicensing/migrations/0026_auto_20210324_1500.py b/mooringlicensing/migrations.original/0026_auto_20210324_1500.py similarity index 100% rename from mooringlicensing/migrations/0026_auto_20210324_1500.py rename to mooringlicensing/migrations.original/0026_auto_20210324_1500.py diff --git a/mooringlicensing/migrations/0027_auto_20210324_1501.py b/mooringlicensing/migrations.original/0027_auto_20210324_1501.py similarity index 100% rename from mooringlicensing/migrations/0027_auto_20210324_1501.py rename to mooringlicensing/migrations.original/0027_auto_20210324_1501.py diff --git a/mooringlicensing/migrations/0028_auto_20210324_1536.py b/mooringlicensing/migrations.original/0028_auto_20210324_1536.py similarity index 100% rename from mooringlicensing/migrations/0028_auto_20210324_1536.py rename to mooringlicensing/migrations.original/0028_auto_20210324_1536.py diff --git a/mooringlicensing/migrations/0029_applicationfee.py b/mooringlicensing/migrations.original/0029_applicationfee.py similarity index 100% rename from mooringlicensing/migrations/0029_applicationfee.py rename to mooringlicensing/migrations.original/0029_applicationfee.py diff --git a/mooringlicensing/migrations/0029_auto_20210325_0946.py b/mooringlicensing/migrations.original/0029_auto_20210325_0946.py similarity index 100% rename from mooringlicensing/migrations/0029_auto_20210325_0946.py rename to mooringlicensing/migrations.original/0029_auto_20210325_0946.py diff --git a/mooringlicensing/migrations/0030_applicationfee_invoice_reference.py b/mooringlicensing/migrations.original/0030_applicationfee_invoice_reference.py similarity index 100% rename from mooringlicensing/migrations/0030_applicationfee_invoice_reference.py rename to mooringlicensing/migrations.original/0030_applicationfee_invoice_reference.py diff --git a/mooringlicensing/migrations/0030_proposal_vessel_details.py b/mooringlicensing/migrations.original/0030_proposal_vessel_details.py similarity index 100% rename from mooringlicensing/migrations/0030_proposal_vessel_details.py rename to mooringlicensing/migrations.original/0030_proposal_vessel_details.py diff --git a/mooringlicensing/migrations/0031_remove_proposal_vessel_details_many.py b/mooringlicensing/migrations.original/0031_remove_proposal_vessel_details_many.py similarity index 100% rename from mooringlicensing/migrations/0031_remove_proposal_vessel_details_many.py rename to mooringlicensing/migrations.original/0031_remove_proposal_vessel_details_many.py diff --git a/mooringlicensing/migrations/0032_auto_20210325_1530.py b/mooringlicensing/migrations.original/0032_auto_20210325_1530.py similarity index 100% rename from mooringlicensing/migrations/0032_auto_20210325_1530.py rename to mooringlicensing/migrations.original/0032_auto_20210325_1530.py diff --git a/mooringlicensing/migrations/0033_auto_20210325_1549.py b/mooringlicensing/migrations.original/0033_auto_20210325_1549.py similarity index 100% rename from mooringlicensing/migrations/0033_auto_20210325_1549.py rename to mooringlicensing/migrations.original/0033_auto_20210325_1549.py diff --git a/mooringlicensing/migrations/0034_auto_20210325_1757.py b/mooringlicensing/migrations.original/0034_auto_20210325_1757.py similarity index 100% rename from mooringlicensing/migrations/0034_auto_20210325_1757.py rename to mooringlicensing/migrations.original/0034_auto_20210325_1757.py diff --git a/mooringlicensing/migrations/0035_auto_20210326_1434.py b/mooringlicensing/migrations.original/0035_auto_20210326_1434.py similarity index 100% rename from mooringlicensing/migrations/0035_auto_20210326_1434.py rename to mooringlicensing/migrations.original/0035_auto_20210326_1434.py diff --git a/mooringlicensing/migrations/0036_auto_20210326_1445.py b/mooringlicensing/migrations.original/0036_auto_20210326_1445.py similarity index 100% rename from mooringlicensing/migrations/0036_auto_20210326_1445.py rename to mooringlicensing/migrations.original/0036_auto_20210326_1445.py diff --git a/mooringlicensing/migrations/0037_merge_20210326_1607.py b/mooringlicensing/migrations.original/0037_merge_20210326_1607.py similarity index 100% rename from mooringlicensing/migrations/0037_merge_20210326_1607.py rename to mooringlicensing/migrations.original/0037_merge_20210326_1607.py diff --git a/mooringlicensing/migrations/0038_auto_20210326_1723.py b/mooringlicensing/migrations.original/0038_auto_20210326_1723.py similarity index 100% rename from mooringlicensing/migrations/0038_auto_20210326_1723.py rename to mooringlicensing/migrations.original/0038_auto_20210326_1723.py diff --git a/mooringlicensing/migrations/0039_auto_20210329_1517.py b/mooringlicensing/migrations.original/0039_auto_20210329_1517.py similarity index 100% rename from mooringlicensing/migrations/0039_auto_20210329_1517.py rename to mooringlicensing/migrations.original/0039_auto_20210329_1517.py diff --git a/mooringlicensing/migrations/0039_proposal_rego_no.py b/mooringlicensing/migrations.original/0039_proposal_rego_no.py similarity index 100% rename from mooringlicensing/migrations/0039_proposal_rego_no.py rename to mooringlicensing/migrations.original/0039_proposal_rego_no.py diff --git a/mooringlicensing/migrations/0040_proposal_proposal_type.py b/mooringlicensing/migrations.original/0040_proposal_proposal_type.py similarity index 100% rename from mooringlicensing/migrations/0040_proposal_proposal_type.py rename to mooringlicensing/migrations.original/0040_proposal_proposal_type.py diff --git a/mooringlicensing/migrations/0040_proposal_vessel_id.py b/mooringlicensing/migrations.original/0040_proposal_vessel_id.py similarity index 100% rename from mooringlicensing/migrations/0040_proposal_vessel_id.py rename to mooringlicensing/migrations.original/0040_proposal_vessel_id.py diff --git a/mooringlicensing/migrations/0041_auto_20210329_1531.py b/mooringlicensing/migrations.original/0041_auto_20210329_1531.py similarity index 100% rename from mooringlicensing/migrations/0041_auto_20210329_1531.py rename to mooringlicensing/migrations.original/0041_auto_20210329_1531.py diff --git a/mooringlicensing/migrations/0041_proposal_individual_owner.py b/mooringlicensing/migrations.original/0041_proposal_individual_owner.py similarity index 100% rename from mooringlicensing/migrations/0041_proposal_individual_owner.py rename to mooringlicensing/migrations.original/0041_proposal_individual_owner.py diff --git a/mooringlicensing/migrations/0042_auto_20210329_1554.py b/mooringlicensing/migrations.original/0042_auto_20210329_1554.py similarity index 100% rename from mooringlicensing/migrations/0042_auto_20210329_1554.py rename to mooringlicensing/migrations.original/0042_auto_20210329_1554.py diff --git a/mooringlicensing/migrations/0042_proposal_insurance_choice.py b/mooringlicensing/migrations.original/0042_proposal_insurance_choice.py similarity index 100% rename from mooringlicensing/migrations/0042_proposal_insurance_choice.py rename to mooringlicensing/migrations.original/0042_proposal_insurance_choice.py diff --git a/mooringlicensing/migrations/0043_auto_20210329_1617.py b/mooringlicensing/migrations.original/0043_auto_20210329_1617.py similarity index 100% rename from mooringlicensing/migrations/0043_auto_20210329_1617.py rename to mooringlicensing/migrations.original/0043_auto_20210329_1617.py diff --git a/mooringlicensing/migrations/0043_mooringbay.py b/mooringlicensing/migrations.original/0043_mooringbay.py similarity index 100% rename from mooringlicensing/migrations/0043_mooringbay.py rename to mooringlicensing/migrations.original/0043_mooringbay.py diff --git a/mooringlicensing/migrations/0044_feeseasonapplicationtype.py b/mooringlicensing/migrations.original/0044_feeseasonapplicationtype.py similarity index 100% rename from mooringlicensing/migrations/0044_feeseasonapplicationtype.py rename to mooringlicensing/migrations.original/0044_feeseasonapplicationtype.py diff --git a/mooringlicensing/migrations/0044_mooringbay_mooring_bay_id.py b/mooringlicensing/migrations.original/0044_mooringbay_mooring_bay_id.py similarity index 100% rename from mooringlicensing/migrations/0044_mooringbay_mooring_bay_id.py rename to mooringlicensing/migrations.original/0044_mooringbay_mooring_bay_id.py diff --git a/mooringlicensing/migrations/0045_auto_20210330_0853.py b/mooringlicensing/migrations.original/0045_auto_20210330_0853.py similarity index 100% rename from mooringlicensing/migrations/0045_auto_20210330_0853.py rename to mooringlicensing/migrations.original/0045_auto_20210330_0853.py diff --git a/mooringlicensing/migrations/0045_auto_20210406_1440.py b/mooringlicensing/migrations.original/0045_auto_20210406_1440.py similarity index 100% rename from mooringlicensing/migrations/0045_auto_20210406_1440.py rename to mooringlicensing/migrations.original/0045_auto_20210406_1440.py diff --git a/mooringlicensing/migrations/0046_auto_20210330_0901.py b/mooringlicensing/migrations.original/0046_auto_20210330_0901.py similarity index 100% rename from mooringlicensing/migrations/0046_auto_20210330_0901.py rename to mooringlicensing/migrations.original/0046_auto_20210330_0901.py diff --git a/mooringlicensing/migrations/0046_mooringbay_active.py b/mooringlicensing/migrations.original/0046_mooringbay_active.py similarity index 100% rename from mooringlicensing/migrations/0046_mooringbay_active.py rename to mooringlicensing/migrations.original/0046_mooringbay_active.py diff --git a/mooringlicensing/migrations/0047_proposal_preferred_bay.py b/mooringlicensing/migrations.original/0047_proposal_preferred_bay.py similarity index 100% rename from mooringlicensing/migrations/0047_proposal_preferred_bay.py rename to mooringlicensing/migrations.original/0047_proposal_preferred_bay.py diff --git a/mooringlicensing/migrations/0047_vesselsizecategorygroup.py b/mooringlicensing/migrations.original/0047_vesselsizecategorygroup.py similarity index 100% rename from mooringlicensing/migrations/0047_vesselsizecategorygroup.py rename to mooringlicensing/migrations.original/0047_vesselsizecategorygroup.py diff --git a/mooringlicensing/migrations/0048_auto_20210330_0917.py b/mooringlicensing/migrations.original/0048_auto_20210330_0917.py similarity index 100% rename from mooringlicensing/migrations/0048_auto_20210330_0917.py rename to mooringlicensing/migrations.original/0048_auto_20210330_0917.py diff --git a/mooringlicensing/migrations/0049_fee.py b/mooringlicensing/migrations.original/0049_fee.py similarity index 100% rename from mooringlicensing/migrations/0049_fee.py rename to mooringlicensing/migrations.original/0049_fee.py diff --git a/mooringlicensing/migrations/0050_auto_20210330_0955.py b/mooringlicensing/migrations.original/0050_auto_20210330_0955.py similarity index 100% rename from mooringlicensing/migrations/0050_auto_20210330_0955.py rename to mooringlicensing/migrations.original/0050_auto_20210330_0955.py diff --git a/mooringlicensing/migrations/0051_auto_20210330_1023.py b/mooringlicensing/migrations.original/0051_auto_20210330_1023.py similarity index 100% rename from mooringlicensing/migrations/0051_auto_20210330_1023.py rename to mooringlicensing/migrations.original/0051_auto_20210330_1023.py diff --git a/mooringlicensing/migrations/0052_merge_20210401_1042.py b/mooringlicensing/migrations.original/0052_merge_20210401_1042.py similarity index 100% rename from mooringlicensing/migrations/0052_merge_20210401_1042.py rename to mooringlicensing/migrations.original/0052_merge_20210401_1042.py diff --git a/mooringlicensing/migrations/0053_auto_20210401_1046.py b/mooringlicensing/migrations.original/0053_auto_20210401_1046.py similarity index 100% rename from mooringlicensing/migrations/0053_auto_20210401_1046.py rename to mooringlicensing/migrations.original/0053_auto_20210401_1046.py diff --git a/mooringlicensing/migrations/0054_auto_20210401_1057.py b/mooringlicensing/migrations.original/0054_auto_20210401_1057.py similarity index 100% rename from mooringlicensing/migrations/0054_auto_20210401_1057.py rename to mooringlicensing/migrations.original/0054_auto_20210401_1057.py diff --git a/mooringlicensing/migrations/0055_auto_20210401_1153.py b/mooringlicensing/migrations.original/0055_auto_20210401_1153.py similarity index 100% rename from mooringlicensing/migrations/0055_auto_20210401_1153.py rename to mooringlicensing/migrations.original/0055_auto_20210401_1153.py diff --git a/mooringlicensing/migrations/0056_auto_20210401_1550.py b/mooringlicensing/migrations.original/0056_auto_20210401_1550.py similarity index 100% rename from mooringlicensing/migrations/0056_auto_20210401_1550.py rename to mooringlicensing/migrations.original/0056_auto_20210401_1550.py diff --git a/mooringlicensing/migrations/0057_merge_20210406_1453.py b/mooringlicensing/migrations.original/0057_merge_20210406_1453.py similarity index 100% rename from mooringlicensing/migrations/0057_merge_20210406_1453.py rename to mooringlicensing/migrations.original/0057_merge_20210406_1453.py diff --git a/mooringlicensing/migrations/0058_feeconstructor_incur_gst.py b/mooringlicensing/migrations.original/0058_feeconstructor_incur_gst.py similarity index 100% rename from mooringlicensing/migrations/0058_feeconstructor_incur_gst.py rename to mooringlicensing/migrations.original/0058_feeconstructor_incur_gst.py diff --git a/mooringlicensing/migrations/0059_auto_20210406_1504.py b/mooringlicensing/migrations.original/0059_auto_20210406_1504.py similarity index 100% rename from mooringlicensing/migrations/0059_auto_20210406_1504.py rename to mooringlicensing/migrations.original/0059_auto_20210406_1504.py diff --git a/mooringlicensing/migrations/0060_merge_20210406_1554.py b/mooringlicensing/migrations.original/0060_merge_20210406_1554.py similarity index 100% rename from mooringlicensing/migrations/0060_merge_20210406_1554.py rename to mooringlicensing/migrations.original/0060_merge_20210406_1554.py diff --git a/mooringlicensing/migrations/0061_merge_20210406_1707.py b/mooringlicensing/migrations.original/0061_merge_20210406_1707.py similarity index 100% rename from mooringlicensing/migrations/0061_merge_20210406_1707.py rename to mooringlicensing/migrations.original/0061_merge_20210406_1707.py diff --git a/mooringlicensing/migrations/0061_merge_20210407_0757.py b/mooringlicensing/migrations.original/0061_merge_20210407_0757.py similarity index 100% rename from mooringlicensing/migrations/0061_merge_20210407_0757.py rename to mooringlicensing/migrations.original/0061_merge_20210407_0757.py diff --git a/mooringlicensing/migrations/0062_auto_20210407_0757.py b/mooringlicensing/migrations.original/0062_auto_20210407_0757.py similarity index 100% rename from mooringlicensing/migrations/0062_auto_20210407_0757.py rename to mooringlicensing/migrations.original/0062_auto_20210407_0757.py diff --git a/mooringlicensing/migrations/0062_auto_20210407_1446.py b/mooringlicensing/migrations.original/0062_auto_20210407_1446.py similarity index 100% rename from mooringlicensing/migrations/0062_auto_20210407_1446.py rename to mooringlicensing/migrations.original/0062_auto_20210407_1446.py diff --git a/mooringlicensing/migrations/0063_auto_20210407_1652.py b/mooringlicensing/migrations.original/0063_auto_20210407_1652.py similarity index 100% rename from mooringlicensing/migrations/0063_auto_20210407_1652.py rename to mooringlicensing/migrations.original/0063_auto_20210407_1652.py diff --git a/mooringlicensing/migrations/0064_merge_20210408_0902.py b/mooringlicensing/migrations.original/0064_merge_20210408_0902.py similarity index 100% rename from mooringlicensing/migrations/0064_merge_20210408_0902.py rename to mooringlicensing/migrations.original/0064_merge_20210408_0902.py diff --git a/mooringlicensing/migrations/0065_feeconstructor_enabled.py b/mooringlicensing/migrations.original/0065_feeconstructor_enabled.py similarity index 100% rename from mooringlicensing/migrations/0065_feeconstructor_enabled.py rename to mooringlicensing/migrations.original/0065_feeconstructor_enabled.py diff --git a/mooringlicensing/migrations/0065_proposal_bay_preferences_numbered.py b/mooringlicensing/migrations.original/0065_proposal_bay_preferences_numbered.py similarity index 100% rename from mooringlicensing/migrations/0065_proposal_bay_preferences_numbered.py rename to mooringlicensing/migrations.original/0065_proposal_bay_preferences_numbered.py diff --git a/mooringlicensing/migrations/0066_auto_20210408_1434.py b/mooringlicensing/migrations.original/0066_auto_20210408_1434.py similarity index 100% rename from mooringlicensing/migrations/0066_auto_20210408_1434.py rename to mooringlicensing/migrations.original/0066_auto_20210408_1434.py diff --git a/mooringlicensing/migrations/0066_auto_20210408_1850.py b/mooringlicensing/migrations.original/0066_auto_20210408_1850.py similarity index 100% rename from mooringlicensing/migrations/0066_auto_20210408_1850.py rename to mooringlicensing/migrations.original/0066_auto_20210408_1850.py diff --git a/mooringlicensing/migrations/0067_auto_20210408_1521.py b/mooringlicensing/migrations.original/0067_auto_20210408_1521.py similarity index 100% rename from mooringlicensing/migrations/0067_auto_20210408_1521.py rename to mooringlicensing/migrations.original/0067_auto_20210408_1521.py diff --git a/mooringlicensing/migrations/0067_proposal_mooring_authorisation_preference.py b/mooringlicensing/migrations.original/0067_proposal_mooring_authorisation_preference.py similarity index 100% rename from mooringlicensing/migrations/0067_proposal_mooring_authorisation_preference.py rename to mooringlicensing/migrations.original/0067_proposal_mooring_authorisation_preference.py diff --git a/mooringlicensing/migrations/0068_merge_20210409_0850.py b/mooringlicensing/migrations.original/0068_merge_20210409_0850.py similarity index 100% rename from mooringlicensing/migrations/0068_merge_20210409_0850.py rename to mooringlicensing/migrations.original/0068_merge_20210409_0850.py diff --git a/mooringlicensing/migrations/0069_applicationfee_fee_constructor.py b/mooringlicensing/migrations.original/0069_applicationfee_fee_constructor.py similarity index 100% rename from mooringlicensing/migrations/0069_applicationfee_fee_constructor.py rename to mooringlicensing/migrations.original/0069_applicationfee_fee_constructor.py diff --git a/mooringlicensing/migrations/0070_auto_20210409_1643.py b/mooringlicensing/migrations.original/0070_auto_20210409_1643.py similarity index 100% rename from mooringlicensing/migrations/0070_auto_20210409_1643.py rename to mooringlicensing/migrations.original/0070_auto_20210409_1643.py diff --git a/mooringlicensing/migrations/0070_merge_20210412_0946.py b/mooringlicensing/migrations.original/0070_merge_20210412_0946.py similarity index 100% rename from mooringlicensing/migrations/0070_merge_20210412_0946.py rename to mooringlicensing/migrations.original/0070_merge_20210412_0946.py diff --git a/mooringlicensing/migrations/0071_auto_20210412_1341.py b/mooringlicensing/migrations.original/0071_auto_20210412_1341.py similarity index 100% rename from mooringlicensing/migrations/0071_auto_20210412_1341.py rename to mooringlicensing/migrations.original/0071_auto_20210412_1341.py diff --git a/mooringlicensing/migrations/0071_merge_20210412_1108.py b/mooringlicensing/migrations.original/0071_merge_20210412_1108.py similarity index 100% rename from mooringlicensing/migrations/0071_merge_20210412_1108.py rename to mooringlicensing/migrations.original/0071_merge_20210412_1108.py diff --git a/mooringlicensing/migrations/0072_dcvorganisation_dcvpermit_dcvpermitdocument_dcvvessel.py b/mooringlicensing/migrations.original/0072_dcvorganisation_dcvpermit_dcvpermitdocument_dcvvessel.py similarity index 100% rename from mooringlicensing/migrations/0072_dcvorganisation_dcvpermit_dcvpermitdocument_dcvvessel.py rename to mooringlicensing/migrations.original/0072_dcvorganisation_dcvpermit_dcvpermitdocument_dcvvessel.py diff --git a/mooringlicensing/migrations/0072_merge_20210413_1538.py b/mooringlicensing/migrations.original/0072_merge_20210413_1538.py similarity index 100% rename from mooringlicensing/migrations/0072_merge_20210413_1538.py rename to mooringlicensing/migrations.original/0072_merge_20210413_1538.py diff --git a/mooringlicensing/migrations/0073_insurancecertificatedocument.py b/mooringlicensing/migrations.original/0073_insurancecertificatedocument.py similarity index 100% rename from mooringlicensing/migrations/0073_insurancecertificatedocument.py rename to mooringlicensing/migrations.original/0073_insurancecertificatedocument.py diff --git a/mooringlicensing/migrations/0073_merge_20210414_0829.py b/mooringlicensing/migrations.original/0073_merge_20210414_0829.py similarity index 100% rename from mooringlicensing/migrations/0073_merge_20210414_0829.py rename to mooringlicensing/migrations.original/0073_merge_20210414_0829.py diff --git a/mooringlicensing/migrations/0074_auto_20210414_1026.py b/mooringlicensing/migrations.original/0074_auto_20210414_1026.py similarity index 100% rename from mooringlicensing/migrations/0074_auto_20210414_1026.py rename to mooringlicensing/migrations.original/0074_auto_20210414_1026.py diff --git a/mooringlicensing/migrations/0075_merge_20210415_0931.py b/mooringlicensing/migrations.original/0075_merge_20210415_0931.py similarity index 100% rename from mooringlicensing/migrations/0075_merge_20210415_0931.py rename to mooringlicensing/migrations.original/0075_merge_20210415_0931.py diff --git a/mooringlicensing/migrations/0076_auto_20210415_0942.py b/mooringlicensing/migrations.original/0076_auto_20210415_0942.py similarity index 100% rename from mooringlicensing/migrations/0076_auto_20210415_0942.py rename to mooringlicensing/migrations.original/0076_auto_20210415_0942.py diff --git a/mooringlicensing/migrations/0077_dcvpermit_submitter.py b/mooringlicensing/migrations.original/0077_dcvpermit_submitter.py similarity index 100% rename from mooringlicensing/migrations/0077_dcvpermit_submitter.py rename to mooringlicensing/migrations.original/0077_dcvpermit_submitter.py diff --git a/mooringlicensing/migrations/0078_auto_20210415_1122.py b/mooringlicensing/migrations.original/0078_auto_20210415_1122.py similarity index 100% rename from mooringlicensing/migrations/0078_auto_20210415_1122.py rename to mooringlicensing/migrations.original/0078_auto_20210415_1122.py diff --git a/mooringlicensing/migrations/0079_dcvpermit_lodgement_date.py b/mooringlicensing/migrations.original/0079_dcvpermit_lodgement_date.py similarity index 100% rename from mooringlicensing/migrations/0079_dcvpermit_lodgement_date.py rename to mooringlicensing/migrations.original/0079_dcvpermit_lodgement_date.py diff --git a/mooringlicensing/migrations/0080_remove_dcvpermit_lodgement_date.py b/mooringlicensing/migrations.original/0080_remove_dcvpermit_lodgement_date.py similarity index 100% rename from mooringlicensing/migrations/0080_remove_dcvpermit_lodgement_date.py rename to mooringlicensing/migrations.original/0080_remove_dcvpermit_lodgement_date.py diff --git a/mooringlicensing/migrations/0081_dcvpermit_lodgement_datetime.py b/mooringlicensing/migrations.original/0081_dcvpermit_lodgement_datetime.py similarity index 100% rename from mooringlicensing/migrations/0081_dcvpermit_lodgement_datetime.py rename to mooringlicensing/migrations.original/0081_dcvpermit_lodgement_datetime.py diff --git a/mooringlicensing/migrations/0082_applicationtype_oracle_code_application.py b/mooringlicensing/migrations.original/0082_applicationtype_oracle_code_application.py similarity index 100% rename from mooringlicensing/migrations/0082_applicationtype_oracle_code_application.py rename to mooringlicensing/migrations.original/0082_applicationtype_oracle_code_application.py diff --git a/mooringlicensing/migrations/0083_auto_20210415_1436.py b/mooringlicensing/migrations.original/0083_auto_20210415_1436.py similarity index 100% rename from mooringlicensing/migrations/0083_auto_20210415_1436.py rename to mooringlicensing/migrations.original/0083_auto_20210415_1436.py diff --git a/mooringlicensing/migrations/0084_auto_20210415_1632.py b/mooringlicensing/migrations.original/0084_auto_20210415_1632.py similarity index 100% rename from mooringlicensing/migrations/0084_auto_20210415_1632.py rename to mooringlicensing/migrations.original/0084_auto_20210415_1632.py diff --git a/mooringlicensing/migrations/0085_auto_20210415_1632.py b/mooringlicensing/migrations.original/0085_auto_20210415_1632.py similarity index 100% rename from mooringlicensing/migrations/0085_auto_20210415_1632.py rename to mooringlicensing/migrations.original/0085_auto_20210415_1632.py diff --git a/mooringlicensing/migrations/0086_auto_20210416_1025.py b/mooringlicensing/migrations.original/0086_auto_20210416_1025.py similarity index 100% rename from mooringlicensing/migrations/0086_auto_20210416_1025.py rename to mooringlicensing/migrations.original/0086_auto_20210416_1025.py diff --git a/mooringlicensing/migrations/0087_dcvpermit_fee_season.py b/mooringlicensing/migrations.original/0087_dcvpermit_fee_season.py similarity index 100% rename from mooringlicensing/migrations/0087_dcvpermit_fee_season.py rename to mooringlicensing/migrations.original/0087_dcvpermit_fee_season.py diff --git a/mooringlicensing/migrations/0088_auto_20210416_1621.py b/mooringlicensing/migrations.original/0088_auto_20210416_1621.py similarity index 100% rename from mooringlicensing/migrations/0088_auto_20210416_1621.py rename to mooringlicensing/migrations.original/0088_auto_20210416_1621.py diff --git a/mooringlicensing/migrations/0088_remove_proposal_percentage.py b/mooringlicensing/migrations.original/0088_remove_proposal_percentage.py similarity index 100% rename from mooringlicensing/migrations/0088_remove_proposal_percentage.py rename to mooringlicensing/migrations.original/0088_remove_proposal_percentage.py diff --git a/mooringlicensing/migrations/0089_proposal_percentage.py b/mooringlicensing/migrations.original/0089_proposal_percentage.py similarity index 100% rename from mooringlicensing/migrations/0089_proposal_percentage.py rename to mooringlicensing/migrations.original/0089_proposal_percentage.py diff --git a/mooringlicensing/migrations/0090_auto_20210419_1014.py b/mooringlicensing/migrations.original/0090_auto_20210419_1014.py similarity index 100% rename from mooringlicensing/migrations/0090_auto_20210419_1014.py rename to mooringlicensing/migrations.original/0090_auto_20210419_1014.py diff --git a/mooringlicensing/migrations/0091_merge_20210419_1022.py b/mooringlicensing/migrations.original/0091_merge_20210419_1022.py similarity index 100% rename from mooringlicensing/migrations/0091_merge_20210419_1022.py rename to mooringlicensing/migrations.original/0091_merge_20210419_1022.py diff --git a/mooringlicensing/migrations/0092_auto_20210419_1158.py b/mooringlicensing/migrations.original/0092_auto_20210419_1158.py similarity index 100% rename from mooringlicensing/migrations/0092_auto_20210419_1158.py rename to mooringlicensing/migrations.original/0092_auto_20210419_1158.py diff --git a/mooringlicensing/migrations/0093_dcvadmission.py b/mooringlicensing/migrations.original/0093_dcvadmission.py similarity index 100% rename from mooringlicensing/migrations/0093_dcvadmission.py rename to mooringlicensing/migrations.original/0093_dcvadmission.py diff --git a/mooringlicensing/migrations/0094_dcvadmissionfee.py b/mooringlicensing/migrations.original/0094_dcvadmissionfee.py similarity index 100% rename from mooringlicensing/migrations/0094_dcvadmissionfee.py rename to mooringlicensing/migrations.original/0094_dcvadmissionfee.py diff --git a/mooringlicensing/migrations/0095_dcvadmission_lodgement_datetime.py b/mooringlicensing/migrations.original/0095_dcvadmission_lodgement_datetime.py similarity index 100% rename from mooringlicensing/migrations/0095_dcvadmission_lodgement_datetime.py rename to mooringlicensing/migrations.original/0095_dcvadmission_lodgement_datetime.py diff --git a/mooringlicensing/migrations/0096_dcvadmissiondocument.py b/mooringlicensing/migrations.original/0096_dcvadmissiondocument.py similarity index 100% rename from mooringlicensing/migrations/0096_dcvadmissiondocument.py rename to mooringlicensing/migrations.original/0096_dcvadmissiondocument.py diff --git a/mooringlicensing/migrations/0097_auto_20210423_0931.py b/mooringlicensing/migrations.original/0097_auto_20210423_0931.py similarity index 100% rename from mooringlicensing/migrations/0097_auto_20210423_0931.py rename to mooringlicensing/migrations.original/0097_auto_20210423_0931.py diff --git a/mooringlicensing/migrations/0098_admissiontype_agegroup.py b/mooringlicensing/migrations.original/0098_admissiontype_agegroup.py similarity index 100% rename from mooringlicensing/migrations/0098_admissiontype_agegroup.py rename to mooringlicensing/migrations.original/0098_admissiontype_agegroup.py diff --git a/mooringlicensing/migrations/0099_auto_20210423_1121.py b/mooringlicensing/migrations.original/0099_auto_20210423_1121.py similarity index 100% rename from mooringlicensing/migrations/0099_auto_20210423_1121.py rename to mooringlicensing/migrations.original/0099_auto_20210423_1121.py diff --git a/mooringlicensing/migrations/0100_auto_20210423_1125.py b/mooringlicensing/migrations.original/0100_auto_20210423_1125.py similarity index 100% rename from mooringlicensing/migrations/0100_auto_20210423_1125.py rename to mooringlicensing/migrations.original/0100_auto_20210423_1125.py diff --git a/mooringlicensing/migrations/0101_auto_20210423_1436.py b/mooringlicensing/migrations.original/0101_auto_20210423_1436.py similarity index 100% rename from mooringlicensing/migrations/0101_auto_20210423_1436.py rename to mooringlicensing/migrations.original/0101_auto_20210423_1436.py diff --git a/mooringlicensing/migrations/0102_dcvadmissionfee_fee_constructor.py b/mooringlicensing/migrations.original/0102_dcvadmissionfee_fee_constructor.py similarity index 100% rename from mooringlicensing/migrations/0102_dcvadmissionfee_fee_constructor.py rename to mooringlicensing/migrations.original/0102_dcvadmissionfee_fee_constructor.py diff --git a/mooringlicensing/migrations/0103_auto_20210423_1539.py b/mooringlicensing/migrations.original/0103_auto_20210423_1539.py similarity index 100% rename from mooringlicensing/migrations/0103_auto_20210423_1539.py rename to mooringlicensing/migrations.original/0103_auto_20210423_1539.py diff --git a/mooringlicensing/migrations/0104_dcvadmissionarrival_numberofpeople.py b/mooringlicensing/migrations.original/0104_dcvadmissionarrival_numberofpeople.py similarity index 100% rename from mooringlicensing/migrations/0104_dcvadmissionarrival_numberofpeople.py rename to mooringlicensing/migrations.original/0104_dcvadmissionarrival_numberofpeople.py diff --git a/mooringlicensing/migrations/0105_auto_20210423_1607.py b/mooringlicensing/migrations.original/0105_auto_20210423_1607.py similarity index 100% rename from mooringlicensing/migrations/0105_auto_20210423_1607.py rename to mooringlicensing/migrations.original/0105_auto_20210423_1607.py diff --git a/mooringlicensing/migrations/0106_auto_20210427_1247.py b/mooringlicensing/migrations.original/0106_auto_20210427_1247.py similarity index 100% rename from mooringlicensing/migrations/0106_auto_20210427_1247.py rename to mooringlicensing/migrations.original/0106_auto_20210427_1247.py diff --git a/mooringlicensing/migrations/0107_auto_20210430_1340.py b/mooringlicensing/migrations.original/0107_auto_20210430_1340.py similarity index 100% rename from mooringlicensing/migrations/0107_auto_20210430_1340.py rename to mooringlicensing/migrations.original/0107_auto_20210430_1340.py diff --git a/mooringlicensing/migrations/0108_auto_20210430_1416.py b/mooringlicensing/migrations.original/0108_auto_20210430_1416.py similarity index 100% rename from mooringlicensing/migrations/0108_auto_20210430_1416.py rename to mooringlicensing/migrations.original/0108_auto_20210430_1416.py diff --git a/mooringlicensing/migrations/0109_auto_20210503_1318.py b/mooringlicensing/migrations.original/0109_auto_20210503_1318.py similarity index 100% rename from mooringlicensing/migrations/0109_auto_20210503_1318.py rename to mooringlicensing/migrations.original/0109_auto_20210503_1318.py diff --git a/mooringlicensing/migrations/0110_dcvadmission_dcv_vessel.py b/mooringlicensing/migrations.original/0110_dcvadmission_dcv_vessel.py similarity index 100% rename from mooringlicensing/migrations/0110_dcvadmission_dcv_vessel.py rename to mooringlicensing/migrations.original/0110_dcvadmission_dcv_vessel.py diff --git a/mooringlicensing/migrations/0111_auto_20210510_1551.py b/mooringlicensing/migrations.original/0111_auto_20210510_1551.py similarity index 100% rename from mooringlicensing/migrations/0111_auto_20210510_1551.py rename to mooringlicensing/migrations.original/0111_auto_20210510_1551.py diff --git a/mooringlicensing/migrations/0111_proposalstandardrequirement_application_type.py b/mooringlicensing/migrations.original/0111_proposalstandardrequirement_application_type.py similarity index 100% rename from mooringlicensing/migrations/0111_proposalstandardrequirement_application_type.py rename to mooringlicensing/migrations.original/0111_proposalstandardrequirement_application_type.py diff --git a/mooringlicensing/migrations/0112_auto_20210511_1223.py b/mooringlicensing/migrations.original/0112_auto_20210511_1223.py similarity index 100% rename from mooringlicensing/migrations/0112_auto_20210511_1223.py rename to mooringlicensing/migrations.original/0112_auto_20210511_1223.py diff --git a/mooringlicensing/migrations/0113_auto_20210511_1534.py b/mooringlicensing/migrations.original/0113_auto_20210511_1534.py similarity index 100% rename from mooringlicensing/migrations/0113_auto_20210511_1534.py rename to mooringlicensing/migrations.original/0113_auto_20210511_1534.py diff --git a/mooringlicensing/migrations/0114_auto_20210512_1027.py b/mooringlicensing/migrations.original/0114_auto_20210512_1027.py similarity index 100% rename from mooringlicensing/migrations/0114_auto_20210512_1027.py rename to mooringlicensing/migrations.original/0114_auto_20210512_1027.py diff --git a/mooringlicensing/migrations/0115_merge_20210517_0953.py b/mooringlicensing/migrations.original/0115_merge_20210517_0953.py similarity index 100% rename from mooringlicensing/migrations/0115_merge_20210517_0953.py rename to mooringlicensing/migrations.original/0115_merge_20210517_0953.py diff --git a/mooringlicensing/migrations/0116_auto_20210518_1120.py b/mooringlicensing/migrations.original/0116_auto_20210518_1120.py similarity index 100% rename from mooringlicensing/migrations/0116_auto_20210518_1120.py rename to mooringlicensing/migrations.original/0116_auto_20210518_1120.py diff --git a/mooringlicensing/migrations/0117_auto_20210519_1133.py b/mooringlicensing/migrations.original/0117_auto_20210519_1133.py similarity index 100% rename from mooringlicensing/migrations/0117_auto_20210519_1133.py rename to mooringlicensing/migrations.original/0117_auto_20210519_1133.py diff --git a/mooringlicensing/migrations/0117_auto_20210521_0924.py b/mooringlicensing/migrations.original/0117_auto_20210521_0924.py similarity index 100% rename from mooringlicensing/migrations/0117_auto_20210521_0924.py rename to mooringlicensing/migrations.original/0117_auto_20210521_0924.py diff --git a/mooringlicensing/migrations/0118_mooring.py b/mooringlicensing/migrations.original/0118_mooring.py similarity index 100% rename from mooringlicensing/migrations/0118_mooring.py rename to mooringlicensing/migrations.original/0118_mooring.py diff --git a/mooringlicensing/migrations/0119_mooring_active.py b/mooringlicensing/migrations.original/0119_mooring_active.py similarity index 100% rename from mooringlicensing/migrations/0119_mooring_active.py rename to mooringlicensing/migrations.original/0119_mooring_active.py diff --git a/mooringlicensing/migrations/0120_merge_20210521_0925.py b/mooringlicensing/migrations.original/0120_merge_20210521_0925.py similarity index 100% rename from mooringlicensing/migrations/0120_merge_20210521_0925.py rename to mooringlicensing/migrations.original/0120_merge_20210521_0925.py diff --git a/mooringlicensing/migrations/0121_authoriseduserapplication_uuid.py b/mooringlicensing/migrations.original/0121_authoriseduserapplication_uuid.py similarity index 100% rename from mooringlicensing/migrations/0121_authoriseduserapplication_uuid.py rename to mooringlicensing/migrations.original/0121_authoriseduserapplication_uuid.py diff --git a/mooringlicensing/migrations/0121_auto_20210521_1637.py b/mooringlicensing/migrations.original/0121_auto_20210521_1637.py similarity index 100% rename from mooringlicensing/migrations/0121_auto_20210521_1637.py rename to mooringlicensing/migrations.original/0121_auto_20210521_1637.py diff --git a/mooringlicensing/migrations/0122_vessel_blocking_owner.py b/mooringlicensing/migrations.original/0122_vessel_blocking_owner.py similarity index 100% rename from mooringlicensing/migrations/0122_vessel_blocking_owner.py rename to mooringlicensing/migrations.original/0122_vessel_blocking_owner.py diff --git a/mooringlicensing/migrations/0123_auto_20210524_1145.py b/mooringlicensing/migrations.original/0123_auto_20210524_1145.py similarity index 100% rename from mooringlicensing/migrations/0123_auto_20210524_1145.py rename to mooringlicensing/migrations.original/0123_auto_20210524_1145.py diff --git a/mooringlicensing/migrations/0124_merge_20210525_0959.py b/mooringlicensing/migrations.original/0124_merge_20210525_0959.py similarity index 100% rename from mooringlicensing/migrations/0124_merge_20210525_0959.py rename to mooringlicensing/migrations.original/0124_merge_20210525_0959.py diff --git a/mooringlicensing/migrations/0124_merge_20210525_1121.py b/mooringlicensing/migrations.original/0124_merge_20210525_1121.py similarity index 100% rename from mooringlicensing/migrations/0124_merge_20210525_1121.py rename to mooringlicensing/migrations.original/0124_merge_20210525_1121.py diff --git a/mooringlicensing/migrations/0125_auto_20210525_1417.py b/mooringlicensing/migrations.original/0125_auto_20210525_1417.py similarity index 100% rename from mooringlicensing/migrations/0125_auto_20210525_1417.py rename to mooringlicensing/migrations.original/0125_auto_20210525_1417.py diff --git a/mooringlicensing/migrations/0126_merge_20210525_1438.py b/mooringlicensing/migrations.original/0126_merge_20210525_1438.py similarity index 100% rename from mooringlicensing/migrations/0126_merge_20210525_1438.py rename to mooringlicensing/migrations.original/0126_merge_20210525_1438.py diff --git a/mooringlicensing/migrations/0127_mooringlicenceapplication_uuid.py b/mooringlicensing/migrations.original/0127_mooringlicenceapplication_uuid.py similarity index 100% rename from mooringlicensing/migrations/0127_mooringlicenceapplication_uuid.py rename to mooringlicensing/migrations.original/0127_mooringlicenceapplication_uuid.py diff --git a/mooringlicensing/migrations/0128_mooringreportdocument_writtenproofdocument.py b/mooringlicensing/migrations.original/0128_mooringreportdocument_writtenproofdocument.py similarity index 100% rename from mooringlicensing/migrations/0128_mooringreportdocument_writtenproofdocument.py rename to mooringlicensing/migrations.original/0128_mooringreportdocument_writtenproofdocument.py diff --git a/mooringlicensing/migrations/0129_auto_20210531_1703.py b/mooringlicensing/migrations.original/0129_auto_20210531_1703.py similarity index 100% rename from mooringlicensing/migrations/0129_auto_20210531_1703.py rename to mooringlicensing/migrations.original/0129_auto_20210531_1703.py diff --git a/mooringlicensing/migrations/0130_approval_wla_queue_date.py b/mooringlicensing/migrations.original/0130_approval_wla_queue_date.py similarity index 100% rename from mooringlicensing/migrations/0130_approval_wla_queue_date.py rename to mooringlicensing/migrations.original/0130_approval_wla_queue_date.py diff --git a/mooringlicensing/migrations/0130_auto_20210602_1145.py b/mooringlicensing/migrations.original/0130_auto_20210602_1145.py similarity index 100% rename from mooringlicensing/migrations/0130_auto_20210602_1145.py rename to mooringlicensing/migrations.original/0130_auto_20210602_1145.py diff --git a/mooringlicensing/migrations/0131_approval_wla_order.py b/mooringlicensing/migrations.original/0131_approval_wla_order.py similarity index 100% rename from mooringlicensing/migrations/0131_approval_wla_order.py rename to mooringlicensing/migrations.original/0131_approval_wla_order.py diff --git a/mooringlicensing/migrations/0132_waitinglistofferdocument.py b/mooringlicensing/migrations.original/0132_waitinglistofferdocument.py similarity index 100% rename from mooringlicensing/migrations/0132_waitinglistofferdocument.py rename to mooringlicensing/migrations.original/0132_waitinglistofferdocument.py diff --git a/mooringlicensing/migrations/0133_auto_20210602_1429.py b/mooringlicensing/migrations.original/0133_auto_20210602_1429.py similarity index 100% rename from mooringlicensing/migrations/0133_auto_20210602_1429.py rename to mooringlicensing/migrations.original/0133_auto_20210602_1429.py diff --git a/mooringlicensing/migrations/0134_proposal_allocated_mooring.py b/mooringlicensing/migrations.original/0134_proposal_allocated_mooring.py similarity index 100% rename from mooringlicensing/migrations/0134_proposal_allocated_mooring.py rename to mooringlicensing/migrations.original/0134_proposal_allocated_mooring.py diff --git a/mooringlicensing/migrations/0135_proposal_waiting_list_allocation.py b/mooringlicensing/migrations.original/0135_proposal_waiting_list_allocation.py similarity index 100% rename from mooringlicensing/migrations/0135_proposal_waiting_list_allocation.py rename to mooringlicensing/migrations.original/0135_proposal_waiting_list_allocation.py diff --git a/mooringlicensing/migrations/0136_auto_20210603_1124.py b/mooringlicensing/migrations.original/0136_auto_20210603_1124.py similarity index 100% rename from mooringlicensing/migrations/0136_auto_20210603_1124.py rename to mooringlicensing/migrations.original/0136_auto_20210603_1124.py diff --git a/mooringlicensing/migrations/0136_merge_20210603_1113.py b/mooringlicensing/migrations.original/0136_merge_20210603_1113.py similarity index 100% rename from mooringlicensing/migrations/0136_merge_20210603_1113.py rename to mooringlicensing/migrations.original/0136_merge_20210603_1113.py diff --git a/mooringlicensing/migrations/0137_auto_20210603_1647.py b/mooringlicensing/migrations.original/0137_auto_20210603_1647.py similarity index 100% rename from mooringlicensing/migrations/0137_auto_20210603_1647.py rename to mooringlicensing/migrations.original/0137_auto_20210603_1647.py diff --git a/mooringlicensing/migrations/0137_auto_20210604_1006.py b/mooringlicensing/migrations.original/0137_auto_20210604_1006.py similarity index 100% rename from mooringlicensing/migrations/0137_auto_20210604_1006.py rename to mooringlicensing/migrations.original/0137_auto_20210604_1006.py diff --git a/mooringlicensing/migrations/0138_merge_20210604_1104.py b/mooringlicensing/migrations.original/0138_merge_20210604_1104.py similarity index 100% rename from mooringlicensing/migrations/0138_merge_20210604_1104.py rename to mooringlicensing/migrations.original/0138_merge_20210604_1104.py diff --git a/mooringlicensing/migrations/0138_mooring_mooring_licence.py b/mooringlicensing/migrations.original/0138_mooring_mooring_licence.py similarity index 100% rename from mooringlicensing/migrations/0138_mooring_mooring_licence.py rename to mooringlicensing/migrations.original/0138_mooring_mooring_licence.py diff --git a/mooringlicensing/migrations/0139_auto_20210604_1105.py b/mooringlicensing/migrations.original/0139_auto_20210604_1105.py similarity index 100% rename from mooringlicensing/migrations/0139_auto_20210604_1105.py rename to mooringlicensing/migrations.original/0139_auto_20210604_1105.py diff --git a/mooringlicensing/migrations/0140_auto_20210604_1338.py b/mooringlicensing/migrations.original/0140_auto_20210604_1338.py similarity index 100% rename from mooringlicensing/migrations/0140_auto_20210604_1338.py rename to mooringlicensing/migrations.original/0140_auto_20210604_1338.py diff --git a/mooringlicensing/migrations/0141_auto_20210604_1435.py b/mooringlicensing/migrations.original/0141_auto_20210604_1435.py similarity index 100% rename from mooringlicensing/migrations/0141_auto_20210604_1435.py rename to mooringlicensing/migrations.original/0141_auto_20210604_1435.py diff --git a/mooringlicensing/migrations/0142_auto_20210604_1516.py b/mooringlicensing/migrations.original/0142_auto_20210604_1516.py similarity index 100% rename from mooringlicensing/migrations/0142_auto_20210604_1516.py rename to mooringlicensing/migrations.original/0142_auto_20210604_1516.py diff --git a/mooringlicensing/migrations/0143_auto_20210604_1602.py b/mooringlicensing/migrations.original/0143_auto_20210604_1602.py similarity index 100% rename from mooringlicensing/migrations/0143_auto_20210604_1602.py rename to mooringlicensing/migrations.original/0143_auto_20210604_1602.py diff --git a/mooringlicensing/migrations/0143_merge_20210608_1038.py b/mooringlicensing/migrations.original/0143_merge_20210608_1038.py similarity index 100% rename from mooringlicensing/migrations/0143_merge_20210608_1038.py rename to mooringlicensing/migrations.original/0143_merge_20210608_1038.py diff --git a/mooringlicensing/migrations/0144_auto_20210604_1622.py b/mooringlicensing/migrations.original/0144_auto_20210604_1622.py similarity index 100% rename from mooringlicensing/migrations/0144_auto_20210604_1622.py rename to mooringlicensing/migrations.original/0144_auto_20210604_1622.py diff --git a/mooringlicensing/migrations/0145_merge_20210608_1209.py b/mooringlicensing/migrations.original/0145_merge_20210608_1209.py similarity index 100% rename from mooringlicensing/migrations/0145_merge_20210608_1209.py rename to mooringlicensing/migrations.original/0145_merge_20210608_1209.py diff --git a/mooringlicensing/migrations/0146_auto_20210608_1330.py b/mooringlicensing/migrations.original/0146_auto_20210608_1330.py similarity index 100% rename from mooringlicensing/migrations/0146_auto_20210608_1330.py rename to mooringlicensing/migrations.original/0146_auto_20210608_1330.py diff --git a/mooringlicensing/migrations/0147_mooringuseraction.py b/mooringlicensing/migrations.original/0147_mooringuseraction.py similarity index 100% rename from mooringlicensing/migrations/0147_mooringuseraction.py rename to mooringlicensing/migrations.original/0147_mooringuseraction.py diff --git a/mooringlicensing/migrations/0148_auto_20210609_1516.py b/mooringlicensing/migrations.original/0148_auto_20210609_1516.py similarity index 100% rename from mooringlicensing/migrations/0148_auto_20210609_1516.py rename to mooringlicensing/migrations.original/0148_auto_20210609_1516.py diff --git a/mooringlicensing/migrations/0149_auto_20210610_1734.py b/mooringlicensing/migrations.original/0149_auto_20210610_1734.py similarity index 100% rename from mooringlicensing/migrations/0149_auto_20210610_1734.py rename to mooringlicensing/migrations.original/0149_auto_20210610_1734.py diff --git a/mooringlicensing/migrations/0150_auto_20210611_1150.py b/mooringlicensing/migrations.original/0150_auto_20210611_1150.py similarity index 100% rename from mooringlicensing/migrations/0150_auto_20210611_1150.py rename to mooringlicensing/migrations.original/0150_auto_20210611_1150.py diff --git a/mooringlicensing/migrations/0151_auto_20210614_0856.py b/mooringlicensing/migrations.original/0151_auto_20210614_0856.py similarity index 100% rename from mooringlicensing/migrations/0151_auto_20210614_0856.py rename to mooringlicensing/migrations.original/0151_auto_20210614_0856.py diff --git a/mooringlicensing/migrations/0152_stickeractiondetail.py b/mooringlicensing/migrations.original/0152_stickeractiondetail.py similarity index 100% rename from mooringlicensing/migrations/0152_stickeractiondetail.py rename to mooringlicensing/migrations.original/0152_stickeractiondetail.py diff --git a/mooringlicensing/migrations/0153_auto_20210614_1435.py b/mooringlicensing/migrations.original/0153_auto_20210614_1435.py similarity index 100% rename from mooringlicensing/migrations/0153_auto_20210614_1435.py rename to mooringlicensing/migrations.original/0153_auto_20210614_1435.py diff --git a/mooringlicensing/migrations/0154_auto_20210614_1557.py b/mooringlicensing/migrations.original/0154_auto_20210614_1557.py similarity index 100% rename from mooringlicensing/migrations/0154_auto_20210614_1557.py rename to mooringlicensing/migrations.original/0154_auto_20210614_1557.py diff --git a/mooringlicensing/migrations/0155_auto_20210615_1415.py b/mooringlicensing/migrations.original/0155_auto_20210615_1415.py similarity index 100% rename from mooringlicensing/migrations/0155_auto_20210615_1415.py rename to mooringlicensing/migrations.original/0155_auto_20210615_1415.py diff --git a/mooringlicensing/migrations/0156_mooringonapproval_sticker.py b/mooringlicensing/migrations.original/0156_mooringonapproval_sticker.py similarity index 100% rename from mooringlicensing/migrations/0156_mooringonapproval_sticker.py rename to mooringlicensing/migrations.original/0156_mooringonapproval_sticker.py diff --git a/mooringlicensing/migrations/0156_proposal_dot_name.py b/mooringlicensing/migrations.original/0156_proposal_dot_name.py similarity index 100% rename from mooringlicensing/migrations/0156_proposal_dot_name.py rename to mooringlicensing/migrations.original/0156_proposal_dot_name.py diff --git a/mooringlicensing/migrations/0157_merge_20210617_0859.py b/mooringlicensing/migrations.original/0157_merge_20210617_0859.py similarity index 100% rename from mooringlicensing/migrations/0157_merge_20210617_0859.py rename to mooringlicensing/migrations.original/0157_merge_20210617_0859.py diff --git a/mooringlicensing/migrations/0157_merge_20210617_1407.py b/mooringlicensing/migrations.original/0157_merge_20210617_1407.py similarity index 100% rename from mooringlicensing/migrations/0157_merge_20210617_1407.py rename to mooringlicensing/migrations.original/0157_merge_20210617_1407.py diff --git a/mooringlicensing/migrations/0158_auto_20210617_0920.py b/mooringlicensing/migrations.original/0158_auto_20210617_0920.py similarity index 100% rename from mooringlicensing/migrations/0158_auto_20210617_0920.py rename to mooringlicensing/migrations.original/0158_auto_20210617_0920.py diff --git a/mooringlicensing/migrations/0158_auto_20210617_1413.py b/mooringlicensing/migrations.original/0158_auto_20210617_1413.py similarity index 100% rename from mooringlicensing/migrations/0158_auto_20210617_1413.py rename to mooringlicensing/migrations.original/0158_auto_20210617_1413.py diff --git a/mooringlicensing/migrations/0159_merge_20210617_1552.py b/mooringlicensing/migrations.original/0159_merge_20210617_1552.py similarity index 100% rename from mooringlicensing/migrations/0159_merge_20210617_1552.py rename to mooringlicensing/migrations.original/0159_merge_20210617_1552.py diff --git a/mooringlicensing/migrations/0160_auto_20210618_1609.py b/mooringlicensing/migrations.original/0160_auto_20210618_1609.py similarity index 100% rename from mooringlicensing/migrations/0160_auto_20210618_1609.py rename to mooringlicensing/migrations.original/0160_auto_20210618_1609.py diff --git a/mooringlicensing/migrations/0160_auto_20210621_1115.py b/mooringlicensing/migrations.original/0160_auto_20210621_1115.py similarity index 100% rename from mooringlicensing/migrations/0160_auto_20210621_1115.py rename to mooringlicensing/migrations.original/0160_auto_20210621_1115.py diff --git a/mooringlicensing/migrations/0161_auto_20210621_1000.py b/mooringlicensing/migrations.original/0161_auto_20210621_1000.py similarity index 100% rename from mooringlicensing/migrations/0161_auto_20210621_1000.py rename to mooringlicensing/migrations.original/0161_auto_20210621_1000.py diff --git a/mooringlicensing/migrations/0161_vesselonapproval_dot_name.py b/mooringlicensing/migrations.original/0161_vesselonapproval_dot_name.py similarity index 100% rename from mooringlicensing/migrations/0161_vesselonapproval_dot_name.py rename to mooringlicensing/migrations.original/0161_vesselonapproval_dot_name.py diff --git a/mooringlicensing/migrations/0162_numberofdaystype_code.py b/mooringlicensing/migrations.original/0162_numberofdaystype_code.py similarity index 100% rename from mooringlicensing/migrations/0162_numberofdaystype_code.py rename to mooringlicensing/migrations.original/0162_numberofdaystype_code.py diff --git a/mooringlicensing/migrations/0163_remove_authoriseduserpermit_endorsed_by.py b/mooringlicensing/migrations.original/0163_remove_authoriseduserpermit_endorsed_by.py similarity index 100% rename from mooringlicensing/migrations/0163_remove_authoriseduserpermit_endorsed_by.py rename to mooringlicensing/migrations.original/0163_remove_authoriseduserpermit_endorsed_by.py diff --git a/mooringlicensing/migrations/0164_proposal_endorser_reminder_sent.py b/mooringlicensing/migrations.original/0164_proposal_endorser_reminder_sent.py similarity index 100% rename from mooringlicensing/migrations/0164_proposal_endorser_reminder_sent.py rename to mooringlicensing/migrations.original/0164_proposal_endorser_reminder_sent.py diff --git a/mooringlicensing/migrations/0165_merge_20210622_1028.py b/mooringlicensing/migrations.original/0165_merge_20210622_1028.py similarity index 100% rename from mooringlicensing/migrations/0165_merge_20210622_1028.py rename to mooringlicensing/migrations.original/0165_merge_20210622_1028.py diff --git a/mooringlicensing/migrations/0166_auto_20210622_1043.py b/mooringlicensing/migrations.original/0166_auto_20210622_1043.py similarity index 100% rename from mooringlicensing/migrations/0166_auto_20210622_1043.py rename to mooringlicensing/migrations.original/0166_auto_20210622_1043.py diff --git a/mooringlicensing/migrations/0167_auto_20210622_1109.py b/mooringlicensing/migrations.original/0167_auto_20210622_1109.py similarity index 100% rename from mooringlicensing/migrations/0167_auto_20210622_1109.py rename to mooringlicensing/migrations.original/0167_auto_20210622_1109.py diff --git a/mooringlicensing/migrations/0168_auto_20210622_1109.py b/mooringlicensing/migrations.original/0168_auto_20210622_1109.py similarity index 100% rename from mooringlicensing/migrations/0168_auto_20210622_1109.py rename to mooringlicensing/migrations.original/0168_auto_20210622_1109.py diff --git a/mooringlicensing/migrations/0169_proposal_date_invited.py b/mooringlicensing/migrations.original/0169_proposal_date_invited.py similarity index 100% rename from mooringlicensing/migrations/0169_proposal_date_invited.py rename to mooringlicensing/migrations.original/0169_proposal_date_invited.py diff --git a/mooringlicensing/migrations/0170_merge_20210622_1439.py b/mooringlicensing/migrations.original/0170_merge_20210622_1439.py similarity index 100% rename from mooringlicensing/migrations/0170_merge_20210622_1439.py rename to mooringlicensing/migrations.original/0170_merge_20210622_1439.py diff --git a/mooringlicensing/migrations/0170_proposal_invitee_reminder_sent.py b/mooringlicensing/migrations.original/0170_proposal_invitee_reminder_sent.py similarity index 100% rename from mooringlicensing/migrations/0170_proposal_invitee_reminder_sent.py rename to mooringlicensing/migrations.original/0170_proposal_invitee_reminder_sent.py diff --git a/mooringlicensing/migrations/0171_auto_20210622_1439.py b/mooringlicensing/migrations.original/0171_auto_20210622_1439.py similarity index 100% rename from mooringlicensing/migrations/0171_auto_20210622_1439.py rename to mooringlicensing/migrations.original/0171_auto_20210622_1439.py diff --git a/mooringlicensing/migrations/0172_proposal_previous_application.py b/mooringlicensing/migrations.original/0172_proposal_previous_application.py similarity index 100% rename from mooringlicensing/migrations/0172_proposal_previous_application.py rename to mooringlicensing/migrations.original/0172_proposal_previous_application.py diff --git a/mooringlicensing/migrations/0173_auto_20210622_1501.py b/mooringlicensing/migrations.original/0173_auto_20210622_1501.py similarity index 100% rename from mooringlicensing/migrations/0173_auto_20210622_1501.py rename to mooringlicensing/migrations.original/0173_auto_20210622_1501.py diff --git a/mooringlicensing/migrations/0174_auto_20210622_1503.py b/mooringlicensing/migrations.original/0174_auto_20210622_1503.py similarity index 100% rename from mooringlicensing/migrations/0174_auto_20210622_1503.py rename to mooringlicensing/migrations.original/0174_auto_20210622_1503.py diff --git a/mooringlicensing/migrations/0175_merge_20210622_1557.py b/mooringlicensing/migrations.original/0175_merge_20210622_1557.py similarity index 100% rename from mooringlicensing/migrations/0175_merge_20210622_1557.py rename to mooringlicensing/migrations.original/0175_merge_20210622_1557.py diff --git a/mooringlicensing/migrations/0176_auto_20210622_1634.py b/mooringlicensing/migrations.original/0176_auto_20210622_1634.py similarity index 100% rename from mooringlicensing/migrations/0176_auto_20210622_1634.py rename to mooringlicensing/migrations.original/0176_auto_20210622_1634.py diff --git a/mooringlicensing/migrations/0177_auto_20210623_1115.py b/mooringlicensing/migrations.original/0177_auto_20210623_1115.py similarity index 100% rename from mooringlicensing/migrations/0177_auto_20210623_1115.py rename to mooringlicensing/migrations.original/0177_auto_20210623_1115.py diff --git a/mooringlicensing/migrations/0178_auto_20210624_1025.py b/mooringlicensing/migrations.original/0178_auto_20210624_1025.py similarity index 100% rename from mooringlicensing/migrations/0178_auto_20210624_1025.py rename to mooringlicensing/migrations.original/0178_auto_20210624_1025.py diff --git a/mooringlicensing/migrations/0179_auto_20210624_1610.py b/mooringlicensing/migrations.original/0179_auto_20210624_1610.py similarity index 100% rename from mooringlicensing/migrations/0179_auto_20210624_1610.py rename to mooringlicensing/migrations.original/0179_auto_20210624_1610.py diff --git a/mooringlicensing/migrations/0180_remove_stickerprintingcontact_global_setting.py b/mooringlicensing/migrations.original/0180_remove_stickerprintingcontact_global_setting.py similarity index 100% rename from mooringlicensing/migrations/0180_remove_stickerprintingcontact_global_setting.py rename to mooringlicensing/migrations.original/0180_remove_stickerprintingcontact_global_setting.py diff --git a/mooringlicensing/migrations/0181_auto_20210624_1626.py b/mooringlicensing/migrations.original/0181_auto_20210624_1626.py similarity index 100% rename from mooringlicensing/migrations/0181_auto_20210624_1626.py rename to mooringlicensing/migrations.original/0181_auto_20210624_1626.py diff --git a/mooringlicensing/migrations/0182_sticker_vessel_details.py b/mooringlicensing/migrations.original/0182_sticker_vessel_details.py similarity index 100% rename from mooringlicensing/migrations/0182_sticker_vessel_details.py rename to mooringlicensing/migrations.original/0182_sticker_vessel_details.py diff --git a/mooringlicensing/migrations/0183_auto_20210629_1156.py b/mooringlicensing/migrations.original/0183_auto_20210629_1156.py similarity index 100% rename from mooringlicensing/migrations/0183_auto_20210629_1156.py rename to mooringlicensing/migrations.original/0183_auto_20210629_1156.py diff --git a/mooringlicensing/migrations/0184_auto_20210630_1422.py b/mooringlicensing/migrations.original/0184_auto_20210630_1422.py similarity index 100% rename from mooringlicensing/migrations/0184_auto_20210630_1422.py rename to mooringlicensing/migrations.original/0184_auto_20210630_1422.py diff --git a/mooringlicensing/migrations/0185_auto_20210702_1009.py b/mooringlicensing/migrations.original/0185_auto_20210702_1009.py similarity index 100% rename from mooringlicensing/migrations/0185_auto_20210702_1009.py rename to mooringlicensing/migrations.original/0185_auto_20210702_1009.py diff --git a/mooringlicensing/migrations/0186_remove_sticker_vessel_details.py b/mooringlicensing/migrations.original/0186_remove_sticker_vessel_details.py similarity index 100% rename from mooringlicensing/migrations/0186_remove_sticker_vessel_details.py rename to mooringlicensing/migrations.original/0186_remove_sticker_vessel_details.py diff --git a/mooringlicensing/migrations/0187_auto_20210705_1252.py b/mooringlicensing/migrations.original/0187_auto_20210705_1252.py similarity index 100% rename from mooringlicensing/migrations/0187_auto_20210705_1252.py rename to mooringlicensing/migrations.original/0187_auto_20210705_1252.py diff --git a/mooringlicensing/migrations/0187_auto_20210708_1355.py b/mooringlicensing/migrations.original/0187_auto_20210708_1355.py similarity index 100% rename from mooringlicensing/migrations/0187_auto_20210708_1355.py rename to mooringlicensing/migrations.original/0187_auto_20210708_1355.py diff --git a/mooringlicensing/migrations/0188_merge_20210709_0828.py b/mooringlicensing/migrations.original/0188_merge_20210709_0828.py similarity index 100% rename from mooringlicensing/migrations/0188_merge_20210709_0828.py rename to mooringlicensing/migrations.original/0188_merge_20210709_0828.py diff --git a/mooringlicensing/migrations/0189_auto_20210709_1520.py b/mooringlicensing/migrations.original/0189_auto_20210709_1520.py similarity index 100% rename from mooringlicensing/migrations/0189_auto_20210709_1520.py rename to mooringlicensing/migrations.original/0189_auto_20210709_1520.py diff --git a/mooringlicensing/migrations/0190_auto_20210709_1522.py b/mooringlicensing/migrations.original/0190_auto_20210709_1522.py similarity index 100% rename from mooringlicensing/migrations/0190_auto_20210709_1522.py rename to mooringlicensing/migrations.original/0190_auto_20210709_1522.py diff --git a/mooringlicensing/migrations/0191_vesselsizecategory_null_vessel.py b/mooringlicensing/migrations.original/0191_vesselsizecategory_null_vessel.py similarity index 100% rename from mooringlicensing/migrations/0191_vesselsizecategory_null_vessel.py rename to mooringlicensing/migrations.original/0191_vesselsizecategory_null_vessel.py diff --git a/mooringlicensing/migrations/0192_auto_20210715_1517.py b/mooringlicensing/migrations.original/0192_auto_20210715_1517.py similarity index 100% rename from mooringlicensing/migrations/0192_auto_20210715_1517.py rename to mooringlicensing/migrations.original/0192_auto_20210715_1517.py diff --git a/mooringlicensing/migrations/0193_auto_20210716_1609.py b/mooringlicensing/migrations.original/0193_auto_20210716_1609.py similarity index 100% rename from mooringlicensing/migrations/0193_auto_20210716_1609.py rename to mooringlicensing/migrations.original/0193_auto_20210716_1609.py diff --git a/mooringlicensing/migrations/0193_auto_20210719_0906.py b/mooringlicensing/migrations.original/0193_auto_20210719_0906.py similarity index 100% rename from mooringlicensing/migrations/0193_auto_20210719_0906.py rename to mooringlicensing/migrations.original/0193_auto_20210719_0906.py diff --git a/mooringlicensing/migrations/0194_merge_20210719_1028.py b/mooringlicensing/migrations.original/0194_merge_20210719_1028.py similarity index 100% rename from mooringlicensing/migrations/0194_merge_20210719_1028.py rename to mooringlicensing/migrations.original/0194_merge_20210719_1028.py diff --git a/mooringlicensing/migrations/0195_auto_20210720_1403.py b/mooringlicensing/migrations.original/0195_auto_20210720_1403.py similarity index 100% rename from mooringlicensing/migrations/0195_auto_20210720_1403.py rename to mooringlicensing/migrations.original/0195_auto_20210720_1403.py diff --git a/mooringlicensing/migrations/0196_auto_20210720_1437.py b/mooringlicensing/migrations.original/0196_auto_20210720_1437.py similarity index 100% rename from mooringlicensing/migrations/0196_auto_20210720_1437.py rename to mooringlicensing/migrations.original/0196_auto_20210720_1437.py diff --git a/mooringlicensing/migrations/0197_auto_20210722_1048.py b/mooringlicensing/migrations.original/0197_auto_20210722_1048.py similarity index 100% rename from mooringlicensing/migrations/0197_auto_20210722_1048.py rename to mooringlicensing/migrations.original/0197_auto_20210722_1048.py diff --git a/mooringlicensing/migrations/0198_auto_20210730_1555.py b/mooringlicensing/migrations.original/0198_auto_20210730_1555.py similarity index 100% rename from mooringlicensing/migrations/0198_auto_20210730_1555.py rename to mooringlicensing/migrations.original/0198_auto_20210730_1555.py diff --git a/mooringlicensing/migrations/0199_auto_20210802_1027.py b/mooringlicensing/migrations.original/0199_auto_20210802_1027.py similarity index 100% rename from mooringlicensing/migrations/0199_auto_20210802_1027.py rename to mooringlicensing/migrations.original/0199_auto_20210802_1027.py diff --git a/mooringlicensing/migrations/0200_auto_20210802_1539.py b/mooringlicensing/migrations.original/0200_auto_20210802_1539.py similarity index 100% rename from mooringlicensing/migrations/0200_auto_20210802_1539.py rename to mooringlicensing/migrations.original/0200_auto_20210802_1539.py diff --git a/mooringlicensing/migrations/0201_auto_20210805_1019.py b/mooringlicensing/migrations.original/0201_auto_20210805_1019.py similarity index 100% rename from mooringlicensing/migrations/0201_auto_20210805_1019.py rename to mooringlicensing/migrations.original/0201_auto_20210805_1019.py diff --git a/mooringlicensing/migrations/0201_stickerprintingresponse_imported_datetime.py b/mooringlicensing/migrations.original/0201_stickerprintingresponse_imported_datetime.py similarity index 100% rename from mooringlicensing/migrations/0201_stickerprintingresponse_imported_datetime.py rename to mooringlicensing/migrations.original/0201_stickerprintingresponse_imported_datetime.py diff --git a/mooringlicensing/migrations/0202_auto_20210805_1023.py b/mooringlicensing/migrations.original/0202_auto_20210805_1023.py similarity index 100% rename from mooringlicensing/migrations/0202_auto_20210805_1023.py rename to mooringlicensing/migrations.original/0202_auto_20210805_1023.py diff --git a/mooringlicensing/migrations/0202_auto_20210805_1149.py b/mooringlicensing/migrations.original/0202_auto_20210805_1149.py similarity index 100% rename from mooringlicensing/migrations/0202_auto_20210805_1149.py rename to mooringlicensing/migrations.original/0202_auto_20210805_1149.py diff --git a/mooringlicensing/migrations/0203_stickerprintingresponse_email_date.py b/mooringlicensing/migrations.original/0203_stickerprintingresponse_email_date.py similarity index 100% rename from mooringlicensing/migrations/0203_stickerprintingresponse_email_date.py rename to mooringlicensing/migrations.original/0203_stickerprintingresponse_email_date.py diff --git a/mooringlicensing/migrations/0204_stickerprintingresponse_email_from.py b/mooringlicensing/migrations.original/0204_stickerprintingresponse_email_from.py similarity index 100% rename from mooringlicensing/migrations/0204_stickerprintingresponse_email_from.py rename to mooringlicensing/migrations.original/0204_stickerprintingresponse_email_from.py diff --git a/mooringlicensing/migrations/0205_stickerprintingresponse_processed.py b/mooringlicensing/migrations.original/0205_stickerprintingresponse_processed.py similarity index 100% rename from mooringlicensing/migrations/0205_stickerprintingresponse_processed.py rename to mooringlicensing/migrations.original/0205_stickerprintingresponse_processed.py diff --git a/mooringlicensing/migrations/0206_stickerprintingresponse_email_message_id.py b/mooringlicensing/migrations.original/0206_stickerprintingresponse_email_message_id.py similarity index 100% rename from mooringlicensing/migrations/0206_stickerprintingresponse_email_message_id.py rename to mooringlicensing/migrations.original/0206_stickerprintingresponse_email_message_id.py diff --git a/mooringlicensing/migrations/0207_merge_20210805_1732.py b/mooringlicensing/migrations.original/0207_merge_20210805_1732.py similarity index 100% rename from mooringlicensing/migrations/0207_merge_20210805_1732.py rename to mooringlicensing/migrations.original/0207_merge_20210805_1732.py diff --git a/mooringlicensing/migrations/0208_remove_dcvvessel_uvi_vessel_identifier.py b/mooringlicensing/migrations.original/0208_remove_dcvvessel_uvi_vessel_identifier.py similarity index 100% rename from mooringlicensing/migrations/0208_remove_dcvvessel_uvi_vessel_identifier.py rename to mooringlicensing/migrations.original/0208_remove_dcvvessel_uvi_vessel_identifier.py diff --git a/mooringlicensing/migrations/0209_proofofidentitydocument_signedlicenceagreementdocument.py b/mooringlicensing/migrations.original/0209_proofofidentitydocument_signedlicenceagreementdocument.py similarity index 100% rename from mooringlicensing/migrations/0209_proofofidentitydocument_signedlicenceagreementdocument.py rename to mooringlicensing/migrations.original/0209_proofofidentitydocument_signedlicenceagreementdocument.py diff --git a/mooringlicensing/migrations/0209_vesselownership_mooring_licence_expiry_date.py b/mooringlicensing/migrations.original/0209_vesselownership_mooring_licence_expiry_date.py similarity index 100% rename from mooringlicensing/migrations/0209_vesselownership_mooring_licence_expiry_date.py rename to mooringlicensing/migrations.original/0209_vesselownership_mooring_licence_expiry_date.py diff --git a/mooringlicensing/migrations/0210_mooringonapproval_end_date.py b/mooringlicensing/migrations.original/0210_mooringonapproval_end_date.py similarity index 100% rename from mooringlicensing/migrations/0210_mooringonapproval_end_date.py rename to mooringlicensing/migrations.original/0210_mooringonapproval_end_date.py diff --git a/mooringlicensing/migrations/0210_stickerprintingresponseemail.py b/mooringlicensing/migrations.original/0210_stickerprintingresponseemail.py similarity index 100% rename from mooringlicensing/migrations/0210_stickerprintingresponseemail.py rename to mooringlicensing/migrations.original/0210_stickerprintingresponseemail.py diff --git a/mooringlicensing/migrations/0211_auto_20210818_1640.py b/mooringlicensing/migrations.original/0211_auto_20210818_1640.py similarity index 100% rename from mooringlicensing/migrations/0211_auto_20210818_1640.py rename to mooringlicensing/migrations.original/0211_auto_20210818_1640.py diff --git a/mooringlicensing/migrations/0211_stickerprintingresponse_sticker_printing_response_email.py b/mooringlicensing/migrations.original/0211_stickerprintingresponse_sticker_printing_response_email.py similarity index 100% rename from mooringlicensing/migrations/0211_stickerprintingresponse_sticker_printing_response_email.py rename to mooringlicensing/migrations.original/0211_stickerprintingresponse_sticker_printing_response_email.py diff --git a/mooringlicensing/migrations/0212_auto_20210817_1221.py b/mooringlicensing/migrations.original/0212_auto_20210817_1221.py similarity index 100% rename from mooringlicensing/migrations/0212_auto_20210817_1221.py rename to mooringlicensing/migrations.original/0212_auto_20210817_1221.py diff --git a/mooringlicensing/migrations/0213_stickerprintingresponse_no_errors_when_process.py b/mooringlicensing/migrations.original/0213_stickerprintingresponse_no_errors_when_process.py similarity index 100% rename from mooringlicensing/migrations/0213_stickerprintingresponse_no_errors_when_process.py rename to mooringlicensing/migrations.original/0213_stickerprintingresponse_no_errors_when_process.py diff --git a/mooringlicensing/migrations/0214_dcvadmissionarrival_departure_date.py b/mooringlicensing/migrations.original/0214_dcvadmissionarrival_departure_date.py similarity index 100% rename from mooringlicensing/migrations/0214_dcvadmissionarrival_departure_date.py rename to mooringlicensing/migrations.original/0214_dcvadmissionarrival_departure_date.py diff --git a/mooringlicensing/migrations/0215_merge_20210818_1524.py b/mooringlicensing/migrations.original/0215_merge_20210818_1524.py similarity index 100% rename from mooringlicensing/migrations/0215_merge_20210818_1524.py rename to mooringlicensing/migrations.original/0215_merge_20210818_1524.py diff --git a/mooringlicensing/migrations/0216_merge_20210818_1800.py b/mooringlicensing/migrations.original/0216_merge_20210818_1800.py similarity index 100% rename from mooringlicensing/migrations/0216_merge_20210818_1800.py rename to mooringlicensing/migrations.original/0216_merge_20210818_1800.py diff --git a/mooringlicensing/migrations/0216_vesselsizecategory_incremental.py b/mooringlicensing/migrations.original/0216_vesselsizecategory_incremental.py similarity index 100% rename from mooringlicensing/migrations/0216_vesselsizecategory_incremental.py rename to mooringlicensing/migrations.original/0216_vesselsizecategory_incremental.py diff --git a/mooringlicensing/migrations/0217_feeitem_absolute_amount.py b/mooringlicensing/migrations.original/0217_feeitem_absolute_amount.py similarity index 100% rename from mooringlicensing/migrations/0217_feeitem_absolute_amount.py rename to mooringlicensing/migrations.original/0217_feeitem_absolute_amount.py diff --git a/mooringlicensing/migrations/0218_auto_20210819_1045.py b/mooringlicensing/migrations.original/0218_auto_20210819_1045.py similarity index 100% rename from mooringlicensing/migrations/0218_auto_20210819_1045.py rename to mooringlicensing/migrations.original/0218_auto_20210819_1045.py diff --git a/mooringlicensing/migrations/0219_remove_feeitem_incremental_amount.py b/mooringlicensing/migrations.original/0219_remove_feeitem_incremental_amount.py similarity index 100% rename from mooringlicensing/migrations/0219_remove_feeitem_incremental_amount.py rename to mooringlicensing/migrations.original/0219_remove_feeitem_incremental_amount.py diff --git a/mooringlicensing/migrations/0220_auto_20210819_1109.py b/mooringlicensing/migrations.original/0220_auto_20210819_1109.py similarity index 100% rename from mooringlicensing/migrations/0220_auto_20210819_1109.py rename to mooringlicensing/migrations.original/0220_auto_20210819_1109.py diff --git a/mooringlicensing/migrations/0221_merge_20210819_1632.py b/mooringlicensing/migrations.original/0221_merge_20210819_1632.py similarity index 100% rename from mooringlicensing/migrations/0221_merge_20210819_1632.py rename to mooringlicensing/migrations.original/0221_merge_20210819_1632.py diff --git a/mooringlicensing/migrations/0222_auto_20210820_1209.py b/mooringlicensing/migrations.original/0222_auto_20210820_1209.py similarity index 100% rename from mooringlicensing/migrations/0222_auto_20210820_1209.py rename to mooringlicensing/migrations.original/0222_auto_20210820_1209.py diff --git a/mooringlicensing/migrations/0222_auto_20210824_1139.py b/mooringlicensing/migrations.original/0222_auto_20210824_1139.py similarity index 100% rename from mooringlicensing/migrations/0222_auto_20210824_1139.py rename to mooringlicensing/migrations.original/0222_auto_20210824_1139.py diff --git a/mooringlicensing/migrations/0223_oraclecode.py b/mooringlicensing/migrations.original/0223_oraclecode.py similarity index 100% rename from mooringlicensing/migrations/0223_oraclecode.py rename to mooringlicensing/migrations.original/0223_oraclecode.py diff --git a/mooringlicensing/migrations/0224_auto_20210823_1405.py b/mooringlicensing/migrations.original/0224_auto_20210823_1405.py similarity index 100% rename from mooringlicensing/migrations/0224_auto_20210823_1405.py rename to mooringlicensing/migrations.original/0224_auto_20210823_1405.py diff --git a/mooringlicensing/migrations/0225_auto_20210823_1445.py b/mooringlicensing/migrations.original/0225_auto_20210823_1445.py similarity index 100% rename from mooringlicensing/migrations/0225_auto_20210823_1445.py rename to mooringlicensing/migrations.original/0225_auto_20210823_1445.py diff --git a/mooringlicensing/migrations/0226_oraclecodeitem_application_type.py b/mooringlicensing/migrations.original/0226_oraclecodeitem_application_type.py similarity index 100% rename from mooringlicensing/migrations/0226_oraclecodeitem_application_type.py rename to mooringlicensing/migrations.original/0226_oraclecodeitem_application_type.py diff --git a/mooringlicensing/migrations/0227_auto_20210823_1632.py b/mooringlicensing/migrations.original/0227_auto_20210823_1632.py similarity index 100% rename from mooringlicensing/migrations/0227_auto_20210823_1632.py rename to mooringlicensing/migrations.original/0227_auto_20210823_1632.py diff --git a/mooringlicensing/migrations/0228_stickeractionfee.py b/mooringlicensing/migrations.original/0228_stickeractionfee.py similarity index 100% rename from mooringlicensing/migrations/0228_stickeractionfee.py rename to mooringlicensing/migrations.original/0228_stickeractionfee.py diff --git a/mooringlicensing/migrations/0229_auto_20210824_1604.py b/mooringlicensing/migrations.original/0229_auto_20210824_1604.py similarity index 100% rename from mooringlicensing/migrations/0229_auto_20210824_1604.py rename to mooringlicensing/migrations.original/0229_auto_20210824_1604.py diff --git a/mooringlicensing/migrations/0230_merge_20210824_1618.py b/mooringlicensing/migrations.original/0230_merge_20210824_1618.py similarity index 100% rename from mooringlicensing/migrations/0230_merge_20210824_1618.py rename to mooringlicensing/migrations.original/0230_merge_20210824_1618.py diff --git a/mooringlicensing/migrations/0231_auto_20210825_1144.py b/mooringlicensing/migrations.original/0231_auto_20210825_1144.py similarity index 100% rename from mooringlicensing/migrations/0231_auto_20210825_1144.py rename to mooringlicensing/migrations.original/0231_auto_20210825_1144.py diff --git a/mooringlicensing/migrations/0232_auto_20210825_1609.py b/mooringlicensing/migrations.original/0232_auto_20210825_1609.py similarity index 100% rename from mooringlicensing/migrations/0232_auto_20210825_1609.py rename to mooringlicensing/migrations.original/0232_auto_20210825_1609.py diff --git a/mooringlicensing/migrations/0232_auto_20210826_0843.py b/mooringlicensing/migrations.original/0232_auto_20210826_0843.py similarity index 100% rename from mooringlicensing/migrations/0232_auto_20210826_0843.py rename to mooringlicensing/migrations.original/0232_auto_20210826_0843.py diff --git a/mooringlicensing/migrations/0233_feeitemstickerreplacement_enabled.py b/mooringlicensing/migrations.original/0233_feeitemstickerreplacement_enabled.py similarity index 100% rename from mooringlicensing/migrations/0233_feeitemstickerreplacement_enabled.py rename to mooringlicensing/migrations.original/0233_feeitemstickerreplacement_enabled.py diff --git a/mooringlicensing/migrations/0234_auto_20210825_1653.py b/mooringlicensing/migrations.original/0234_auto_20210825_1653.py similarity index 100% rename from mooringlicensing/migrations/0234_auto_20210825_1653.py rename to mooringlicensing/migrations.original/0234_auto_20210825_1653.py diff --git a/mooringlicensing/migrations/0235_merge_20210826_1233.py b/mooringlicensing/migrations.original/0235_merge_20210826_1233.py similarity index 100% rename from mooringlicensing/migrations/0235_merge_20210826_1233.py rename to mooringlicensing/migrations.original/0235_merge_20210826_1233.py diff --git a/mooringlicensing/migrations/0236_auto_20210831_1623.py b/mooringlicensing/migrations.original/0236_auto_20210831_1623.py similarity index 100% rename from mooringlicensing/migrations/0236_auto_20210831_1623.py rename to mooringlicensing/migrations.original/0236_auto_20210831_1623.py diff --git a/mooringlicensing/migrations/0237_auto_20210902_0740.py b/mooringlicensing/migrations.original/0237_auto_20210902_0740.py similarity index 100% rename from mooringlicensing/migrations/0237_auto_20210902_0740.py rename to mooringlicensing/migrations.original/0237_auto_20210902_0740.py diff --git a/mooringlicensing/migrations/0238_auto_20210902_0945.py b/mooringlicensing/migrations.original/0238_auto_20210902_0945.py similarity index 100% rename from mooringlicensing/migrations/0238_auto_20210902_0945.py rename to mooringlicensing/migrations.original/0238_auto_20210902_0945.py diff --git a/mooringlicensing/migrations/0239_auto_20210903_1624.py b/mooringlicensing/migrations.original/0239_auto_20210903_1624.py similarity index 100% rename from mooringlicensing/migrations/0239_auto_20210903_1624.py rename to mooringlicensing/migrations.original/0239_auto_20210903_1624.py diff --git a/mooringlicensing/migrations/0240_feeseason_application_type.py b/mooringlicensing/migrations.original/0240_feeseason_application_type.py similarity index 100% rename from mooringlicensing/migrations/0240_feeseason_application_type.py rename to mooringlicensing/migrations.original/0240_feeseason_application_type.py diff --git a/mooringlicensing/migrations/0241_applicationtype_fee_by_fee_constructor.py b/mooringlicensing/migrations.original/0241_applicationtype_fee_by_fee_constructor.py similarity index 100% rename from mooringlicensing/migrations/0241_applicationtype_fee_by_fee_constructor.py rename to mooringlicensing/migrations.original/0241_applicationtype_fee_by_fee_constructor.py diff --git a/mooringlicensing/migrations/0242_auto_20210906_1109.py b/mooringlicensing/migrations.original/0242_auto_20210906_1109.py similarity index 100% rename from mooringlicensing/migrations/0242_auto_20210906_1109.py rename to mooringlicensing/migrations.original/0242_auto_20210906_1109.py diff --git a/mooringlicensing/migrations/0243_auto_20210906_1203.py b/mooringlicensing/migrations.original/0243_auto_20210906_1203.py similarity index 100% rename from mooringlicensing/migrations/0243_auto_20210906_1203.py rename to mooringlicensing/migrations.original/0243_auto_20210906_1203.py diff --git a/mooringlicensing/migrations/0244_auto_20210907_1119.py b/mooringlicensing/migrations.original/0244_auto_20210907_1119.py similarity index 100% rename from mooringlicensing/migrations/0244_auto_20210907_1119.py rename to mooringlicensing/migrations.original/0244_auto_20210907_1119.py diff --git a/mooringlicensing/migrations/0245_proposal_temporary_document_collection_id.py b/mooringlicensing/migrations.original/0245_proposal_temporary_document_collection_id.py similarity index 100% rename from mooringlicensing/migrations/0245_proposal_temporary_document_collection_id.py rename to mooringlicensing/migrations.original/0245_proposal_temporary_document_collection_id.py diff --git a/mooringlicensing/migrations/0246_auto_20210908_0950.py b/mooringlicensing/migrations.original/0246_auto_20210908_0950.py similarity index 100% rename from mooringlicensing/migrations/0246_auto_20210908_0950.py rename to mooringlicensing/migrations.original/0246_auto_20210908_0950.py diff --git a/mooringlicensing/migrations/0247_proposal_keep_existing_mooring.py b/mooringlicensing/migrations.original/0247_proposal_keep_existing_mooring.py similarity index 100% rename from mooringlicensing/migrations/0247_proposal_keep_existing_mooring.py rename to mooringlicensing/migrations.original/0247_proposal_keep_existing_mooring.py diff --git a/mooringlicensing/migrations/0248_auto_20210912_1243.py b/mooringlicensing/migrations.original/0248_auto_20210912_1243.py similarity index 100% rename from mooringlicensing/migrations/0248_auto_20210912_1243.py rename to mooringlicensing/migrations.original/0248_auto_20210912_1243.py diff --git a/mooringlicensing/migrations/0249_auto_20210913_0945.py b/mooringlicensing/migrations.original/0249_auto_20210913_0945.py similarity index 100% rename from mooringlicensing/migrations/0249_auto_20210913_0945.py rename to mooringlicensing/migrations.original/0249_auto_20210913_0945.py diff --git a/mooringlicensing/migrations/0249_auto_20210913_2311.py b/mooringlicensing/migrations.original/0249_auto_20210913_2311.py similarity index 100% rename from mooringlicensing/migrations/0249_auto_20210913_2311.py rename to mooringlicensing/migrations.original/0249_auto_20210913_2311.py diff --git a/mooringlicensing/migrations/0250_remove_vesseldetails_vessel_overall_length.py b/mooringlicensing/migrations.original/0250_remove_vesseldetails_vessel_overall_length.py similarity index 100% rename from mooringlicensing/migrations/0250_remove_vesseldetails_vessel_overall_length.py rename to mooringlicensing/migrations.original/0250_remove_vesseldetails_vessel_overall_length.py diff --git a/mooringlicensing/migrations/0251_merge_20210914_0613.py b/mooringlicensing/migrations.original/0251_merge_20210914_0613.py similarity index 100% rename from mooringlicensing/migrations/0251_merge_20210914_0613.py rename to mooringlicensing/migrations.original/0251_merge_20210914_0613.py diff --git a/mooringlicensing/migrations/0252_auto_20210914_1133.py b/mooringlicensing/migrations.original/0252_auto_20210914_1133.py similarity index 100% rename from mooringlicensing/migrations/0252_auto_20210914_1133.py rename to mooringlicensing/migrations.original/0252_auto_20210914_1133.py diff --git a/mooringlicensing/migrations/0252_dcvpermit_migrated.py b/mooringlicensing/migrations.original/0252_dcvpermit_migrated.py similarity index 100% rename from mooringlicensing/migrations/0252_dcvpermit_migrated.py rename to mooringlicensing/migrations.original/0252_dcvpermit_migrated.py diff --git a/mooringlicensing/migrations/0253_merge_20210914_1155.py b/mooringlicensing/migrations.original/0253_merge_20210914_1155.py similarity index 100% rename from mooringlicensing/migrations/0253_merge_20210914_1155.py rename to mooringlicensing/migrations.original/0253_merge_20210914_1155.py diff --git a/mooringlicensing/migrations/0254_remove_proposal_vessel_overall_length.py b/mooringlicensing/migrations.original/0254_remove_proposal_vessel_overall_length.py similarity index 100% rename from mooringlicensing/migrations/0254_remove_proposal_vessel_overall_length.py rename to mooringlicensing/migrations.original/0254_remove_proposal_vessel_overall_length.py diff --git a/mooringlicensing/migrations/0255_auto_20210914_1543.py b/mooringlicensing/migrations.original/0255_auto_20210914_1543.py similarity index 100% rename from mooringlicensing/migrations/0255_auto_20210914_1543.py rename to mooringlicensing/migrations.original/0255_auto_20210914_1543.py diff --git a/mooringlicensing/migrations/0256_approval_export_to_mooring_booking.py b/mooringlicensing/migrations.original/0256_approval_export_to_mooring_booking.py similarity index 100% rename from mooringlicensing/migrations/0256_approval_export_to_mooring_booking.py rename to mooringlicensing/migrations.original/0256_approval_export_to_mooring_booking.py diff --git a/mooringlicensing/migrations/0257_approval_fee_item.py b/mooringlicensing/migrations.original/0257_approval_fee_item.py similarity index 100% rename from mooringlicensing/migrations/0257_approval_fee_item.py rename to mooringlicensing/migrations.original/0257_approval_fee_item.py diff --git a/mooringlicensing/migrations/0258_auto_20210920_1130.py b/mooringlicensing/migrations.original/0258_auto_20210920_1130.py similarity index 100% rename from mooringlicensing/migrations/0258_auto_20210920_1130.py rename to mooringlicensing/migrations.original/0258_auto_20210920_1130.py diff --git a/mooringlicensing/migrations/0259_remove_approval_fee_period.py b/mooringlicensing/migrations.original/0259_remove_approval_fee_period.py similarity index 100% rename from mooringlicensing/migrations/0259_remove_approval_fee_period.py rename to mooringlicensing/migrations.original/0259_remove_approval_fee_period.py diff --git a/mooringlicensing/migrations/0260_sticker_fee_season.py b/mooringlicensing/migrations.original/0260_sticker_fee_season.py similarity index 100% rename from mooringlicensing/migrations/0260_sticker_fee_season.py rename to mooringlicensing/migrations.original/0260_sticker_fee_season.py diff --git a/mooringlicensing/migrations/0261_auto_20210928_1532.py b/mooringlicensing/migrations.original/0261_auto_20210928_1532.py similarity index 100% rename from mooringlicensing/migrations/0261_auto_20210928_1532.py rename to mooringlicensing/migrations.original/0261_auto_20210928_1532.py diff --git a/mooringlicensing/migrations/0262_applicationfee_fee_items_additional_aa.py b/mooringlicensing/migrations.original/0262_applicationfee_fee_items_additional_aa.py similarity index 100% rename from mooringlicensing/migrations/0262_applicationfee_fee_items_additional_aa.py rename to mooringlicensing/migrations.original/0262_applicationfee_fee_items_additional_aa.py diff --git a/mooringlicensing/migrations/0262_auto_20211011_1039.py b/mooringlicensing/migrations.original/0262_auto_20211011_1039.py similarity index 100% rename from mooringlicensing/migrations/0262_auto_20211011_1039.py rename to mooringlicensing/migrations.original/0262_auto_20211011_1039.py diff --git a/mooringlicensing/migrations/0263_auto_20211006_0955.py b/mooringlicensing/migrations.original/0263_auto_20211006_0955.py similarity index 100% rename from mooringlicensing/migrations/0263_auto_20211006_0955.py rename to mooringlicensing/migrations.original/0263_auto_20211006_0955.py diff --git a/mooringlicensing/migrations/0264_remove_applicationfee_fee_items_additional_aa.py b/mooringlicensing/migrations.original/0264_remove_applicationfee_fee_items_additional_aa.py similarity index 100% rename from mooringlicensing/migrations/0264_remove_applicationfee_fee_items_additional_aa.py rename to mooringlicensing/migrations.original/0264_remove_applicationfee_fee_items_additional_aa.py diff --git a/mooringlicensing/migrations/0265_auto_20211006_0958.py b/mooringlicensing/migrations.original/0265_auto_20211006_0958.py similarity index 100% rename from mooringlicensing/migrations/0265_auto_20211006_0958.py rename to mooringlicensing/migrations.original/0265_auto_20211006_0958.py diff --git a/mooringlicensing/migrations/0266_remove_applicationfee_fee_items.py b/mooringlicensing/migrations.original/0266_remove_applicationfee_fee_items.py similarity index 100% rename from mooringlicensing/migrations/0266_remove_applicationfee_fee_items.py rename to mooringlicensing/migrations.original/0266_remove_applicationfee_fee_items.py diff --git a/mooringlicensing/migrations/0267_auto_20211007_1204.py b/mooringlicensing/migrations.original/0267_auto_20211007_1204.py similarity index 100% rename from mooringlicensing/migrations/0267_auto_20211007_1204.py rename to mooringlicensing/migrations.original/0267_auto_20211007_1204.py diff --git a/mooringlicensing/migrations/0268_merge_20211012_1505.py b/mooringlicensing/migrations.original/0268_merge_20211012_1505.py similarity index 100% rename from mooringlicensing/migrations/0268_merge_20211012_1505.py rename to mooringlicensing/migrations.original/0268_merge_20211012_1505.py diff --git a/mooringlicensing/migrations/0269_auto_20211012_1505.py b/mooringlicensing/migrations.original/0269_auto_20211012_1505.py similarity index 100% rename from mooringlicensing/migrations/0269_auto_20211012_1505.py rename to mooringlicensing/migrations.original/0269_auto_20211012_1505.py diff --git a/mooringlicensing/migrations/0270_auto_20211015_1308.py b/mooringlicensing/migrations.original/0270_auto_20211015_1308.py similarity index 100% rename from mooringlicensing/migrations/0270_auto_20211015_1308.py rename to mooringlicensing/migrations.original/0270_auto_20211015_1308.py diff --git a/mooringlicensing/migrations/0270_auto_20211018_1145.py b/mooringlicensing/migrations.original/0270_auto_20211018_1145.py similarity index 100% rename from mooringlicensing/migrations/0270_auto_20211018_1145.py rename to mooringlicensing/migrations.original/0270_auto_20211018_1145.py diff --git a/mooringlicensing/migrations/0271_auto_20211018_0857.py b/mooringlicensing/migrations.original/0271_auto_20211018_0857.py similarity index 100% rename from mooringlicensing/migrations/0271_auto_20211018_0857.py rename to mooringlicensing/migrations.original/0271_auto_20211018_0857.py diff --git a/mooringlicensing/migrations/0271_delete_question.py b/mooringlicensing/migrations.original/0271_delete_question.py similarity index 100% rename from mooringlicensing/migrations/0271_delete_question.py rename to mooringlicensing/migrations.original/0271_delete_question.py diff --git a/mooringlicensing/migrations/0272_auto_20211020_1408.py b/mooringlicensing/migrations.original/0272_auto_20211020_1408.py similarity index 100% rename from mooringlicensing/migrations/0272_auto_20211020_1408.py rename to mooringlicensing/migrations.original/0272_auto_20211020_1408.py diff --git a/mooringlicensing/migrations/0272_merge_20211019_0905.py b/mooringlicensing/migrations.original/0272_merge_20211019_0905.py similarity index 100% rename from mooringlicensing/migrations/0272_merge_20211019_0905.py rename to mooringlicensing/migrations.original/0272_merge_20211019_0905.py diff --git a/mooringlicensing/migrations/0273_merge_20211019_1218.py b/mooringlicensing/migrations.original/0273_merge_20211019_1218.py similarity index 100% rename from mooringlicensing/migrations/0273_merge_20211019_1218.py rename to mooringlicensing/migrations.original/0273_merge_20211019_1218.py diff --git a/mooringlicensing/migrations/0274_merge_20211021_0901.py b/mooringlicensing/migrations.original/0274_merge_20211021_0901.py similarity index 100% rename from mooringlicensing/migrations/0274_merge_20211021_0901.py rename to mooringlicensing/migrations.original/0274_merge_20211021_0901.py diff --git a/mooringlicensing/migrations/0275_compliance_submitter.py b/mooringlicensing/migrations.original/0275_compliance_submitter.py similarity index 100% rename from mooringlicensing/migrations/0275_compliance_submitter.py rename to mooringlicensing/migrations.original/0275_compliance_submitter.py diff --git a/mooringlicensing/migrations/0276_auto_20211105_1309.py b/mooringlicensing/migrations.original/0276_auto_20211105_1309.py similarity index 100% rename from mooringlicensing/migrations/0276_auto_20211105_1309.py rename to mooringlicensing/migrations.original/0276_auto_20211105_1309.py diff --git a/mooringlicensing/migrations/0277_proposal_fee_season.py b/mooringlicensing/migrations.original/0277_proposal_fee_season.py similarity index 100% rename from mooringlicensing/migrations/0277_proposal_fee_season.py rename to mooringlicensing/migrations.original/0277_proposal_fee_season.py diff --git a/mooringlicensing/migrations/0278_proposal_keep_existing_vessel.py b/mooringlicensing/migrations.original/0278_proposal_keep_existing_vessel.py similarity index 100% rename from mooringlicensing/migrations/0278_proposal_keep_existing_vessel.py rename to mooringlicensing/migrations.original/0278_proposal_keep_existing_vessel.py diff --git a/mooringlicensing/migrations/0278_sticker_replaced_for_renewal.py b/mooringlicensing/migrations.original/0278_sticker_replaced_for_renewal.py similarity index 100% rename from mooringlicensing/migrations/0278_sticker_replaced_for_renewal.py rename to mooringlicensing/migrations.original/0278_sticker_replaced_for_renewal.py diff --git a/mooringlicensing/migrations/0279_auto_20220316_0840.py b/mooringlicensing/migrations.original/0279_auto_20220316_0840.py similarity index 100% rename from mooringlicensing/migrations/0279_auto_20220316_0840.py rename to mooringlicensing/migrations.original/0279_auto_20220316_0840.py diff --git a/mooringlicensing/migrations/0279_remove_sticker_replaced_for_renewal.py b/mooringlicensing/migrations.original/0279_remove_sticker_replaced_for_renewal.py similarity index 100% rename from mooringlicensing/migrations/0279_remove_sticker_replaced_for_renewal.py rename to mooringlicensing/migrations.original/0279_remove_sticker_replaced_for_renewal.py diff --git a/mooringlicensing/migrations/0280_sticker_sticker_to_replace.py b/mooringlicensing/migrations.original/0280_sticker_sticker_to_replace.py similarity index 100% rename from mooringlicensing/migrations/0280_sticker_sticker_to_replace.py rename to mooringlicensing/migrations.original/0280_sticker_sticker_to_replace.py diff --git a/mooringlicensing/migrations/0281_merge_20220316_0934.py b/mooringlicensing/migrations.original/0281_merge_20220316_0934.py similarity index 100% rename from mooringlicensing/migrations/0281_merge_20220316_0934.py rename to mooringlicensing/migrations.original/0281_merge_20220316_0934.py diff --git a/mooringlicensing/migrations/0282_merge_20220316_1027.py b/mooringlicensing/migrations.original/0282_merge_20220316_1027.py similarity index 100% rename from mooringlicensing/migrations/0282_merge_20220316_1027.py rename to mooringlicensing/migrations.original/0282_merge_20220316_1027.py diff --git a/mooringlicensing/migrations/0283_auto_20220316_1450.py b/mooringlicensing/migrations.original/0283_auto_20220316_1450.py similarity index 100% rename from mooringlicensing/migrations/0283_auto_20220316_1450.py rename to mooringlicensing/migrations.original/0283_auto_20220316_1450.py diff --git a/mooringlicensing/migrations/0284_auto_20220321_1921.py b/mooringlicensing/migrations.original/0284_auto_20220321_1921.py similarity index 100% rename from mooringlicensing/migrations/0284_auto_20220321_1921.py rename to mooringlicensing/migrations.original/0284_auto_20220321_1921.py diff --git a/mooringlicensing/migrations/0285_auto_20220328_1507.py b/mooringlicensing/migrations.original/0285_auto_20220328_1507.py similarity index 100% rename from mooringlicensing/migrations/0285_auto_20220328_1507.py rename to mooringlicensing/migrations.original/0285_auto_20220328_1507.py diff --git a/mooringlicensing/migrations/0286_auto_20220328_1616.py b/mooringlicensing/migrations.original/0286_auto_20220328_1616.py similarity index 100% rename from mooringlicensing/migrations/0286_auto_20220328_1616.py rename to mooringlicensing/migrations.original/0286_auto_20220328_1616.py diff --git a/mooringlicensing/migrations/0287_stickerprintedcontact.py b/mooringlicensing/migrations.original/0287_stickerprintedcontact.py similarity index 100% rename from mooringlicensing/migrations/0287_stickerprintedcontact.py rename to mooringlicensing/migrations.original/0287_stickerprintedcontact.py diff --git a/mooringlicensing/migrations/0288_auto_20220408_1512.py b/mooringlicensing/migrations.original/0288_auto_20220408_1512.py similarity index 100% rename from mooringlicensing/migrations/0288_auto_20220408_1512.py rename to mooringlicensing/migrations.original/0288_auto_20220408_1512.py diff --git a/mooringlicensing/migrations/0288_proposal_auto_approve.py b/mooringlicensing/migrations.original/0288_proposal_auto_approve.py similarity index 100% rename from mooringlicensing/migrations/0288_proposal_auto_approve.py rename to mooringlicensing/migrations.original/0288_proposal_auto_approve.py diff --git a/mooringlicensing/migrations/0289_auto_20220411_1413.py b/mooringlicensing/migrations.original/0289_auto_20220411_1413.py similarity index 100% rename from mooringlicensing/migrations/0289_auto_20220411_1413.py rename to mooringlicensing/migrations.original/0289_auto_20220411_1413.py diff --git a/mooringlicensing/migrations/0290_auto_20220427_1339.py b/mooringlicensing/migrations.original/0290_auto_20220427_1339.py similarity index 100% rename from mooringlicensing/migrations/0290_auto_20220427_1339.py rename to mooringlicensing/migrations.original/0290_auto_20220427_1339.py diff --git a/mooringlicensing/migrations/0291_merge_20220503_1621.py b/mooringlicensing/migrations.original/0291_merge_20220503_1621.py similarity index 100% rename from mooringlicensing/migrations/0291_merge_20220503_1621.py rename to mooringlicensing/migrations.original/0291_merge_20220503_1621.py diff --git a/mooringlicensing/migrations/0292_applicationfee_system_invoice.py b/mooringlicensing/migrations.original/0292_applicationfee_system_invoice.py similarity index 100% rename from mooringlicensing/migrations/0292_applicationfee_system_invoice.py rename to mooringlicensing/migrations.original/0292_applicationfee_system_invoice.py diff --git a/mooringlicensing/migrations/0293_proposal_null_vessel_on_create.py b/mooringlicensing/migrations.original/0293_proposal_null_vessel_on_create.py similarity index 100% rename from mooringlicensing/migrations/0293_proposal_null_vessel_on_create.py rename to mooringlicensing/migrations.original/0293_proposal_null_vessel_on_create.py diff --git a/mooringlicensing/migrations/0294_feeitemapplicationfee_amount_paid.py b/mooringlicensing/migrations.original/0294_feeitemapplicationfee_amount_paid.py similarity index 100% rename from mooringlicensing/migrations/0294_feeitemapplicationfee_amount_paid.py rename to mooringlicensing/migrations.original/0294_feeitemapplicationfee_amount_paid.py diff --git a/mooringlicensing/migrations/0295_feeitemapplicationfee_amount_to_be_paid.py b/mooringlicensing/migrations.original/0295_feeitemapplicationfee_amount_to_be_paid.py similarity index 100% rename from mooringlicensing/migrations/0295_feeitemapplicationfee_amount_to_be_paid.py rename to mooringlicensing/migrations.original/0295_feeitemapplicationfee_amount_to_be_paid.py diff --git a/mooringlicensing/migrations/0296_auto_20220531_1555.py b/mooringlicensing/migrations.original/0296_auto_20220531_1555.py similarity index 100% rename from mooringlicensing/migrations/0296_auto_20220531_1555.py rename to mooringlicensing/migrations.original/0296_auto_20220531_1555.py diff --git a/mooringlicensing/migrations.original/__init__.py b/mooringlicensing/migrations.original/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mooringlicensing/migrations/0001_initial.py b/mooringlicensing/migrations/0001_initial.py index c1f341260..cbef7617b 100644 --- a/mooringlicensing/migrations/0001_initial.py +++ b/mooringlicensing/migrations/0001_initial.py @@ -1,18 +1,19 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.29 on 2021-03-02 08:32 -from __future__ import unicode_literals +# Generated by Django 3.2.16 on 2022-12-08 01:22 import ckeditor.fields import dirtyfields.dirtyfields -from django.conf import settings +import django.contrib.postgres.fields import django.contrib.postgres.fields.jsonb import django.core.validators from django.db import migrations, models import django.db.models.deletion +import django.utils.timezone import mooringlicensing.components.approvals.models import mooringlicensing.components.compliances.models import mooringlicensing.components.organisations.models import mooringlicensing.components.proposals.models +import smart_selects.db_fields +import uuid class Migration(migrations.Migration): @@ -20,12 +21,23 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('sites', '0002_alter_domain_unique'), - ('accounts', '0024_organisation_email'), ] operations = [ + migrations.CreateModel( + name='AdmissionType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(choices=[('landing', 'Landing'), ('extended_stay', 'Extended stay'), ('not_landing', 'Not landing'), ('approved_events', 'Approved events')], default='landing', max_length=40)), + ], + ), + migrations.CreateModel( + name='AgeGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(choices=[('adult', 'Adult'), ('child', 'Child')], default='adult', max_length=40)), + ], + ), migrations.CreateModel( name='AmendmentReason', fields=[ @@ -37,34 +49,50 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Application Amendment Reasons', }, ), + migrations.CreateModel( + name='ApplicationFee', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('send_invoice', models.BooleanField(default=False)), + ('confirmation_sent', models.BooleanField(default=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('expiry_time', models.DateTimeField(auto_now_add=True, null=True)), + ('payment_type', models.SmallIntegerField(choices=[(0, 'Internet booking'), (1, 'Reception booking'), (2, 'Black booking'), (3, 'Temporary reservation')], default=0)), + ('cost', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('created_by', models.IntegerField(blank=True, null=True)), + ('invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), + ('system_invoice', models.BooleanField(default=False)), + ], + ), migrations.CreateModel( name='ApplicationType', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(choices=[('Waiting List Application', 'Waiting List Application'), ('Annual Admission Application', 'Annual Admission Application'), ('Authorised User Application', 'Authorised User Application'), ('Mooring License Application', 'Mooring License Application')], max_length=64, verbose_name='Application Type name')), - ('order', models.PositiveSmallIntegerField(default=0)), - ('visible', models.BooleanField(default=True)), - ('application_fee', models.DecimalField(decimal_places=2, max_digits=6)), - ('oracle_code_application', models.CharField(max_length=50)), - ('is_gst_exempt', models.BooleanField(default=True)), + ('code', models.CharField(blank=True, max_length=30, null=True, unique=True)), + ('description', models.CharField(blank=True, max_length=200, null=True)), + ('fee_by_fee_constructor', models.BooleanField(default=True)), ], options={ - 'ordering': ['order', 'name'], + 'verbose_name': 'Oracle code', }, ), migrations.CreateModel( name='Approval', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('lodgement_number', models.CharField(blank=True, default='', max_length=9)), - ('status', models.CharField(choices=[('current', 'Current'), ('expired', 'Expired'), ('cancelled', 'Cancelled'), ('surrendered', 'Surrendered'), ('suspended', 'Suspended'), ('extended', 'extended'), ('awaiting_payment', 'Awaiting Payment')], default='current', max_length=40)), + ('lodgement_number', models.CharField(blank=True, max_length=9, unique=True)), + ('status', models.CharField(choices=[('current', 'Current'), ('expired', 'Expired'), ('cancelled', 'Cancelled'), ('surrendered', 'Surrendered'), ('suspended', 'Suspended'), ('fulfilled', 'Fulfilled')], default='current', max_length=40)), + ('internal_status', models.CharField(blank=True, choices=[('waiting', 'Waiting for offer'), ('offered', 'Mooring Licence offered'), ('submitted', 'Mooring Licence application submitted')], max_length=40, null=True)), ('renewal_sent', models.BooleanField(default=False)), ('issue_date', models.DateTimeField()), + ('wla_queue_date', models.DateTimeField(blank=True, null=True)), ('original_issue_date', models.DateField(auto_now_add=True)), - ('start_date', models.DateField()), - ('expiry_date', models.DateField()), + ('start_date', models.DateField(blank=True, null=True)), + ('expiry_date', models.DateField(blank=True, null=True)), ('surrender_details', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), ('suspension_details', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), + ('submitter', models.IntegerField(blank=True, null=True)), + ('proxy_applicant', models.IntegerField(blank=True, null=True)), ('extracted_fields', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), ('cancellation_details', models.TextField(blank=True)), ('extend_details', models.TextField(blank=True)), @@ -74,9 +102,16 @@ class Migration(migrations.Migration): ('set_to_surrender', models.BooleanField(default=False)), ('renewal_count', models.PositiveSmallIntegerField(default=0, verbose_name='Number of times an Approval has been renewed')), ('migrated', models.BooleanField(default=False)), - ('extended', models.BooleanField(default=False)), ('expiry_notice_sent', models.BooleanField(default=False)), + ('exported', models.BooleanField(default=False)), + ('wla_order', models.PositiveIntegerField(help_text='wla order per mooring bay', null=True)), + ('vessel_nomination_reminder_sent', models.BooleanField(default=False)), + ('reissued', models.BooleanField(default=False)), + ('export_to_mooring_booking', models.BooleanField(default=False)), ], + options={ + 'ordering': ['-id'], + }, ), migrations.CreateModel( name='ApprovalDocument', @@ -87,54 +122,49 @@ class Migration(migrations.Migration): ('uploaded_date', models.DateTimeField(auto_now_add=True)), ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_approval_doc_filename)), ('can_delete', models.BooleanField(default=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.approval')), ], ), migrations.CreateModel( - name='ApprovalLogDocument', + name='CommunicationsLogEntry', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, null=True, upload_to=mooringlicensing.components.approvals.models.update_approval_comms_log_filename)), + ('to', models.TextField(blank=True, verbose_name='To')), + ('fromm', models.CharField(blank=True, max_length=200, verbose_name='From')), + ('cc', models.TextField(blank=True, verbose_name='cc')), + ('type', models.CharField(choices=[('email', 'Email'), ('phone', 'Phone Call'), ('mail', 'Mail'), ('person', 'In Person'), ('onhold', 'On Hold'), ('onhold_remove', 'Remove On Hold'), ('with_qaofficer', 'With QA Officer'), ('with_qaofficer_completed', 'QA Officer Completed'), ('referral_complete', 'Referral Completed')], default='email', max_length=35)), + ('reference', models.CharField(blank=True, max_length=100)), + ('subject', models.CharField(blank=True, max_length=200, verbose_name='Subject / Description')), + ('text', models.TextField(blank=True)), + ('customer', models.IntegerField(null=True)), + ('staff', models.IntegerField(blank=True, null=True)), + ('created', models.DateTimeField(auto_now_add=True)), ], ), migrations.CreateModel( - name='ApprovalUserAction', + name='Company', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('when', models.DateTimeField(auto_now_add=True)), - ('what', models.TextField()), + ('name', models.CharField(blank=True, max_length=200, null=True, unique=True)), ], options={ - 'ordering': ('-when',), + 'verbose_name_plural': 'Companies', }, ), migrations.CreateModel( - name='ChecklistQuestion', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField()), - ('list_type', models.CharField(choices=[('assessor_list', 'Assessor Checklist'), ('referral_list', 'Referral Checklist')], default='assessor_list', max_length=30, verbose_name='Checklist type')), - ('answer_type', models.CharField(choices=[('yes_no', 'Yes/No type'), ('free_text', 'Free text type')], default='yes_no', max_length=30, verbose_name='Answer type')), - ('obsolete', models.BooleanField(default=False)), - ('order', models.PositiveSmallIntegerField(default=1)), - ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), - ], - ), - migrations.CreateModel( - name='CommunicationsLogEntry', + name='CompanyOwnership', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('to', models.TextField(blank=True, verbose_name='To')), - ('fromm', models.CharField(blank=True, max_length=200, verbose_name='From')), - ('cc', models.TextField(blank=True, verbose_name='cc')), - ('type', models.CharField(choices=[('email', 'Email'), ('phone', 'Phone Call'), ('mail', 'Mail'), ('person', 'In Person'), ('onhold', 'On Hold'), ('onhold_remove', 'Remove On Hold'), ('with_qaofficer', 'With QA Officer'), ('with_qaofficer_completed', 'QA Officer Completed'), ('referral_complete', 'Referral Completed')], default='email', max_length=35)), - ('reference', models.CharField(blank=True, max_length=100)), - ('subject', models.CharField(blank=True, max_length=200, verbose_name='Subject / Description')), - ('text', models.TextField(blank=True)), - ('created', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(choices=[('approved', 'Approved'), ('draft', 'Draft'), ('old', 'Old'), ('declined', 'Declined')], default='draft', max_length=50)), + ('percentage', models.IntegerField(blank=True, null=True)), + ('start_date', models.DateTimeField(default=django.utils.timezone.now)), + ('end_date', models.DateTimeField(null=True)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('updated', models.DateTimeField(auto_now=True)), ], + options={ + 'verbose_name_plural': 'Company Ownership', + }, ), migrations.CreateModel( name='Compliance', @@ -144,12 +174,15 @@ class Migration(migrations.Migration): ('due_date', models.DateField()), ('text', models.TextField(blank=True)), ('num_participants', models.SmallIntegerField(blank=True, null=True, verbose_name='Number of participants')), - ('processing_status', models.CharField(choices=[('due', 'Due'), ('future', 'Future'), ('with_assessor', 'With Assessor'), ('approved', 'Approved'), ('discarded', 'Discarded')], max_length=20)), - ('customer_status', models.CharField(choices=[('due', 'Due'), ('future', 'Future'), ('with_assessor', 'Under Review'), ('approved', 'Approved'), ('discarded', 'Discarded')], default='future', max_length=20)), + ('processing_status', models.CharField(choices=[('due', 'Due'), ('overdue', 'Overdue'), ('future', 'Future'), ('with_assessor', 'With Assessor'), ('approved', 'Approved'), ('discarded', 'Discarded')], max_length=20)), + ('customer_status', models.CharField(choices=[('due', 'Due'), ('overdue', 'Overdue'), ('future', 'Future'), ('with_assessor', 'Under Review'), ('approved', 'Approved'), ('discarded', 'Discarded')], max_length=20)), + ('assigned_to', models.IntegerField(blank=True, null=True)), ('lodgement_date', models.DateTimeField(blank=True, null=True)), + ('submitter', models.IntegerField(blank=True, null=True)), ('reminder_sent', models.BooleanField(default=False)), ('post_reminder_sent', models.BooleanField(default=False)), ('fee_invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='compliances', to='mooringlicensing.approval')), ], ), migrations.CreateModel( @@ -160,71 +193,166 @@ class Migration(migrations.Migration): ], ), migrations.CreateModel( - name='ComplianceDocument', + name='CompRequest', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_proposal_complaince_filename)), - ('can_delete', models.BooleanField(default=True)), - ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Compliance')), + ('subject', models.CharField(blank=True, max_length=200)), + ('text', models.TextField(blank=True)), + ('officer', models.IntegerField(blank=True, null=True)), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='mooringlicensing.compliance')), ], ), migrations.CreateModel( - name='ComplianceLogDocument', + name='DcvAdmission', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_compliance_comms_log_filename)), + ('submitter', models.IntegerField(blank=True, null=True)), + ('lodgement_number', models.CharField(blank=True, max_length=10, unique=True)), + ('lodgement_datetime', models.DateTimeField(blank=True, null=True)), + ('skipper', models.CharField(blank=True, max_length=50, null=True)), + ('contact_number', models.CharField(blank=True, max_length=50, null=True)), ], ), migrations.CreateModel( - name='ComplianceUserAction', + name='DcvAdmissionArrival', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('when', models.DateTimeField(auto_now_add=True)), - ('what', models.TextField()), - ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Compliance')), - ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('arrival_date', models.DateField(blank=True, null=True)), + ('departure_date', models.DateField(blank=True, null=True)), + ('private_visit', models.BooleanField(default=False)), + ('start_date', models.DateField(blank=True, null=True)), + ('end_date', models.DateField(blank=True, null=True)), + ('dcv_admission', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dcv_admission_arrivals', to='mooringlicensing.dcvadmission')), ], ), migrations.CreateModel( - name='CompRequest', + name='DcvOrganisation', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('subject', models.CharField(blank=True, max_length=200)), - ('text', models.TextField(blank=True)), + ('name', models.CharField(blank=True, max_length=128, null=True)), + ('abn', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='ABN')), + ], + ), + migrations.CreateModel( + name='DcvPermit', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('submitter', models.IntegerField(blank=True, null=True)), + ('lodgement_number', models.CharField(blank=True, max_length=10, unique=True)), + ('lodgement_datetime', models.DateTimeField(blank=True, null=True)), + ('start_date', models.DateField(blank=True, null=True)), + ('end_date', models.DateField(blank=True, null=True)), + ('renewal_sent', models.BooleanField(default=False)), + ('migrated', models.BooleanField(default=False)), + ('dcv_organisation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.dcvorganisation')), + ], + ), + migrations.CreateModel( + name='FeeConstructor', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('incur_gst', models.BooleanField(default=True)), + ('enabled', models.BooleanField(default=True)), + ('application_type', models.ForeignKey(limit_choices_to={'fee_by_fee_constructor': True}, on_delete=django.db.models.deletion.PROTECT, to='mooringlicensing.applicationtype')), + ], + ), + migrations.CreateModel( + name='FeeItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, default='0.00', help_text='$', max_digits=8)), + ('incremental_amount', models.BooleanField(default=False, help_text='When ticked, The amount will be the increase in the rate per meter')), + ('admission_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.admissiontype')), + ('age_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.agegroup')), + ('fee_constructor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeconstructor')), + ], + ), + migrations.CreateModel( + name='FeeItemStickerReplacement', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=2, default='0.00', help_text='unit [$AU/Sticker]', max_digits=8)), + ('date_of_enforcement', models.DateField(blank=True, null=True)), + ('enabled', models.BooleanField(default=True)), + ('incur_gst', models.BooleanField(default=True)), + ], + options={ + 'verbose_name': 'Fee (sticker replacement)', + 'verbose_name_plural': 'Fee (sticker replacement)', + }, + ), + migrations.CreateModel( + name='FeeSeason', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('application_type', models.ForeignKey(blank=True, limit_choices_to={'fee_by_fee_constructor': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.applicationtype')), ], + options={ + 'verbose_name': 'season', + }, ), migrations.CreateModel( name='GlobalSettings', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('key', models.CharField(choices=[('', '')], max_length=255)), + ('key', models.CharField(choices=[('dcv_permit_template_file', 'DcvPermit template file'), ('dcv_admission_template_file', 'DcvAdmission template file'), ('wla_template_file', 'Waiting List Allocation template file'), ('aap_template_file', 'Annual Admission Permit template file'), ('aup_template_file', 'Authorised User Permit template file'), ('ml_template_file', 'Mooring Licence template file'), ('ml_au_list_template_file', 'Mooring Licence Authorised User Summary template file'), ('minimum_vessel_length', 'Minimum vessel length'), ('minimum_mooring_vessel_length', 'Minimum mooring vessel length'), ('min_sticker_number_for_dcv_permit', 'Minimun sticker number for DCV Permit')], max_length=255)), ('value', models.CharField(max_length=255)), + ('_file', models.FileField(blank=True, null=True, upload_to='approval_permit_template')), ], options={ 'verbose_name_plural': 'Global Settings', }, ), migrations.CreateModel( - name='HelpPage', + name='Mooring', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('content', ckeditor.fields.RichTextField()), - ('description', models.CharField(blank=True, max_length=256, null=True)), - ('help_type', models.SmallIntegerField(choices=[(1, 'External'), (2, 'Internal')], default=1, verbose_name='Help Type')), - ('version', models.SmallIntegerField(default=1)), - ('application_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ('name', models.CharField(max_length=100)), + ('active', models.BooleanField(default=True)), + ('vessel_size_limit', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_draft_limit', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_beam_limit', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_weight_limit', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('mooring_bookings_id', models.IntegerField()), + ('mooring_bookings_mooring_specification', models.IntegerField(choices=[(1, 'Rental Mooring'), (2, 'Private Mooring')])), + ('mooring_bookings_bay_id', models.IntegerField()), + ], + options={ + 'verbose_name_plural': 'Moorings', + }, + ), + migrations.CreateModel( + name='MooringBay', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('mooring_bookings_id', models.IntegerField()), + ('active', models.BooleanField(default=True)), + ], + options={ + 'verbose_name_plural': 'Mooring Bays', + }, + ), + migrations.CreateModel( + name='NumberOfDaysType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(blank=True, max_length=100, null=True)), + ('name', models.CharField(max_length=100)), + ('description', models.TextField(blank=True, verbose_name='description')), ], + options={ + 'verbose_name': 'Number of days Settings', + 'verbose_name_plural': 'Number of days Settings', + }, ), migrations.CreateModel( name='Organisation', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('organisation', models.IntegerField()), + ('delegates', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), blank=True, size=None)), ('admin_pin_one', models.CharField(blank=True, max_length=50)), ('admin_pin_two', models.CharField(blank=True, max_length=50)), ('user_pin_one', models.CharField(blank=True, max_length=50)), @@ -245,114 +373,87 @@ class Migration(migrations.Migration): name='OrganisationAccessGroup', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), - ('site', models.OneToOneField(default='1', on_delete=django.db.models.deletion.CASCADE, to='sites.Site')), + ('members', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), blank=True, size=None)), ], options={ 'verbose_name_plural': 'Organisation access group', }, ), - migrations.CreateModel( - name='OrganisationAction', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('when', models.DateTimeField(auto_now_add=True)), - ('what', models.TextField()), - ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Organisation')), - ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='OrganisationContact', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user_status', models.CharField(choices=[('draft', 'Draft'), ('pending', 'Pending'), ('active', 'Active'), ('declined', 'Declined'), ('unlinked', 'Unlinked'), ('suspended', 'Suspended')], default='draft', max_length=40, verbose_name='Status')), - ('user_role', models.CharField(choices=[('organisation_admin', 'Organisation Admin'), ('organisation_user', 'Organisation User'), ('consultant', 'Consultant')], default='organisation_user', max_length=40, verbose_name='Role')), - ('is_admin', models.BooleanField(default=False)), - ('email', models.EmailField(max_length=254)), - ('first_name', models.CharField(max_length=128, verbose_name='Given name(s)')), - ('last_name', models.CharField(max_length=128)), - ('phone_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='phone number')), - ('mobile_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='mobile number')), - ('fax_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='fax number')), - ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='mooringlicensing.Organisation')), - ], - ), - migrations.CreateModel( - name='OrganisationLogDocument', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_comms_log_filename)), - ], - ), migrations.CreateModel( name='OrganisationRequest', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=128)), ('abn', models.CharField(blank=True, max_length=50, null=True, verbose_name='ABN')), + ('requester', models.IntegerField()), + ('assigned_officer', models.IntegerField(blank=True, null=True)), ('identification', models.FileField(blank=True, max_length=512, null=True, upload_to='organisation/requests/%Y/%m/%d')), ('status', models.CharField(choices=[('with_assessor', 'With Assessor'), ('approved', 'Approved'), ('declined', 'Declined')], default='with_assessor', max_length=100)), ('lodgement_date', models.DateTimeField(auto_now_add=True)), ('role', models.CharField(choices=[('employee', 'Employee'), ('consultant', 'Consultant')], default='employee', max_length=100)), - ('assigned_officer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='org_request_assignee', to=settings.AUTH_USER_MODEL)), - ('requester', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.CreateModel( - name='OrganisationRequestDeclinedDetails', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reason', models.TextField(blank=True)), - ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.OrganisationRequest')), - ], - ), - migrations.CreateModel( - name='OrganisationRequestLogDocument', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_request_comms_log_filename)), ], ), migrations.CreateModel( - name='OrganisationRequestUserAction', + name='Owner', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('when', models.DateTimeField(auto_now_add=True)), - ('what', models.TextField()), - ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.OrganisationRequest')), - ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('emailuser', models.IntegerField(unique=True)), ], + options={ + 'verbose_name_plural': 'Owners', + }, ), migrations.CreateModel( name='Proposal', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('proposal_type', models.CharField(choices=[('new_proposal', 'New Application'), ('amendment', 'Amendment'), ('renewal', 'Renewal'), ('external', 'External')], default='new_proposal', max_length=40, verbose_name='Application Status Type')), - ('data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), ('assessor_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), ('comment_data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), - ('schema', django.contrib.postgres.fields.jsonb.JSONField()), ('proposed_issuance_approval', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)), - ('customer_status', models.CharField(choices=[('temp', 'Temporary'), ('draft', 'Draft'), ('with_assessor', 'Under Review'), ('amendment_required', 'Amendment Required'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('partially_approved', 'Partially Approved'), ('partially_declined', 'Partially Declined'), ('awaiting_payment', 'Awaiting Payment')], default='draft', max_length=40, verbose_name='Customer Status')), + ('customer_status', models.CharField(choices=[('draft', 'Draft'), ('with_assessor', 'Under Review'), ('with_approver', 'Under Review'), ('awaiting_endorsement', 'Awaiting Endorsement'), ('awaiting_documents', 'Awaiting Documents'), ('sticker_to_be_returned', 'Sticker to be Returned'), ('printing_sticker', 'Printing Sticker'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('awaiting_payment', 'Awaiting Payment'), ('expired', 'Expired')], default='draft', max_length=40, verbose_name='Customer Status')), ('lodgement_number', models.CharField(blank=True, default='', max_length=9)), ('lodgement_sequence', models.IntegerField(blank=True, default=0)), ('lodgement_date', models.DateTimeField(blank=True, null=True)), - ('processing_status', models.CharField(choices=[('temp', 'Temporary'), ('draft', 'Draft'), ('with_assessor', 'With Assessor'), ('with_district_assessor', 'With District Assessor'), ('on_hold', 'On Hold'), ('with_qa_officer', 'With QA Officer'), ('with_referral', 'With Referral'), ('with_assessor_requirements', 'With Assessor (Requirements)'), ('with_approver', 'With Approver'), ('renewal', 'Renewal'), ('licence_amendment', 'Licence Amendment'), ('awaiting_applicant_respone', 'Awaiting Applicant Response'), ('awaiting_assessor_response', 'Awaiting Assessor Response'), ('awaiting_responses', 'Awaiting Responses'), ('ready_for_conditions', 'Ready for Conditions'), ('ready_to_issue', 'Ready to Issue'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('partially_approved', 'Partially Approved'), ('partially_declined', 'Partially Declined'), ('awaiting_payment', 'Awaiting Payment')], default='draft', max_length=30, verbose_name='Processing Status')), - ('prev_processing_status', models.CharField(blank=True, max_length=30, null=True)), + ('proxy_applicant', models.IntegerField(blank=True, null=True)), + ('submitter', models.IntegerField(blank=True, null=True)), + ('assigned_officer', models.IntegerField(blank=True, null=True)), + ('assigned_approver', models.IntegerField(blank=True, null=True)), + ('processing_status', models.CharField(choices=[('draft', 'Draft'), ('with_assessor', 'With Assessor'), ('with_assessor_requirements', 'With Assessor (Requirements)'), ('with_approver', 'With Approver'), ('sticker_to_be_returned', 'Sticker to be Returned'), ('printing_sticker', 'Printing Sticker'), ('awaiting_endorsement', 'Awaiting Endorsement'), ('awaiting_documents', 'Awaiting Documents'), ('approved', 'Approved'), ('declined', 'Declined'), ('discarded', 'Discarded'), ('awaiting_payment', 'Awaiting Payment'), ('expired', 'Expired')], default='draft', max_length=40, verbose_name='Processing Status')), + ('prev_processing_status', models.CharField(blank=True, max_length=40, null=True)), ('proposed_decline_status', models.BooleanField(default=False)), ('title', models.CharField(blank=True, max_length=255, null=True)), ('approval_level', models.CharField(blank=True, max_length=255, null=True, verbose_name='Activity matrix approval level')), ('approval_comment', models.TextField(blank=True)), ('migrated', models.BooleanField(default=False)), - ('application_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ('rego_no', models.CharField(blank=True, max_length=200, null=True)), + ('vessel_id', models.IntegerField(blank=True, null=True)), + ('vessel_type', models.CharField(blank=True, choices=[('yacht', 'Yacht'), ('cabin_cruiser', 'Cabin Cruiser'), ('tender', 'Tender'), ('other', 'Other')], max_length=20)), + ('vessel_name', models.CharField(blank=True, max_length=400)), + ('vessel_length', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_draft', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_beam', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_weight', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('berth_mooring', models.CharField(blank=True, max_length=200)), + ('dot_name', models.CharField(blank=True, max_length=200, null=True)), + ('percentage', models.IntegerField(blank=True, null=True)), + ('individual_owner', models.NullBooleanField()), + ('company_ownership_percentage', models.IntegerField(blank=True, null=True)), + ('company_ownership_name', models.CharField(blank=True, max_length=200, null=True)), + ('insurance_choice', models.CharField(blank=True, choices=[('five_million', '$5 million Third Party Liability insurance cover - required for vessels of length less than 6.4 metres'), ('ten_million', '$10 million Third Party Liability insurance cover - required for vessels of length 6.4 metres or greater'), ('over_ten', 'over $10 million')], max_length=20)), + ('silent_elector', models.NullBooleanField()), + ('mooring_authorisation_preference', models.CharField(blank=True, choices=[('site_licensee', 'By a mooring site licensee for their mooring'), ('ria', 'By Rottnest Island Authority for a mooring allocated by the Authority')], max_length=20)), + ('bay_preferences_numbered', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(blank=True, null=True), blank=True, null=True, size=None)), + ('site_licensee_email', models.CharField(blank=True, max_length=200, null=True)), + ('endorser_reminder_sent', models.BooleanField(default=False)), + ('date_invited', models.DateField(blank=True, null=True)), + ('invitee_reminder_sent', models.BooleanField(default=False)), + ('temporary_document_collection_id', models.IntegerField(blank=True, null=True)), + ('keep_existing_mooring', models.BooleanField(default=True)), + ('keep_existing_vessel', models.BooleanField(default=True)), + ('auto_approve', models.BooleanField(default=False)), + ('null_vessel_on_create', models.BooleanField(default=True)), + ('allocated_mooring', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ria_generated_proposal', to='mooringlicensing.mooring')), + ('approval', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.approval')), ], options={ 'verbose_name': 'Application', @@ -361,558 +462,1264 @@ class Migration(migrations.Migration): bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model), ), migrations.CreateModel( - name='ProposalApproverGroup', + name='ProposalRequest', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ('subject', models.CharField(blank=True, max_length=200)), + ('text', models.TextField(blank=True)), + ('officer', models.IntegerField(blank=True, null=True)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proposalrequest_set', to='mooringlicensing.proposal')), ], - options={ - 'verbose_name': 'Application Approver Group', - 'verbose_name_plural': 'Application Approver Group', - }, ), migrations.CreateModel( - name='ProposalAssessment', + name='ProposalRequirement', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('completed', models.BooleanField(default=False)), - ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='assessment', to='mooringlicensing.Proposal')), - ('submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='proposal_assessment', to=settings.AUTH_USER_MODEL)), + ('order', models.PositiveIntegerField(db_index=True, editable=False)), + ('free_requirement', models.TextField(blank=True, null=True)), + ('standard', models.BooleanField(default=True)), + ('due_date', models.DateField(blank=True, null=True)), + ('recurrence', models.BooleanField(default=False)), + ('recurrence_pattern', models.SmallIntegerField(choices=[(1, 'Weekly'), (2, 'Monthly'), (3, 'Yearly')], default=1)), + ('recurrence_schedule', models.IntegerField(blank=True, null=True)), + ('is_deleted', models.BooleanField(default=False)), + ('copied_for_renewal', models.BooleanField(default=False)), + ('require_due_date', models.BooleanField(default=False)), + ('copied_from', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposalrequirement')), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirements', to='mooringlicensing.proposal')), ], ), migrations.CreateModel( - name='ProposalAssessmentAnswer', + name='ProposalType', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('answer', models.NullBooleanField()), - ('text_answer', models.CharField(blank=True, max_length=256, null=True)), - ('assessment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='mooringlicensing.ProposalAssessment')), - ('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='mooringlicensing.ChecklistQuestion')), + ('code', models.CharField(blank=True, max_length=30, null=True)), + ('description', models.CharField(blank=True, max_length=200, null=True)), ], - options={ - 'verbose_name': 'Assessment answer', - 'verbose_name_plural': 'Assessment answers', - }, ), migrations.CreateModel( - name='ProposalAssessorGroup', + name='Sticker', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('members', models.ManyToManyField(to=settings.AUTH_USER_MODEL)), + ('number', models.CharField(blank=True, default='', max_length=9)), + ('status', models.CharField(choices=[('ready', 'Ready'), ('not_ready_yet', 'Not Ready Yet'), ('awaiting_printing', 'Awaiting Printing'), ('current', 'Current'), ('to_be_returned', 'To be Returned'), ('returned', 'Returned'), ('lost', 'Lost'), ('expired', 'Expired'), ('cancelled', 'Cancelled')], default='ready', max_length=40)), + ('printing_date', models.DateField(blank=True, null=True)), + ('mailing_date', models.DateField(blank=True, null=True)), + ('approval', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stickers', to='mooringlicensing.approval')), + ('dcv_permit', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stickers', to='mooringlicensing.dcvpermit')), + ('fee_constructor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeconstructor')), + ('fee_season', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeseason')), + ('proposal_initiated', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposal')), ], options={ - 'verbose_name': 'Application Assessor Group', - 'verbose_name_plural': 'Application Assessor Group', + 'ordering': ['-number'], }, ), migrations.CreateModel( - name='ProposalDeclinedDetails', + name='StickerActionFee', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reason', models.TextField(blank=True)), - ('cc_email', models.TextField(null=True)), - ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal')), + ('send_invoice', models.BooleanField(default=False)), + ('confirmation_sent', models.BooleanField(default=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('expiry_time', models.DateTimeField(auto_now_add=True, null=True)), + ('payment_type', models.SmallIntegerField(choices=[(0, 'Internet booking'), (1, 'Reception booking'), (2, 'Black booking'), (3, 'Temporary reservation')], default=0)), + ('cost', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('created_by', models.IntegerField(blank=True, null=True)), + ('invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), ], ), migrations.CreateModel( - name='ProposalDocument', + name='StickerPrintedContact', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_doc_filename)), - ('input_name', models.CharField(blank=True, max_length=255, null=True)), - ('can_delete', models.BooleanField(default=True)), - ('can_hide', models.BooleanField(default=False)), - ('hidden', models.BooleanField(default=False)), - ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Proposal')), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('type', models.CharField(choices=[('to', 'To'), ('cc', 'Cc'), ('bcc', 'Bcc')], max_length=255)), + ('enabled', models.BooleanField(default=True)), ], - options={ - 'verbose_name': 'Application Document', - }, ), migrations.CreateModel( - name='ProposalLogDocument', + name='StickerPrintingBatch', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), ('description', models.TextField(blank=True, verbose_name='description')), ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_comms_log_filename)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_sticker_doc_filename)), + ('emailed_datetime', models.DateTimeField(blank=True, null=True)), ], ), migrations.CreateModel( - name='ProposalOnHold', + name='StickerPrintingContact', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('comment', models.TextField(blank=True)), - ('documents', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='onhold_documents', to='mooringlicensing.ProposalDocument')), - ('officer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal')), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('type', models.CharField(choices=[('to', 'To'), ('cc', 'Cc'), ('bcc', 'Bcc')], max_length=255)), + ('enabled', models.BooleanField(default=True)), ], ), migrations.CreateModel( - name='ProposalRequest', + name='StickerPrintingResponseEmail', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('subject', models.CharField(blank=True, max_length=200)), - ('text', models.TextField(blank=True)), + ('email_subject', models.CharField(blank=True, max_length=255, null=True)), + ('email_body', models.TextField(blank=True, null=True)), + ('email_date', models.CharField(blank=True, max_length=255, null=True)), + ('email_from', models.CharField(blank=True, max_length=255, null=True)), + ('email_message_id', models.CharField(blank=True, max_length=255, null=True)), ], ), migrations.CreateModel( - name='ProposalRequirement', + name='SystemMaintenance', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveIntegerField(db_index=True, editable=False)), - ('free_requirement', models.TextField(blank=True, null=True)), - ('standard', models.BooleanField(default=True)), - ('due_date', models.DateField(blank=True, null=True)), - ('recurrence', models.BooleanField(default=False)), - ('recurrence_pattern', models.SmallIntegerField(choices=[(1, 'Weekly'), (2, 'Monthly'), (3, 'Yearly')], default=1)), - ('recurrence_schedule', models.IntegerField(blank=True, null=True)), - ('is_deleted', models.BooleanField(default=False)), - ('copied_for_renewal', models.BooleanField(default=False)), - ('require_due_date', models.BooleanField(default=False)), - ('copied_from', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.ProposalRequirement')), - ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirements', to='mooringlicensing.Proposal')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField()), + ('start_date', models.DateTimeField()), + ('end_date', models.DateTimeField()), ], + options={ + 'verbose_name_plural': 'System maintenance', + }, ), migrations.CreateModel( - name='ProposalStandardRequirement', + name='TemporaryDocumentCollection', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('text', models.TextField()), - ('code', models.CharField(max_length=10, unique=True)), - ('obsolete', models.BooleanField(default=False)), - ('participant_number_required', models.BooleanField(default=False)), - ('default', models.BooleanField(default=False)), - ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), ], - options={ - 'verbose_name': 'Application Standard Requirement', - 'verbose_name_plural': 'Application Standard Requirements', - }, ), migrations.CreateModel( - name='ProposalUserAction', + name='UserSystemSettings', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('when', models.DateTimeField(auto_now_add=True)), - ('what', models.TextField()), - ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Proposal')), - ('who', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('user', models.IntegerField(unique=True)), ], options={ - 'ordering': ('-when',), + 'verbose_name_plural': 'User System Settings', }, ), migrations.CreateModel( - name='Question', + name='Vessel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('question_text', models.TextField()), - ('answer_one', models.CharField(blank=True, max_length=200)), - ('answer_two', models.CharField(blank=True, max_length=200)), - ('answer_three', models.CharField(blank=True, max_length=200)), - ('answer_four', models.CharField(blank=True, max_length=200)), - ('correct_answer', models.CharField(choices=[('answer_one', 'Answer one'), ('answer_two', 'Answer two'), ('answer_three', 'Answer three'), ('answer_four', 'Answer four')], default='answer_one', max_length=40, verbose_name='Correct Answer')), - ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ApplicationType')), + ('rego_no', models.CharField(max_length=200, unique=True)), ], + options={ + 'verbose_name_plural': 'Vessels', + }, ), migrations.CreateModel( - name='RequirementDocument', + name='VesselOwnership', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), - ('description', models.TextField(blank=True, verbose_name='description')), - ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_requirement_doc_filename)), - ('input_name', models.CharField(blank=True, max_length=255, null=True)), - ('can_delete', models.BooleanField(default=True)), - ('visible', models.BooleanField(default=True)), - ('requirement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirement_documents', to='mooringlicensing.ProposalRequirement')), + ('percentage', models.IntegerField(blank=True, null=True)), + ('start_date', models.DateTimeField(default=django.utils.timezone.now)), + ('end_date', models.DateField(blank=True, null=True)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('updated', models.DateTimeField(auto_now=True)), + ('exported', models.BooleanField(default=False)), + ('dot_name', models.CharField(blank=True, max_length=200, null=True)), + ('company_ownership', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.companyownership')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.owner')), + ('vessel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.vessel')), ], options={ - 'abstract': False, + 'verbose_name_plural': 'Vessel Details Ownership', }, ), migrations.CreateModel( - name='SystemMaintenance', + name='VesselSizeCategoryGroup', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), - ('description', models.TextField()), - ('start_date', models.DateTimeField()), - ('end_date', models.DateTimeField()), ], options={ - 'verbose_name_plural': 'System maintenance', + 'verbose_name_plural': 'Vessel Size Category Group', }, ), migrations.CreateModel( - name='TemporaryDocument', + name='AnnualAdmissionApplication', + fields=[ + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposal')), + ], + bases=('mooringlicensing.proposal',), + ), + migrations.CreateModel( + name='AnnualAdmissionPermit', + fields=[ + ('approval', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='AuthorisedUserApplication', + fields=[ + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposal')), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)), + ], + bases=('mooringlicensing.proposal',), + ), + migrations.CreateModel( + name='AuthorisedUserPermit', + fields=[ + ('approval', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='ComplianceRequest', + fields=[ + ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposalrequest')), + ('reason', models.CharField(choices=[('outstanding', 'There are currently outstanding returns for the previous licence'), ('other', 'Other')], default='outstanding', max_length=30, verbose_name='Reason')), + ], + bases=('mooringlicensing.proposalrequest',), + ), + migrations.CreateModel( + name='MooringLicence', + fields=[ + ('approval', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='MooringLicenceApplication', + fields=[ + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposal')), + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False)), + ], + bases=('mooringlicensing.proposal',), + ), + migrations.CreateModel( + name='PreviewTempApproval', + fields=[ + ('approval_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='WaitingListAllocation', + fields=[ + ('approval', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.approval',), + ), + migrations.CreateModel( + name='WaitingListApplication', + fields=[ + ('proposal', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposal')), + ], + bases=('mooringlicensing.proposal',), + ), + migrations.CreateModel( + name='WrittenProofDocument', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), ('description', models.TextField(blank=True, verbose_name='description')), ('uploaded_date', models.DateTimeField(auto_now_add=True)), - ('_file', models.FileField(max_length=255, upload_to='')), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='written_proof_documents', to='mooringlicensing.proposal')), ], + options={ + 'verbose_name': 'Written Proof Document', + }, ), migrations.CreateModel( - name='TemporaryDocumentCollection', + name='WaitingListOfferDocument', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='waiting_list_offer_documents', to='mooringlicensing.approval')), ], + options={ + 'verbose_name': 'Waiting List Offer Documents', + }, ), migrations.CreateModel( - name='UserDelegation', + name='VesselSizeCategory', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Organisation')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('name', models.CharField(max_length=100)), + ('start_size', models.DecimalField(decimal_places=2, default='0.00', help_text='unit [m]', max_digits=8)), + ('include_start_size', models.BooleanField(default=True)), + ('null_vessel', models.BooleanField(default=False)), + ('vessel_size_category_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vessel_size_categories', to='mooringlicensing.vesselsizecategorygroup')), ], + options={ + 'verbose_name_plural': 'Vessel Size Categories', + }, ), migrations.CreateModel( - name='UserSystemSettings', + name='VesselRegistrationDocument', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='system_settings', to=settings.AUTH_USER_MODEL)), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('vessel_ownership', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vessel_registration_documents', to='mooringlicensing.vesselownership')), ], options={ - 'verbose_name_plural': 'User System Settings', + 'verbose_name': 'Vessel Registration Papers', }, ), migrations.CreateModel( - name='Vessel', + name='VesselOwnershipOnApproval', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('nominated_vessel', models.CharField(blank=True, max_length=200)), - ('spv_no', models.CharField(blank=True, max_length=200)), - ('hire_rego', models.CharField(blank=True, max_length=200)), - ('craft_no', models.CharField(blank=True, max_length=200)), - ('size', models.CharField(blank=True, max_length=200)), - ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='vessels', to='mooringlicensing.Proposal')), + ('end_date', models.DateField(blank=True, null=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.approval')), + ('vessel_ownership', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.vesselownership')), ], + options={ + 'unique_together': {('vessel_ownership', 'approval')}, + }, ), migrations.CreateModel( - name='AmendmentRequest', + name='VesselDetails', fields=[ - ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), - ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), - ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.AmendmentReason')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('vessel_type', models.CharField(choices=[('yacht', 'Yacht'), ('cabin_cruiser', 'Cabin Cruiser'), ('tender', 'Tender'), ('other', 'Other')], max_length=20)), + ('vessel_name', models.CharField(max_length=400)), + ('vessel_length', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_draft', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_beam', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('vessel_weight', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('berth_mooring', models.CharField(blank=True, max_length=200)), + ('created', models.DateTimeField(default=django.utils.timezone.now)), + ('updated', models.DateTimeField(auto_now=True)), + ('exported', models.BooleanField(default=False)), + ('vessel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.vessel')), ], - bases=('mooringlicensing.proposalrequest',), + options={ + 'verbose_name_plural': 'Vessel Details', + }, ), - migrations.CreateModel( - name='ApprovalLogEntry', - fields=[ - ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), - ], - bases=('mooringlicensing.communicationslogentry',), + migrations.AddField( + model_name='vessel', + name='blocking_owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blocked_vessel', to='mooringlicensing.vesselownership'), ), migrations.CreateModel( - name='Assessment', + name='TemporaryDocument', fields=[ - ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), - ('status', models.CharField(choices=[('awaiting_assessment', 'Awaiting Assessment'), ('assessed', 'Assessed'), ('assessment_expired', 'Assessment Period Expired')], default='awaiting_assessment', max_length=20, verbose_name='Status')), - ('date_last_reminded', models.DateField(blank=True, null=True)), - ('comment', models.TextField(blank=True)), - ('purpose', models.TextField(blank=True)), - ('assigned_assessor', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=255, upload_to='')), + ('temp_document_collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.temporarydocumentcollection')), ], - bases=('mooringlicensing.proposalrequest',), ), migrations.CreateModel( - name='ComplianceAmendmentRequest', + name='StickerPrintingResponse', fields=[ - ('comprequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CompRequest')), - ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), - ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ComplianceAmendmentReason')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_sticker_response_doc_filename)), + ('processed', models.BooleanField(default=False)), + ('no_errors_when_process', models.NullBooleanField(default=None)), + ('sticker_printing_response_email', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.stickerprintingresponseemail')), ], - bases=('mooringlicensing.comprequest',), ), migrations.CreateModel( - name='ComplianceLogEntry', + name='StickerActionDetail', fields=[ - ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.TextField(blank=True)), + ('date_created', models.DateTimeField(auto_now_add=True, null=True)), + ('date_updated', models.DateTimeField(auto_now=True, null=True)), + ('date_of_lost_sticker', models.DateField(blank=True, null=True)), + ('date_of_returned_sticker', models.DateField(blank=True, null=True)), + ('action', models.CharField(blank=True, max_length=50, null=True)), + ('user', models.IntegerField(blank=True, null=True)), + ('sticker', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sticker_action_details', to='mooringlicensing.sticker')), + ('sticker_action_fee', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sticker_action_details', to='mooringlicensing.stickeractionfee')), ], - bases=('mooringlicensing.communicationslogentry',), + options={ + 'ordering': ['-date_created'], + }, + ), + migrations.AddField( + model_name='sticker', + name='sticker_printing_batch', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.stickerprintingbatch'), + ), + migrations.AddField( + model_name='sticker', + name='sticker_printing_response', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.stickerprintingresponse'), + ), + migrations.AddField( + model_name='sticker', + name='sticker_to_replace', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.sticker'), + ), + migrations.AddField( + model_name='sticker', + name='vessel_ownership', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesselownership'), ), migrations.CreateModel( - name='ComplianceRequest', + name='SignedLicenceAgreementDocument', fields=[ - ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.ProposalRequest')), - ('reason', models.CharField(choices=[('outstanding', 'There are currently outstanding returns for the previous licence'), ('other', 'Other')], default='outstanding', max_length=30, verbose_name='Reason')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='signed_licence_agreement_documents', to='mooringlicensing.proposal')), ], - bases=('mooringlicensing.proposalrequest',), + options={ + 'verbose_name': 'Signed Licence Agreement', + }, ), migrations.CreateModel( - name='OrganisationLogEntry', + name='RequirementDocument', fields=[ - ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_requirement_doc_filename)), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('visible', models.BooleanField(default=True)), + ('requirement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirement_documents', to='mooringlicensing.proposalrequirement')), ], - bases=('mooringlicensing.communicationslogentry',), + options={ + 'abstract': False, + }, ), migrations.CreateModel( - name='OrganisationRequestLogEntry', + name='RenewalDocument', fields=[ - ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), - ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.OrganisationRequest')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(upload_to=mooringlicensing.components.approvals.models.update_approval_doc_filename)), + ('can_delete', models.BooleanField(default=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='renewal_documents', to='mooringlicensing.approval')), ], - bases=('mooringlicensing.communicationslogentry',), ), migrations.CreateModel( - name='PreviewTempApproval', + name='ProposalUserAction', fields=[ - ('approval_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.Approval')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField(blank=True, null=True)), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.proposal')), ], - bases=('mooringlicensing.approval',), + options={ + 'ordering': ('-when',), + }, ), migrations.CreateModel( - name='ProposalLogEntry', + name='ProposalStandardRequirement', fields=[ - ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.CommunicationsLogEntry')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.TextField()), + ('code', models.CharField(max_length=10, unique=True)), + ('obsolete', models.BooleanField(default=False)), + ('participant_number_required', models.BooleanField(default=False)), + ('default', models.BooleanField(default=False)), + ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.applicationtype')), ], - bases=('mooringlicensing.communicationslogentry',), - ), - migrations.AddField( - model_name='temporarydocument', - name='temp_document_collection', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.TemporaryDocumentCollection'), + options={ + 'verbose_name': 'Application Standard Requirement', + 'verbose_name_plural': 'Application Standard Requirements', + }, ), migrations.AddField( model_name='proposalrequirement', name='standard_requirement', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.ProposalStandardRequirement'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposalstandardrequirement'), ), - migrations.AddField( - model_name='proposalrequest', - name='officer', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + migrations.CreateModel( + name='ProposalDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_doc_filename)), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Application Document', + }, ), - migrations.AddField( - model_name='proposalrequest', - name='proposal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proposalrequest_set', to='mooringlicensing.Proposal'), + migrations.CreateModel( + name='ProposalDeclinedDetails', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('officer', models.IntegerField(blank=True, null=True)), + ('reason', models.TextField(blank=True)), + ('cc_email', models.TextField(null=True)), + ('proposal', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposal')), + ], ), migrations.AddField( model_name='proposal', - name='approval', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Approval'), + name='approval_level_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='approval_level_document', to='mooringlicensing.proposaldocument'), ), migrations.AddField( model_name='proposal', - name='approval_level_document', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approval_level_document', to='mooringlicensing.ProposalDocument'), + name='fee_season', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeseason'), ), migrations.AddField( model_name='proposal', - name='assigned_approver', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mooringlicensing_proposals_approvals', to=settings.AUTH_USER_MODEL), + name='listed_moorings', + field=models.ManyToManyField(related_name='listed_on_proposals', to='mooringlicensing.Mooring'), ), migrations.AddField( model_name='proposal', - name='assigned_officer', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mooringlicensing_proposals_assigned', to=settings.AUTH_USER_MODEL), + name='listed_vessels', + field=models.ManyToManyField(related_name='listed_on_proposals', to='mooringlicensing.VesselOwnership'), ), migrations.AddField( model_name='proposal', - name='org_applicant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='org_applications', to='mooringlicensing.Organisation'), + name='mooring', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.mooring'), ), migrations.AddField( model_name='proposal', - name='previous_application', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Proposal'), + name='org_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='org_applications', to='mooringlicensing.organisation'), ), migrations.AddField( model_name='proposal', - name='proxy_applicant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_proxy', to=settings.AUTH_USER_MODEL), + name='preferred_bay', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.mooringbay'), ), migrations.AddField( model_name='proposal', - name='submitter', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_proposals', to=settings.AUTH_USER_MODEL), + name='previous_application', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='succeeding_proposals', to='mooringlicensing.proposal'), ), migrations.AddField( - model_name='organisation', - name='delegates', - field=models.ManyToManyField(blank=True, related_name='mooringlicensing_organisations', through='mooringlicensing.UserDelegation', to=settings.AUTH_USER_MODEL), + model_name='proposal', + name='proposal_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposaltype'), ), migrations.AddField( - model_name='organisation', - name='organisation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.Organisation'), + model_name='proposal', + name='vessel_details', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesseldetails'), ), migrations.AddField( - model_name='comprequest', - name='compliance', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Compliance'), + model_name='proposal', + name='vessel_ownership', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesselownership'), ), - migrations.AddField( - model_name='comprequest', - name='officer', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + migrations.CreateModel( + name='ProofOfIdentityDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proof_of_identity_documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Proof Of Identity', + }, ), migrations.AddField( - model_name='compliance', - name='approval', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='compliances', to='mooringlicensing.Approval'), + model_name='owner', + name='vessels', + field=models.ManyToManyField(through='mooringlicensing.VesselOwnership', to='mooringlicensing.Vessel'), + ), + migrations.CreateModel( + name='OrganisationRequestUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField()), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.organisationrequest')), + ], + ), + migrations.CreateModel( + name='OrganisationRequestDeclinedDetails', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('officer', models.IntegerField()), + ('reason', models.TextField(blank=True)), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.organisationrequest')), + ], + ), + migrations.CreateModel( + name='OrganisationAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField()), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.organisation')), + ], + ), + migrations.CreateModel( + name='OracleCodeItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(blank=True, default='T1 EXEMPT', max_length=50, null=True)), + ('date_of_enforcement', models.DateField(blank=True, null=True)), + ('application_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='oracle_code_items', to='mooringlicensing.applicationtype')), + ], + ), + migrations.CreateModel( + name='NumberOfPeople', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number', models.PositiveSmallIntegerField(blank=True, default=0, null=True)), + ('admission_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.admissiontype')), + ('age_group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.agegroup')), + ('dcv_admission_arrival', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.dcvadmissionarrival')), + ], + ), + migrations.CreateModel( + name='NumberOfDaysSetting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('number_of_days', models.PositiveSmallIntegerField(blank=True, null=True)), + ('date_of_enforcement', models.DateField(blank=True, null=True)), + ('number_of_days_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='settings', to='mooringlicensing.numberofdaystype')), + ], + options={ + 'ordering': ['-date_of_enforcement'], + }, + ), + migrations.CreateModel( + name='MooringUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField()), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('mooring', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.mooring')), + ], + options={ + 'ordering': ('-when',), + }, + ), + migrations.CreateModel( + name='MooringReportDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mooring_report_documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Mooring Report Document', + }, + ), + migrations.CreateModel( + name='MooringOnApproval', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('site_licensee', models.BooleanField()), + ('end_date', models.DateField(blank=True, null=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.approval')), + ('mooring', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.mooring')), + ('sticker', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.sticker')), + ], + options={ + 'unique_together': {('mooring', 'approval')}, + }, ), migrations.AddField( - model_name='compliance', - name='assigned_to', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_compliance_assignments', to=settings.AUTH_USER_MODEL), + model_name='mooring', + name='mooring_bay', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.mooringbay'), + ), + migrations.CreateModel( + name='InsuranceCertificateDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='insurance_certificate_documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Insurance Certificate Documents', + }, + ), + migrations.CreateModel( + name='HullIdentificationNumberDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='hull_identification_number_documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Hull Identification Number Documents', + }, + ), + migrations.CreateModel( + name='HelpPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', ckeditor.fields.RichTextField()), + ('description', models.CharField(blank=True, max_length=256, null=True)), + ('help_type', models.SmallIntegerField(choices=[(1, 'External'), (2, 'Internal')], default=1, verbose_name='Help Type')), + ('version', models.SmallIntegerField(default=1)), + ], + options={ + 'unique_together': {('help_type', 'version')}, + }, + ), + migrations.CreateModel( + name='FeePeriod', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, default='', max_length=50, null=True)), + ('start_date', models.DateField(blank=True, null=True)), + ('fee_season', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fee_periods', to='mooringlicensing.feeseason')), + ], + options={ + 'ordering': ['start_date'], + }, + ), + migrations.CreateModel( + name='FeeItemApplicationFee', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount_to_be_paid', models.DecimalField(blank=True, decimal_places=2, default=None, max_digits=8, null=True)), + ('amount_paid', models.DecimalField(blank=True, decimal_places=2, default=None, max_digits=8, null=True)), + ('application_fee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.applicationfee')), + ('fee_item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.feeitem')), + ('vessel_details', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesseldetails')), + ], + ), + migrations.AddField( + model_name='feeitem', + name='fee_period', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeperiod'), + ), + migrations.AddField( + model_name='feeitem', + name='proposal_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposaltype'), + ), + migrations.AddField( + model_name='feeitem', + name='vessel_size_category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesselsizecategory'), + ), + migrations.AddField( + model_name='feeconstructor', + name='fee_season', + field=smart_selects.db_fields.ChainedForeignKey(auto_choose=True, blank=True, chained_field='application_type', chained_model_field='application_type', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fee_constructors', to='mooringlicensing.feeseason'), + ), + migrations.AddField( + model_name='feeconstructor', + name='vessel_size_category_group', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='fee_constructors', to='mooringlicensing.vesselsizecategorygroup'), + ), + migrations.CreateModel( + name='ElectoralRollDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to='')), + ('input_name', models.CharField(blank=True, max_length=255, null=True)), + ('can_delete', models.BooleanField(default=True)), + ('can_hide', models.BooleanField(default=False)), + ('hidden', models.BooleanField(default=False)), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='electoral_roll_documents', to='mooringlicensing.proposal')), + ], + options={ + 'verbose_name': 'Electoral Roll Document', + }, + ), + migrations.CreateModel( + name='DcvVessel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rego_no', models.CharField(blank=True, max_length=200, null=True, unique=True)), + ('vessel_name', models.CharField(blank=True, max_length=400)), + ('dcv_organisation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.dcvorganisation')), + ], + ), + migrations.CreateModel( + name='DcvPermitFee', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('send_invoice', models.BooleanField(default=False)), + ('confirmation_sent', models.BooleanField(default=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('expiry_time', models.DateTimeField(auto_now_add=True, null=True)), + ('payment_type', models.SmallIntegerField(choices=[(0, 'Internet booking'), (1, 'Reception booking'), (2, 'Black booking'), (3, 'Temporary reservation')], default=0)), + ('cost', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('created_by', models.IntegerField(blank=True, null=True)), + ('invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), + ('dcv_permit', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dcv_permit_fees', to='mooringlicensing.dcvpermit')), + ('fee_items', models.ManyToManyField(related_name='dcv_permit_fees', to='mooringlicensing.FeeItem')), + ], + ), + migrations.CreateModel( + name='DcvPermitDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_dcv_permit_doc_filename)), + ('can_delete', models.BooleanField(default=False)), + ('dcv_permit', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='permits', to='mooringlicensing.dcvpermit')), + ], + ), + migrations.AddField( + model_name='dcvpermit', + name='dcv_vessel', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dcv_permits', to='mooringlicensing.dcvvessel'), + ), + migrations.AddField( + model_name='dcvpermit', + name='fee_season', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dcv_permits', to='mooringlicensing.feeseason'), + ), + migrations.CreateModel( + name='DcvAdmissionFee', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('send_invoice', models.BooleanField(default=False)), + ('confirmation_sent', models.BooleanField(default=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('expiry_time', models.DateTimeField(auto_now_add=True, null=True)), + ('payment_type', models.SmallIntegerField(choices=[(0, 'Internet booking'), (1, 'Reception booking'), (2, 'Black booking'), (3, 'Temporary reservation')], default=0)), + ('cost', models.DecimalField(decimal_places=2, default='0.00', max_digits=8)), + ('created_by', models.IntegerField(blank=True, null=True)), + ('invoice_reference', models.CharField(blank=True, default='', max_length=50, null=True)), + ('dcv_admission', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dcv_admission_fees', to='mooringlicensing.dcvadmission')), + ('fee_items', models.ManyToManyField(related_name='dcv_admission_fees', to='mooringlicensing.FeeItem')), + ], + ), + migrations.CreateModel( + name='DcvAdmissionDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_dcv_admission_doc_filename)), + ('can_delete', models.BooleanField(default=False)), + ('dcv_admission', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='admissions', to='mooringlicensing.dcvadmission')), + ], + ), + migrations.AddField( + model_name='dcvadmissionarrival', + name='fee_constructor', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dcv_admission_arrivals', to='mooringlicensing.feeconstructor'), + ), + migrations.AddField( + model_name='dcvadmissionarrival', + name='fee_season', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.feeseason'), + ), + migrations.AddField( + model_name='dcvadmission', + name='dcv_vessel', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dcv_admissions', to='mooringlicensing.dcvvessel'), + ), + migrations.CreateModel( + name='ComplianceUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField()), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.compliance')), + ], + ), + migrations.CreateModel( + name='ComplianceDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_proposal_complaince_filename)), + ('can_delete', models.BooleanField(default=True)), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.compliance')), + ], ), migrations.AddField( model_name='compliance', name='proposal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='compliances', to='mooringlicensing.Proposal'), + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='compliances', to='mooringlicensing.proposal'), ), migrations.AddField( model_name='compliance', name='requirement', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='compliance_requirement', to='mooringlicensing.ProposalRequirement'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='compliance_requirement', to='mooringlicensing.proposalrequirement'), ), migrations.AddField( - model_name='compliance', - name='submitter', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mooringlicensing_compliances', to=settings.AUTH_USER_MODEL), + model_name='companyownership', + name='blocking_proposal', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.proposal'), ), migrations.AddField( - model_name='communicationslogentry', - name='customer', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL), + model_name='companyownership', + name='company', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.company'), ), migrations.AddField( - model_name='communicationslogentry', - name='staff', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL), + model_name='companyownership', + name='vessel', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.vessel'), ), migrations.AddField( - model_name='approvaluseraction', - name='approval', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='action_logs', to='mooringlicensing.Approval'), + model_name='company', + name='vessels', + field=models.ManyToManyField(through='mooringlicensing.CompanyOwnership', to='mooringlicensing.Vessel'), ), - migrations.AddField( - model_name='approvaluseraction', - name='who', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + migrations.CreateModel( + name='AuthorisedUserSummaryDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_approval_doc_filename)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='authorised_user_summary_documents', to='mooringlicensing.approval')), + ], + ), + migrations.CreateModel( + name='ApprovalUserAction', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('who', models.IntegerField(blank=True, null=True)), + ('when', models.DateTimeField(auto_now_add=True)), + ('what', models.TextField()), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='action_logs', to='mooringlicensing.approval')), + ], + options={ + 'ordering': ('-when',), + }, + ), + migrations.CreateModel( + name='ApprovalHistory', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.CharField(blank=True, max_length=100, null=True)), + ('start_date', models.DateTimeField()), + ('end_date', models.DateTimeField(blank=True, null=True)), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.approval')), + ('approval_letter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.approvaldocument')), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approval_history_records', to='mooringlicensing.proposal')), + ('stickers', models.ManyToManyField(to='mooringlicensing.Sticker')), + ('vessel_ownership', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.vesselownership')), + ], + options={ + 'ordering': ['-id'], + }, ), migrations.AddField( - model_name='approvaldocument', - name='approval', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.Approval'), + model_name='approval', + name='authorised_user_summary_document', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='approvals', to='mooringlicensing.authorisedusersummarydocument'), ), migrations.AddField( model_name='approval', name='cover_letter_document', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cover_letter_document', to='mooringlicensing.ApprovalDocument'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='cover_letter_document', to='mooringlicensing.approvaldocument'), ), migrations.AddField( model_name='approval', name='current_proposal', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='approvals', to='mooringlicensing.Proposal'), + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='approvals', to='mooringlicensing.proposal'), ), migrations.AddField( model_name='approval', name='licence_document', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='licence_document', to='mooringlicensing.ApprovalDocument'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='licence_document', to='mooringlicensing.approvaldocument'), ), migrations.AddField( model_name='approval', - name='org_applicant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='org_approvals', to='mooringlicensing.Organisation'), + name='moorings', + field=models.ManyToManyField(through='mooringlicensing.MooringOnApproval', to='mooringlicensing.Mooring'), ), migrations.AddField( model_name='approval', - name='proxy_applicant', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='proxy_approvals', to=settings.AUTH_USER_MODEL), + name='org_applicant', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='org_approvals', to='mooringlicensing.organisation'), ), migrations.AddField( model_name='approval', name='renewal_document', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='renewal_document', to='mooringlicensing.ApprovalDocument'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='renewal_document', to='mooringlicensing.renewaldocument'), ), migrations.AddField( model_name='approval', name='replaced_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.Approval'), + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='replace', to='mooringlicensing.approval'), ), migrations.AddField( model_name='approval', - name='submitter', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='mooringlicensing_approvals', to=settings.AUTH_USER_MODEL), + name='vessel_ownerships', + field=models.ManyToManyField(through='mooringlicensing.VesselOwnershipOnApproval', to='mooringlicensing.VesselOwnership'), ), - migrations.AlterUniqueTogether( - name='userdelegation', - unique_together=set([('organisation', 'user')]), + migrations.AddField( + model_name='applicationfee', + name='fee_items', + field=models.ManyToManyField(related_name='application_fees', through='mooringlicensing.FeeItemApplicationFee', to='mooringlicensing.FeeItem'), ), migrations.AddField( - model_name='proposallogentry', + model_name='applicationfee', name='proposal', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Proposal'), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='application_fees', to='mooringlicensing.proposal'), ), - migrations.AddField( - model_name='proposallogdocument', - name='log_entry', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ProposalLogEntry'), + migrations.CreateModel( + name='VesselLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('vessel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.vessel')), + ], + bases=('mooringlicensing.communicationslogentry',), ), - migrations.AlterUniqueTogether( - name='proposalassessment', - unique_together=set([('proposal',)]), + migrations.CreateModel( + name='VesselLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_vessel_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.vessellogentry')), + ], ), - migrations.AddField( - model_name='organisationrequestlogdocument', - name='log_entry', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.OrganisationRequestLogEntry'), + migrations.CreateModel( + name='UserDelegation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.IntegerField()), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mooringlicensing.organisation')), + ], + options={ + 'unique_together': {('organisation', 'user')}, + }, ), - migrations.AddField( - model_name='organisationlogentry', - name='organisation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Organisation'), + migrations.CreateModel( + name='ProposalLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('proposal', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.proposal')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='ProposalLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_proposal_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.proposallogentry')), + ], ), migrations.AddField( - model_name='organisationlogdocument', - name='log_entry', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.OrganisationLogEntry'), + model_name='proposal', + name='waiting_list_allocation', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ria_generated_proposal', to='mooringlicensing.waitinglistallocation'), ), - migrations.AlterUniqueTogether( - name='organisationcontact', - unique_together=set([('organisation', 'email')]), + migrations.CreateModel( + name='OrganisationRequestLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('request', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.organisationrequest')), + ], + bases=('mooringlicensing.communicationslogentry',), ), - migrations.AlterUniqueTogether( - name='helppage', - unique_together=set([('application_type', 'help_type', 'version')]), + migrations.CreateModel( + name='OrganisationRequestLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_request_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.organisationrequestlogentry')), + ], ), - migrations.AddField( - model_name='compliancelogentry', - name='compliance', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Compliance'), + migrations.CreateModel( + name='OrganisationLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.organisation')), + ], + bases=('mooringlicensing.communicationslogentry',), ), - migrations.AddField( - model_name='compliancelogdocument', - name='log_entry', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ComplianceLogEntry'), + migrations.CreateModel( + name='OrganisationLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.organisations.models.update_organisation_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.organisationlogentry')), + ], ), - migrations.AddField( - model_name='approvallogentry', - name='approval', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.Approval'), + migrations.CreateModel( + name='OrganisationContact', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user_status', models.CharField(choices=[('draft', 'Draft'), ('pending', 'Pending'), ('active', 'Active'), ('declined', 'Declined'), ('unlinked', 'Unlinked'), ('suspended', 'Suspended')], default='draft', max_length=40, verbose_name='Status')), + ('user_role', models.CharField(choices=[('organisation_admin', 'Organisation Admin'), ('organisation_user', 'Organisation User'), ('consultant', 'Consultant')], default='organisation_user', max_length=40, verbose_name='Role')), + ('is_admin', models.BooleanField(default=False)), + ('email', models.EmailField(max_length=254)), + ('first_name', models.CharField(max_length=128, verbose_name='Given name(s)')), + ('last_name', models.CharField(max_length=128)), + ('phone_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='phone number')), + ('mobile_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='mobile number')), + ('fax_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='fax number')), + ('organisation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='mooringlicensing.organisation')), + ], + options={ + 'unique_together': {('organisation', 'email')}, + }, + ), + migrations.CreateModel( + name='MooringLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('mooring', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.mooring')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='MooringLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.proposals.models.update_mooring_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.mooringlogentry')), + ], ), migrations.AddField( - model_name='approvallogdocument', - name='log_entry', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.ApprovalLogEntry'), + model_name='mooring', + name='mooring_licence', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mooring', to='mooringlicensing.mooringlicence'), + ), + migrations.CreateModel( + name='ComplianceLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('compliance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.compliance')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='ComplianceLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, upload_to=mooringlicensing.components.compliances.models.update_compliance_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='documents', to='mooringlicensing.compliancelogentry')), + ], + ), + migrations.CreateModel( + name='ComplianceAmendmentRequest', + fields=[ + ('comprequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.comprequest')), + ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), + ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.complianceamendmentreason')), + ], + bases=('mooringlicensing.comprequest',), + ), + migrations.CreateModel( + name='ApprovalLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('approval', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comms_logs', to='mooringlicensing.approval')), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + migrations.CreateModel( + name='ApprovalLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(max_length=512, null=True, upload_to=mooringlicensing.components.approvals.models.update_approval_comms_log_filename)), + ('log_entry', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.approvallogentry')), + ], ), migrations.AlterUniqueTogether( name='approval', - unique_together=set([('lodgement_number', 'issue_date')]), + unique_together={('lodgement_number', 'issue_date')}, + ), + migrations.CreateModel( + name='AmendmentRequest', + fields=[ + ('proposalrequest_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.proposalrequest')), + ('status', models.CharField(choices=[('requested', 'Requested'), ('amended', 'Amended')], default='requested', max_length=30, verbose_name='Status')), + ('reason', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mooringlicensing.amendmentreason')), + ], + bases=('mooringlicensing.proposalrequest',), ), ] diff --git a/mooringlicensing/migrations/0002_applicationfee_uuid.py b/mooringlicensing/migrations/0002_applicationfee_uuid.py new file mode 100644 index 000000000..be4df6f81 --- /dev/null +++ b/mooringlicensing/migrations/0002_applicationfee_uuid.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-01-11 06:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='applicationfee', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0003_feecalculation.py b/mooringlicensing/migrations/0003_feecalculation.py new file mode 100644 index 000000000..a25620355 --- /dev/null +++ b/mooringlicensing/migrations/0003_feecalculation.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.16 on 2023-01-18 02:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0002_applicationfee_uuid'), + ] + + operations = [ + migrations.CreateModel( + name='FeeCalculation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('uuid', models.CharField(blank=True, max_length=36, null=True)), + ('data', models.JSONField(blank=True, null=True)), + ], + ), + ] diff --git a/mooringlicensing/migrations/0004_auto_20230203_1034.py b/mooringlicensing/migrations/0004_auto_20230203_1034.py new file mode 100644 index 000000000..c6b4e43fe --- /dev/null +++ b/mooringlicensing/migrations/0004_auto_20230203_1034.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.16 on 2023-02-03 02:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0003_feecalculation'), + ] + + operations = [ + migrations.AlterField( + model_name='complianceuseraction', + name='who', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='mooringuseraction', + name='who', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='organisationaction', + name='who', + field=models.IntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name='organisationrequestuseraction', + name='who', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0005_auto_20230207_1452.py b/mooringlicensing/migrations/0005_auto_20230207_1452.py new file mode 100644 index 000000000..b0c632e18 --- /dev/null +++ b/mooringlicensing/migrations/0005_auto_20230207_1452.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.16 on 2023-02-07 06:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0004_auto_20230203_1034'), + ] + + operations = [ + migrations.AddField( + model_name='dcvadmission', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + migrations.AddField( + model_name='dcvpermit', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0006_auto_20230207_1456.py b/mooringlicensing/migrations/0006_auto_20230207_1456.py new file mode 100644 index 000000000..9c8092586 --- /dev/null +++ b/mooringlicensing/migrations/0006_auto_20230207_1456.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.16 on 2023-02-07 06:56 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0005_auto_20230207_1452'), + ] + + operations = [ + migrations.RemoveField( + model_name='dcvadmission', + name='uuid', + ), + migrations.RemoveField( + model_name='dcvpermit', + name='uuid', + ), + ] diff --git a/mooringlicensing/migrations/0007_auto_20230207_1458.py b/mooringlicensing/migrations/0007_auto_20230207_1458.py new file mode 100644 index 000000000..e6d6ee237 --- /dev/null +++ b/mooringlicensing/migrations/0007_auto_20230207_1458.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.16 on 2023-02-07 06:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0006_auto_20230207_1456'), + ] + + operations = [ + migrations.AddField( + model_name='dcvadmissionfee', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + migrations.AddField( + model_name='dcvpermitfee', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0008_stickeractionfee_uuid.py b/mooringlicensing/migrations/0008_stickeractionfee_uuid.py new file mode 100644 index 000000000..ec5755a5a --- /dev/null +++ b/mooringlicensing/migrations/0008_stickeractionfee_uuid.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-02-09 07:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0007_auto_20230207_1458'), + ] + + operations = [ + migrations.AddField( + model_name='stickeractionfee', + name='uuid', + field=models.CharField(blank=True, max_length=36, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0009_emailuserlogentry.py b/mooringlicensing/migrations/0009_emailuserlogentry.py new file mode 100644 index 000000000..2c0538580 --- /dev/null +++ b/mooringlicensing/migrations/0009_emailuserlogentry.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.16 on 2023-02-10 02:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0008_stickeractionfee_uuid'), + ] + + operations = [ + migrations.CreateModel( + name='EmailUserLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('email_user_id', models.IntegerField(blank=True, null=True)), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + ] diff --git a/mooringlicensing/migrations/0010_delete_emailuserlogentry.py b/mooringlicensing/migrations/0010_delete_emailuserlogentry.py new file mode 100644 index 000000000..d3b6e39b7 --- /dev/null +++ b/mooringlicensing/migrations/0010_delete_emailuserlogentry.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.16 on 2023-02-10 03:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0009_emailuserlogentry'), + ] + + operations = [ + migrations.DeleteModel( + name='EmailUserLogEntry', + ), + ] diff --git a/mooringlicensing/migrations/0011_emailuserlogentry.py b/mooringlicensing/migrations/0011_emailuserlogentry.py new file mode 100644 index 000000000..38ea7e376 --- /dev/null +++ b/mooringlicensing/migrations/0011_emailuserlogentry.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.16 on 2023-02-10 03:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0010_delete_emailuserlogentry'), + ] + + operations = [ + migrations.CreateModel( + name='EmailUserLogEntry', + fields=[ + ('communicationslogentry_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='mooringlicensing.communicationslogentry')), + ('email_user_id', models.IntegerField(blank=True, null=True)), + ], + bases=('mooringlicensing.communicationslogentry',), + ), + ] diff --git a/mooringlicensing/migrations/0012_emailuserlogdocument.py b/mooringlicensing/migrations/0012_emailuserlogdocument.py new file mode 100644 index 000000000..ccc95221d --- /dev/null +++ b/mooringlicensing/migrations/0012_emailuserlogdocument.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.16 on 2023-02-10 06:01 + +from django.db import migrations, models +import django.db.models.deletion +import mooringlicensing.components.main.models +import mooringlicensing.components.users.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0011_emailuserlogentry'), + ] + + operations = [ + migrations.CreateModel( + name='EmailUserLogDocument', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, verbose_name='name')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('uploaded_date', models.DateTimeField(auto_now_add=True)), + ('_file', models.FileField(upload_to=mooringlicensing.components.users.models.update_emailuser_comms_log_filename)), + ('log_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='mooringlicensing.emailuserlogentry')), + ], + ), + ] diff --git a/mooringlicensing/migrations/0013_alter_emailuserlogdocument__file.py b/mooringlicensing/migrations/0013_alter_emailuserlogdocument__file.py new file mode 100644 index 000000000..2c38796e6 --- /dev/null +++ b/mooringlicensing/migrations/0013_alter_emailuserlogdocument__file.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.16 on 2023-02-16 06:06 + +from django.db import migrations, models +import mooringlicensing.components.users.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0012_emailuserlogdocument'), + ] + + operations = [ + migrations.AlterField( + model_name='emailuserlogdocument', + name='_file', + field=models.FileField(max_length=300, upload_to=mooringlicensing.components.users.models.update_emailuser_comms_log_filename), + ), + ] diff --git a/mooringlicensing/migrations/0014_auto_20230216_1420.py b/mooringlicensing/migrations/0014_auto_20230216_1420.py new file mode 100644 index 000000000..8fafa2a4b --- /dev/null +++ b/mooringlicensing/migrations/0014_auto_20230216_1420.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2.16 on 2023-02-16 06:20 + +from django.db import migrations, models +import mooringlicensing.components.approvals.models +import mooringlicensing.components.users.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0013_alter_emailuserlogdocument__file'), + ] + + operations = [ + migrations.AlterField( + model_name='emailuserlogdocument', + name='_file', + field=models.FileField(max_length=512, upload_to=mooringlicensing.components.users.models.update_emailuser_comms_log_filename), + ), + migrations.AlterField( + model_name='globalsettings', + name='_file', + field=models.FileField(blank=True, max_length=512, null=True, upload_to='approval_permit_template'), + ), + migrations.AlterField( + model_name='renewaldocument', + name='_file', + field=models.FileField(max_length=512, upload_to=mooringlicensing.components.approvals.models.update_approval_doc_filename), + ), + ] diff --git a/mooringlicensing/migrations/0015_proposal_proposal_applicant_details.py b/mooringlicensing/migrations/0015_proposal_proposal_applicant_details.py new file mode 100644 index 000000000..93bdf4f07 --- /dev/null +++ b/mooringlicensing/migrations/0015_proposal_proposal_applicant_details.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-02-21 01:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0014_auto_20230216_1420'), + ] + + operations = [ + migrations.AddField( + model_name='proposal', + name='proposal_applicant_details', + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/mooringlicensing/migrations/0016_rename_proposal_applicant_details_proposal_personal_details.py b/mooringlicensing/migrations/0016_rename_proposal_applicant_details_proposal_personal_details.py new file mode 100644 index 000000000..acd618b7f --- /dev/null +++ b/mooringlicensing/migrations/0016_rename_proposal_applicant_details_proposal_personal_details.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.16 on 2023-02-21 01:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0015_proposal_proposal_applicant_details'), + ] + + operations = [ + migrations.RenameField( + model_name='proposal', + old_name='proposal_applicant_details', + new_name='personal_details', + ), + ] diff --git a/mooringlicensing/migrations/0297_mooringbay_code.py b/mooringlicensing/migrations/0297_mooringbay_code.py index bb7451323..a85dc2fd0 100644 --- a/mooringlicensing/migrations/0297_mooringbay_code.py +++ b/mooringlicensing/migrations/0297_mooringbay_code.py @@ -8,7 +8,8 @@ class Migration(migrations.Migration): dependencies = [ - ('mooringlicensing', '0296_auto_20220531_1555'), + # ('mooringlicensing', '0296_auto_20220531_1555'), + ('mooringlicensing', '0016_rename_proposal_applicant_details_proposal_personal_details'), ] operations = [ diff --git a/mooringlicensing/migrations/0298_auto_20230310_1518.py b/mooringlicensing/migrations/0298_auto_20230310_1518.py new file mode 100644 index 000000000..0e324d561 --- /dev/null +++ b/mooringlicensing/migrations/0298_auto_20230310_1518.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.18 on 2023-03-10 07:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0297_mooringbay_code'), + ] + + operations = [ + migrations.AlterField( + model_name='proposal', + name='vessel_type', + field=models.CharField(blank=True, choices=[('catamaran', 'Catamaran'), ('bow_rider', 'Bow Rider'), ('cabin_ruiser', 'Cabin Cruiser'), ('centre_console', 'Centre Console'), ('ferry', 'Ferry'), ('rigid_inflatable', 'Rigid Inflatable'), ('half_cabin', 'Half Cabin'), ('inflatable', 'Inflatable'), ('launch', 'Launch'), ('motor_sailer', 'Motor Sailer'), ('multihull', 'Multihull'), ('open_boat', 'Open Boat'), ('power_boat', 'Power Boat'), ('pwc', 'PWC'), ('Runabout', 'Runabout'), ('fishing_boat', 'Fishing Boat'), ('tender', 'Tender'), ('walkaround', 'Walkaround'), ('other', 'Other')], max_length=20), + ), + migrations.AlterField( + model_name='vesseldetails', + name='vessel_draft', + field=models.DecimalField(decimal_places=2, max_digits=8), + ), + migrations.AlterField( + model_name='vesseldetails', + name='vessel_type', + field=models.CharField(choices=[('catamaran', 'Catamaran'), ('bow_rider', 'Bow Rider'), ('cabin_ruiser', 'Cabin Cruiser'), ('centre_console', 'Centre Console'), ('ferry', 'Ferry'), ('rigid_inflatable', 'Rigid Inflatable'), ('half_cabin', 'Half Cabin'), ('inflatable', 'Inflatable'), ('launch', 'Launch'), ('motor_sailer', 'Motor Sailer'), ('multihull', 'Multihull'), ('open_boat', 'Open Boat'), ('power_boat', 'Power Boat'), ('pwc', 'PWC'), ('Runabout', 'Runabout'), ('fishing_boat', 'Fishing Boat'), ('tender', 'Tender'), ('walkaround', 'Walkaround'), ('other', 'Other')], max_length=20), + ), + migrations.AlterField( + model_name='vesseldetails', + name='vessel_weight', + field=models.DecimalField(decimal_places=2, max_digits=8), + ), + ] diff --git a/mooringlicensing/migrations/0299_auto_20230310_1601.py b/mooringlicensing/migrations/0299_auto_20230310_1601.py new file mode 100644 index 000000000..25987913b --- /dev/null +++ b/mooringlicensing/migrations/0299_auto_20230310_1601.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2.18 on 2023-03-10 08:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mooringlicensing', '0298_auto_20230310_1518'), + ] + + operations = [ + migrations.AlterField( + model_name='proposal', + name='vessel_beam', + field=models.DecimalField(decimal_places=2, max_digits=8, null=True), + ), + migrations.AlterField( + model_name='proposal', + name='vessel_draft', + field=models.DecimalField(decimal_places=2, max_digits=8, null=True), + ), + migrations.AlterField( + model_name='proposal', + name='vessel_length', + field=models.DecimalField(decimal_places=2, max_digits=8, null=True), + ), + migrations.AlterField( + model_name='proposal', + name='vessel_weight', + field=models.DecimalField(decimal_places=2, max_digits=8, null=True), + ), + ] diff --git a/mooringlicensing/mooring_booking_utils.py b/mooringlicensing/mooring_booking_utils.py index c9c7367d2..e6a554f3e 100644 --- a/mooringlicensing/mooring_booking_utils.py +++ b/mooringlicensing/mooring_booking_utils.py @@ -8,7 +8,8 @@ import requests import io from django.conf import settings -from django.core.urlresolvers import reverse, reverse_lazy +# from django.core.urlresolvers import reverse, reverse_lazy +from django.urls import reverse from django.core.exceptions import ValidationError from django.db import transaction from django.db.models import Q @@ -17,17 +18,27 @@ from django.utils import timezone from dateutil.tz.tz import tzoffset from pytz import timezone as pytimezone -from ledger.payments.models import Invoice,OracleInterface,CashTransaction -from ledger.payments.utils import oracle_parser_on_invoice, update_payments -from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket +# from ledger.payments.models import Invoice,OracleInterface,CashTransaction +# from ledger.payments.utils import oracle_parser_on_invoice, update_payments +# from ledger.checkout.utils import create_basket_session, create_checkout_session, place_order_submission, get_cookie_basket +from ledger_api_client.utils import create_basket_session, create_checkout_session, place_order_submission, update_payments #from mooring.models import (MooringArea, Mooringsite, MooringsiteRate, MooringsiteBooking, Booking, BookingInvoice, MooringsiteBookingRange, Rate, MooringAreaBookingRange,MooringAreaStayHistory, MooringsiteRate, MarinaEntryRate, BookingVehicleRego, AdmissionsBooking, AdmissionsOracleCode, AdmissionsRate, AdmissionsLine, ChangePricePeriod, CancelPricePeriod, GlobalSettings, MooringAreaGroup, AdmissionsLocation, ChangeGroup, CancelGroup, BookingPeriod, BookingPeriodOption, AdmissionsBookingInvoice, BookingAnnualAdmission) #from mooring import models #from mooring.serialisers import BookingRegoSerializer, MooringsiteRateSerializer, MarinaEntryRateSerializer, RateSerializer, MooringsiteRateReadonlySerializer, AdmissionsRateSerializer #from mooring.emails import send_booking_invoice,send_booking_confirmation #from mooring import emails +# <<<<<<< HEAD +# from oscar.apps.order.models import Order +from ledger_api_client.order import Order +# from ledger.payments.invoice import utils +# ||||||| 741adce2 +# from oscar.apps.order.models import Order +# from ledger.payments.invoice import utils +# ======= #from oscar.apps.order.models import Order -from ledger.order.models import Order -from ledger.payments.invoice import utils +# from ledger.order.models import Order +# from ledger.payments.invoice import utils +# >>>>>>> main #from mooring import models from mooringlicensing.components.proposals.models import Proposal from mooringlicensing.components.payments_ml.models import ApplicationFee @@ -835,7 +846,8 @@ def admissionsCheckout(request, admissionsBooking, lines, invoice_text=None, vou 'vouchers': vouchers, 'system': settings.PS_PAYMENT_SYSTEM_ID, 'custom_basket': True, - 'booking_reference': 'AD-'+str(admissionsBooking.id) + 'booking_reference': 'AD-'+str(admissionsBooking.id), + 'tax_override': True, } basket, basket_hash = create_basket_session(request, basket_params) @@ -879,7 +891,8 @@ def annual_admission_checkout(request, booking, lines, invoice_text=None, vouche 'vouchers': vouchers, 'system': settings.PS_PAYMENT_SYSTEM_ID, 'custom_basket': True, - 'booking_reference': 'AA-'+str(booking.id) + 'booking_reference': 'AA-'+str(booking.id), + 'tax_override': True, } basket, basket_hash = create_basket_session(request, basket_params) checkout_params = { @@ -935,7 +948,8 @@ def checkout(request, booking, lines, invoice_text=None, vouchers=[], internal=F 'vouchers': vouchers, 'system': settings.PS_PAYMENT_SYSTEM_ID, 'custom_basket': True, - 'booking_reference': 'PS-'+str(booking.id) + 'booking_reference': 'PS-'+str(booking.id), + 'tax_override': True, } basket, basket_hash = create_basket_session(request, basket_params) @@ -1009,7 +1023,8 @@ def allocate_failedrefund_to_unallocated(request, booking, lines, invoice_text=N 'vouchers': [], 'system': settings.PS_PAYMENT_SYSTEM_ID, 'custom_basket': True, - 'booking_reference': booking_reference + 'booking_reference': booking_reference, + 'tax_override': True, } basket, basket_hash = create_basket_session(request, basket_params) @@ -1047,7 +1062,8 @@ def allocate_refund_to_invoice(request, booking, lines, invoice_text=None, inter 'vouchers': [], 'system': settings.PS_PAYMENT_SYSTEM_ID, 'custom_basket': True, - 'booking_reference': booking_reference + 'booking_reference': booking_reference, + 'tax_override': True, } basket, basket_hash = create_basket_session(request, basket_params) diff --git a/mooringlicensing/settings.py b/mooringlicensing/settings.py index 8b43a17ca..d04321cd1 100755 --- a/mooringlicensing/settings.py +++ b/mooringlicensing/settings.py @@ -9,7 +9,8 @@ confy.read_environment_file(BASE_DIR+"/.env") os.environ.setdefault("BASE_DIR", BASE_DIR) -from ledger.settings_base import * +# from ledger.settings_base import * +from ledger_api_client.settings_base import * ROOT_URLCONF = 'mooringlicensing.urls' SITE_ID = 1 @@ -42,7 +43,9 @@ def show_toolbar(request): INSTALLED_APPS += [ + 'webtemplate_dbca', 'smart_selects', + 'reversion', 'reversion_compare', 'bootstrap3', 'mooringlicensing', @@ -54,12 +57,13 @@ def show_toolbar(request): 'mooringlicensing.components.approvals', 'mooringlicensing.components.compliances', 'mooringlicensing.components.payments_ml', - 'taggit', + # 'taggit', 'rest_framework', 'rest_framework_datatables', 'rest_framework_gis', 'reset_migrations', 'ckeditor', + 'ledger_api_client', ] ADD_REVERSION_ADMIN=True @@ -78,6 +82,8 @@ def show_toolbar(request): 'mooringlicensing.middleware.CacheControlMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', ] +MIDDLEWARE = MIDDLEWARE_CLASSES +WSGI_APPLICATION = "mooringlicensing.wsgi.application" TEMPLATES[0]['DIRS'].append(os.path.join(BASE_DIR, 'mooringlicensing', 'templates')) TEMPLATES[0]['OPTIONS']['context_processors'].append('mooringlicensing.context_processors.mooringlicensing_processor') @@ -152,11 +158,11 @@ def show_toolbar(request): #PAYMENT_SYSTEM_ID = env('PAYMENT_SYSTEM_ID', 'S651') ## do not read from env -PAYMENT_SYSTEM_ID = 'S651' -OSCAR_BASKET_COOKIE_OPEN = 'mooringlicensing_basket' -PS_PAYMENT_SYSTEM_ID = PAYMENT_SYSTEM_ID -PAYMENT_SYSTEM_PREFIX = env('PAYMENT_SYSTEM_PREFIX', PAYMENT_SYSTEM_ID.replace('S', '0')) +OSCAR_BASKET_COOKIE_OPEN = 'mooringlicensing_basket' +# PAYMENT_SYSTEM_PREFIX = env('PAYMENT_SYSTEM_PREFIX', PAYMENT_SYSTEM_ID.replace('S', '0')) +LEDGER_SYSTEM_ID = env('PAYMENT_INTERFACE_SYSTEM_PROJECT_CODE', 'PAYMENT_INTERFACE_SYSTEM_PROJECT_CODE not configured') +PAYMENT_SYSTEM_ID = LEDGER_SYSTEM_ID.replace('0', 'S') MOORING_BOOKINGS_API_KEY=env('MOORING_BOOKINGS_API_KEY') MOORING_BOOKINGS_API_URL=env('MOORING_BOOKINGS_API_URL') @@ -253,6 +259,7 @@ def show_toolbar(request): # Logging all to mooringlicensing.log file LOGGING['loggers']['']['handlers'].append('file_mooringlicensing') +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" GROUP_MOORING_LICENSING_ADMIN = 'Mooring Licensing - Admin' GROUP_MOORING_LICENSING_PAYMENT_OFFICER = 'Mooring Licensing - Payment Officers' @@ -440,4 +447,15 @@ def show_toolbar(request): # GIT_COMMIT_HASH = os.popen('cat /app/git_hash').read() # if len(GIT_COMMIT_HASH) == 0: # print ("ERROR: No git hash provided") - +LEDGER_TEMPLATE = 'bootstrap5' +SESSION_COOKIE_NAME = "pp_sessionid" +SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" +LEDGER_UI_ACCOUNTS_MANAGEMENT = [ + {'first_name': {'options' : {'view': True, 'edit': True}}}, + {'last_name': {'options' : {'view': True, 'edit': True}}}, + {'residential_address': {'options' : {'view': True, 'edit': True}}}, + {'postal_address': {'options' : {'view': True, 'edit': True}}}, + {'phone_number' : {'options' : {'view': True, 'edit': True}}}, + {'mobile_number' : {'options' : {'view': True, 'edit': True}}}, +] +MOORING_LICENSING_EXTERNAL_URL = env('MOORING_LICENSING_EXTERNAL_URL', 'External url not configured') diff --git a/mooringlicensing/templates/mooringlicensing/base.html b/mooringlicensing/templates/mooringlicensing/base.html index c1208aaa1..dd54c265b 100755 --- a/mooringlicensing/templates/mooringlicensing/base.html +++ b/mooringlicensing/templates/mooringlicensing/base.html @@ -58,7 +58,7 @@ - +
{% if is_internal_login %}Applications {% else %}Home {% endif %}
@@ -115,7 +115,7 @@
  • Admin
  • {% endif %} {% if is_mooringlicensing_admin_user or request.user.is_superuser or is_payment_officer %} -
  • Reports
  • +
  • Financial Reports
  • {% endif %} {% if is_mooringlicensing_admin_user or request.user.is_superuser %} @@ -126,7 +126,8 @@ {% if show_tests and request.user.is_superuser %}
  • Test Emails
  • {% endif %} -
  • Manage Account
  • + +
  • Manage Account
  • {% if not is_internal_login %}
  • Manage Vessels
  • {% endif %} @@ -144,13 +145,14 @@ {% endfor %} -
  • Logout
  • + {% endcomment %} +
  • Logout
  • {% endif %} diff --git a/mooringlicensing/templates/mooringlicensing/emails_2/email_5.html b/mooringlicensing/templates/mooringlicensing/emails_2/email_5.html index 3e165a033..5126de533 100755 --- a/mooringlicensing/templates/mooringlicensing/emails_2/email_5.html +++ b/mooringlicensing/templates/mooringlicensing/emails_2/email_5.html @@ -3,6 +3,7 @@ {% block content_body %} {% include "mooringlicensing/emails_2/salutation.html" %}

    Your {{ proposal.description }} {{ proposal.lodgement_number }} requires amendment.

    +{% if reason %}

    Reason: {{ reason }}

    {% endif %} {% if text %}

    Details: {{ text }}

    {% endif %}

    Please click here to access your application.

    {% include "mooringlicensing/emails/signature-rottnest.html" %} diff --git a/mooringlicensing/templates/mooringlicensing/index.html b/mooringlicensing/templates/mooringlicensing/index.html index 0c8c4ec2d..9697660b5 100755 --- a/mooringlicensing/templates/mooringlicensing/index.html +++ b/mooringlicensing/templates/mooringlicensing/index.html @@ -14,6 +14,7 @@ {% block messages %} {% endblock %} + {% block content %}
    @@ -33,7 +34,6 @@

    Click here if you want to pay for daily admission fees -

    @@ -52,6 +52,10 @@

    Access to {{system_name}}

    + + + Login +{% comment %} {% bootstrap_messages %}
    {% csrf_token %} @@ -70,6 +74,7 @@

    a password. When you need to login to a site, such as {{ system_name }}, simply enter your email and an authentication link will be sent to your registered email address. From there, simply follow the link to complete the login process.

    +{% endcomment %}

    {% endif %}
    diff --git a/mooringlicensing/templates/mooringlicensing/login_success.html b/mooringlicensing/templates/mooringlicensing/login_success.html new file mode 100644 index 000000000..ed1e74a09 --- /dev/null +++ b/mooringlicensing/templates/mooringlicensing/login_success.html @@ -0,0 +1,11 @@ +{% extends 'mooringlicensing/base.html' %} + +{% block content %} +
    +
    +
    + Login Success, Home +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/mooringlicensing/templates/mooringlicensing/payments_ml/success_application_fee.html b/mooringlicensing/templates/mooringlicensing/payments_ml/success_application_fee.html index 341959291..55d8df7ef 100644 --- a/mooringlicensing/templates/mooringlicensing/payments_ml/success_application_fee.html +++ b/mooringlicensing/templates/mooringlicensing/payments_ml/success_application_fee.html @@ -36,7 +36,7 @@

    Success!

    {% if invoice.amount %} {% endif %} diff --git a/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_admission_fee.html b/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_admission_fee.html index 809a43f4f..35fa3c6a1 100644 --- a/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_admission_fee.html +++ b/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_admission_fee.html @@ -39,9 +39,7 @@

    Success!

    {% if request.user.is_authenticated %}
    - {% if invoice.amount %} -
    Invoice: #{{ fee_invoice.invoice_reference }}
    - {% endif %} +
    Invoice: #{{ fee_invoice.invoice_reference }}
    DCV Admission: {% for url in admission_urls %} Confirmation diff --git a/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_permit_fee.html b/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_permit_fee.html index 1e617bd4d..82508550e 100644 --- a/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_permit_fee.html +++ b/mooringlicensing/templates/mooringlicensing/payments_ml/success_dcv_permit_fee.html @@ -30,7 +30,8 @@

    Success!

    {% if request.user.is_authenticated %} diff --git a/mooringlicensing/templates/webtemplate_dbca/includes/menu_bottom.html b/mooringlicensing/templates/webtemplate_dbca/includes/menu_bottom.html new file mode 100644 index 000000000..e8ee980bf --- /dev/null +++ b/mooringlicensing/templates/webtemplate_dbca/includes/menu_bottom.html @@ -0,0 +1 @@ +menu_bottom.html (TODO) \ No newline at end of file diff --git a/mooringlicensing/templates/webtemplate_dbca/includes/primary_menu.html b/mooringlicensing/templates/webtemplate_dbca/includes/primary_menu.html new file mode 100644 index 000000000..a0cb082d8 --- /dev/null +++ b/mooringlicensing/templates/webtemplate_dbca/includes/primary_menu.html @@ -0,0 +1 @@ +primary_menu.html (TODO) \ No newline at end of file diff --git a/mooringlicensing/templates/webtemplate_dbca/includes/staff_menu_extras.html b/mooringlicensing/templates/webtemplate_dbca/includes/staff_menu_extras.html new file mode 100644 index 000000000..80edb1625 --- /dev/null +++ b/mooringlicensing/templates/webtemplate_dbca/includes/staff_menu_extras.html @@ -0,0 +1 @@ +staff_menu_extras.html (TODO) \ No newline at end of file diff --git a/mooringlicensing/templatetags/users.py b/mooringlicensing/templatetags/users.py index 91d8205bb..94b16220d 100755 --- a/mooringlicensing/templatetags/users.py +++ b/mooringlicensing/templatetags/users.py @@ -4,9 +4,10 @@ from django.conf import settings from mooringlicensing import helpers as mooringlicensing_helpers from mooringlicensing.components.main.models import SystemMaintenance -from ledger.payments.helpers import is_payment_admin +# from ledger.payments.helpers import is_payment_admin from datetime import datetime, timedelta from django.utils import timezone +from ledger_api_client.helpers import is_payment_admin import pytz register = Library() diff --git a/mooringlicensing/tests/test_aa.py b/mooringlicensing/tests/test_aa.py index 5ffcedade..cf5e7e60e 100644 --- a/mooringlicensing/tests/test_aa.py +++ b/mooringlicensing/tests/test_aa.py @@ -3,7 +3,8 @@ from mooringlicensing.components.proposals.models import MooringBay, Proposal, Vessel from datetime import datetime import pytz -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE class AnnualAdmissionTests(APITestSetup): diff --git a/mooringlicensing/tests/test_manage_vessels.py b/mooringlicensing/tests/test_manage_vessels.py index 5bc65f560..0ed5e0927 100644 --- a/mooringlicensing/tests/test_manage_vessels.py +++ b/mooringlicensing/tests/test_manage_vessels.py @@ -3,7 +3,7 @@ from mooringlicensing.components.proposals.models import MooringBay, Proposal from datetime import datetime import pytz -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE class ManageVesselTests(APITestSetup): diff --git a/mooringlicensing/tests/test_setup.py b/mooringlicensing/tests/test_setup.py index a187bb54c..ce0b5fb71 100644 --- a/mooringlicensing/tests/test_setup.py +++ b/mooringlicensing/tests/test_setup.py @@ -6,7 +6,7 @@ from mooringlicensing.management.default_data_manager import DefaultDataManager #from .models import * -from ledger.accounts.models import EmailUser, EmailUserManager +# from ledger.accounts.models import EmailUser, EmailUserManager import random import string import json, io, os, sys @@ -18,8 +18,8 @@ RequestsClient, ) from rest_framework import status -from ledger.accounts.models import EmailUser, Address -from ledger.address.models import UserAddress +# from ledger.accounts.models import EmailUser, Address +# from ledger.address.models import UserAddress from requests.auth import HTTPBasicAuth from mooringlicensing.components.proposals.models import ( ProposalType, diff --git a/mooringlicensing/tests/test_wl.py b/mooringlicensing/tests/test_wl.py index 0a5539019..81d79f678 100644 --- a/mooringlicensing/tests/test_wl.py +++ b/mooringlicensing/tests/test_wl.py @@ -3,7 +3,7 @@ from mooringlicensing.components.proposals.models import MooringBay, Proposal, Vessel from datetime import datetime import pytz -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE class WaitingListTests(APITestSetup): diff --git a/mooringlicensing/urls.py b/mooringlicensing/urls.py index baa4f24c5..094b116c8 100755 --- a/mooringlicensing/urls.py +++ b/mooringlicensing/urls.py @@ -5,13 +5,16 @@ from rest_framework import routers from django_media_serv.urls import urlpatterns as media_serv_patterns -import mooringlicensing.components.approvals.api +import mooringlicensing +# import mooringlicensing.components.approvals.api from mooringlicensing import views from mooringlicensing.components.approvals.views import DcvAdmissionFormView from mooringlicensing.components.payments_ml.views import ApplicationFeeView, ApplicationFeeSuccessView, InvoicePDFView, \ DcvPermitFeeView, DcvPermitFeeSuccessView, DcvPermitPDFView, ConfirmationView, DcvAdmissionFeeView, \ DcvAdmissionFeeSuccessView, DcvAdmissionPDFView, ApplicationFeeExistingView, StickerReplacementFeeView, \ - StickerReplacementFeeSuccessView, RefundProposalHistoryView, ProposalPaymentHistoryView, ApplicationFeeAlreadyPaid + StickerReplacementFeeSuccessView, RefundProposalHistoryView, ProposalPaymentHistoryView, ApplicationFeeAlreadyPaid, \ + ApplicationFeeSuccessViewPreload, DcvPermitFeeSuccessViewPreload, DcvAdmissionFeeSuccessViewPreload, \ + StickerReplacementFeeSuccessViewPreload from mooringlicensing.components.proposals import views as proposal_views from mooringlicensing.components.organisations import views as organisation_views from mooringlicensing.components.payments_ml import api as payments_api @@ -23,11 +26,13 @@ from mooringlicensing.components.users import api as users_api from mooringlicensing.components.organisations import api as org_api from mooringlicensing.components.main import api as main_api -from ledger.urls import urlpatterns as ledger_patterns +# from ledger.urls import urlpatterns as ledger_patterns +from ledger_api_client.urls import urlpatterns as ledger_patterns # API patterns from mooringlicensing.management.default_data_manager import DefaultDataManager from mooringlicensing.utils import are_migrations_running +from django.urls import path router = routers.DefaultRouter() router.register(r'organisations', org_api.OrganisationViewSet) @@ -79,7 +84,7 @@ url(r'^api/filtered_payments$', approval_api.ApprovalPaymentFilterViewSet.as_view(), name='filtered_payments'), url(r'^api/application_types$', proposal_api.GetApplicationTypeDescriptions.as_view(), name='get-application-type-descriptions'), url(r'^api/application_types_dict$', proposal_api.GetApplicationTypeDict.as_view(), name='get-application-type-dict'), - url(r'^api/applicants_dict$', proposal_api.GetApplicantsDict.as_view(), name='get-applicants-dict'), + # url(r'^api/applicants_dict$', proposal_api.GetApplicantsDict.as_view(), name='get-applicants-dict'), url(r'^api/payment_system_id$', proposal_api.GetPaymentSystemId.as_view(), name='get-payment-system-id'), url(r'^api/fee_item_sticker_replacement$', proposal_api.GetStickerReplacementFeeItem.as_view(), name='get-sticker-replacement-fee-item'), url(r'^api/vessel_rego_nos$', proposal_api.GetVesselRegoNos.as_view(), name='get-vessel_rego-nos'), @@ -113,14 +118,17 @@ url(r'^api/search_reference',proposal_api.SearchReferenceView.as_view(),name='search_reference'), url(r'^api/oracle_job$',main_api.OracleJob.as_view(), name='get-oracle'), url(r'^api/reports/booking_settlements$', main_api.BookingSettlementReportView.as_view(),name='booking-settlements-report'), + url(r'^api/external_dashboard_sections_list/$',main_api.GetExternalDashboardSectionsList.as_view(), name='get-external-dashboard-sections-list'), ] # URL Patterns urlpatterns = [ - url(r'^ledger/admin/', admin.site.urls, name='ledger_admin'), + # url(r'^ledger/admin/', admin.site.urls, name='ledger_admin'), + path(r"admin/", admin.site.urls), url(r'^chaining/', include('smart_selects.urls')), url(r'', include(api_patterns)), - url(r'^$', views.MooringLicensingRoutingView.as_view(), name='ds_home'), + # url(r'^$', views.MooringLicensingRoutingView.as_view(), name='ds_home'), + url(r'^$', views.MooringLicensingRoutingView.as_view(), name='home'), url(r'^contact/', views.MooringLicensingContactView.as_view(), name='ds_contact'), url(r'^further_info/', views.MooringLicensingFurtherInformationView.as_view(), name='ds_further_info'), url(r'^internal/', views.InternalView.as_view(), name='internal'), @@ -130,6 +138,7 @@ url(r'^profiles/', views.ExternalView.as_view(), name='manage-profiles'), url(r'^help/(?P[^/]+)/(?P[^/]+)/$', views.HelpView.as_view(), name='help'), url(r'^mgt-commands/$', views.ManagementCommandsView.as_view(), name='mgt-commands'), + url(r'^login-success/$', views.LoginSuccess.as_view(), name='login-success'), #following url is used to include url path when sending Proposal amendment request to user. url(r'^proposal/$', proposal_views.ProposalView.as_view(), name='proposal'), @@ -137,17 +146,22 @@ # payment related urls url(r'^application_fee/(?P\d+)/$', ApplicationFeeView.as_view(), name='application_fee'), - url(r'^application_fee_existing/(?P\d+)/$', ApplicationFeeExistingView.as_view(), name='application_fee_existing'), + url(r'^application_fee_existing/(?P\d+)/$', ApplicationFeeExistingView.as_view(), name='application_fee_existing'), url(r'^application_fee_already_paid/(?P\d+)/$', ApplicationFeeAlreadyPaid.as_view(), name='application_fee_already_paid'), # url(r'^application_fee_already_paid/$', ApplicationFeeAlreadyPaid.as_view(), name='application_fee_already_paid'), - url(r'^sticker_replacement_fee/$', StickerReplacementFeeView.as_view(), name='sticker_replacement_fee'), - url(r'^sticker_replacement_fee_success/fee/$', StickerReplacementFeeSuccessView.as_view(), name='sticker_replacement_fee_success'), url(r'^confirmation/(?P\d+)/$', ConfirmationView.as_view(), name='confirmation'), + url(r'^success/fee/(?P.+)/$', ApplicationFeeSuccessView.as_view(), name='fee_success'), + # url(r'^success2/fee/$', ApplicationFeeSuccessViewPreload.as_view(), name='fee_success_preload'), + url(r"ledger-api-success-callback/(?P.+)/", ApplicationFeeSuccessViewPreload.as_view(), name="ledger-api-success-callback",), url(r'^dcv_permit_fee/(?P\d+)/$', DcvPermitFeeView.as_view(), name='dcv_permit_fee'), + url(r'^dcv_permit_success/(?P.+)/$', DcvPermitFeeSuccessView.as_view(), name='dcv_permit_fee_success'), + url(r'^dcv_permit_success_preload/(?P.+)/$', DcvPermitFeeSuccessViewPreload.as_view(), name='dcv_permit_fee_success_preload'), url(r'^dcv_admission_fee/(?P\d+)/$', DcvAdmissionFeeView.as_view(), name='dcv_admission_fee'), - url(r'^success/fee/$', ApplicationFeeSuccessView.as_view(), name='fee_success'), - url(r'^dcv_permit_success/fee/$', DcvPermitFeeSuccessView.as_view(), name='dcv_permit_fee_success'), - url(r'^dcv_admission_success/fee/$', DcvAdmissionFeeSuccessView.as_view(), name='dcv_admission_fee_success'), + url(r'^dcv_admission_success/(?P.+)/$', DcvAdmissionFeeSuccessView.as_view(), name='dcv_admission_fee_success'), + url(r'^dcv_admission_success_preload/(?P.+)/$', DcvAdmissionFeeSuccessViewPreload.as_view(), name='dcv_admission_fee_success_preload'), + url(r'^sticker_replacement_fee/$', StickerReplacementFeeView.as_view(), name='sticker_replacement_fee'), + url(r'^sticker_replacement_fee_success/(?P.+)/$', StickerReplacementFeeSuccessView.as_view(), name='sticker_replacement_fee_success'), + url(r'^sticker_replacement_fee_success_preload/(?P.+)/$', StickerReplacementFeeSuccessViewPreload.as_view(), name='sticker_replacement_fee_success_preload'), url(r'^aua_for_endorsement/(?P[a-zA-Z0-9-]+)/endorse/$', AuthorisedUserApplicationEndorseView.as_view(), {'action': 'endorse'}, name='endorse-url'), url(r'^aua_for_endorsement/(?P[a-zA-Z0-9-]+)/decline/$', AuthorisedUserApplicationEndorseView.as_view(), {'action': 'decline'}, name='decline-url'), url(r'^mla_documents_upload/(?P[a-zA-Z0-9-]+)/$', MooringLicenceApplicationDocumentsUploadView.as_view(), name='mla-documents-upload'), diff --git a/mooringlicensing/utils/annual_admission_migrate.py b/mooringlicensing/utils/annual_admission_migrate.py index 589378405..0a47b5680 100644 --- a/mooringlicensing/utils/annual_admission_migrate.py +++ b/mooringlicensing/utils/annual_admission_migrate.py @@ -6,8 +6,8 @@ from decimal import Decimal from django.db import transaction from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, @@ -185,7 +185,7 @@ def migrate(self): ua=ProposalUserAction.objects.create( proposal=proposal, - who=user, + who=user.id, what='Annual Admission - Migrated Application', ) diff --git a/mooringlicensing/utils/authorised_user_migrate.15Oct2021.py b/mooringlicensing/utils/authorised_user_migrate.15Oct2021.py index 342ab31a9..c2b43a7bd 100644 --- a/mooringlicensing/utils/authorised_user_migrate.15Oct2021.py +++ b/mooringlicensing/utils/authorised_user_migrate.15Oct2021.py @@ -5,8 +5,8 @@ from decimal import Decimal from django.db import transaction from django.core.exceptions import ObjectDoesNotExist -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, diff --git a/mooringlicensing/utils/authorised_user_migrate.py b/mooringlicensing/utils/authorised_user_migrate.py index dc84e0f6f..fa365ae81 100644 --- a/mooringlicensing/utils/authorised_user_migrate.py +++ b/mooringlicensing/utils/authorised_user_migrate.py @@ -6,8 +6,8 @@ from decimal import Decimal from django.db import transaction from django.core.exceptions import ObjectDoesNotExist -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, @@ -351,7 +351,7 @@ def migrate(self): ua=ProposalUserAction.objects.create( proposal=proposal, - who=user, + who=user.id, what='Authorised User Permit - Migrated Application', ) diff --git a/mooringlicensing/utils/dcv_migrate.py b/mooringlicensing/utils/dcv_migrate.py index 98eff84ba..bd4a4ca78 100644 --- a/mooringlicensing/utils/dcv_migrate.py +++ b/mooringlicensing/utils/dcv_migrate.py @@ -7,8 +7,8 @@ import datetime from decimal import Decimal -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, diff --git a/mooringlicensing/utils/ml_csvwriter.py b/mooringlicensing/utils/ml_csvwriter.py index 3d7644b54..ccc94cbc4 100644 --- a/mooringlicensing/utils/ml_csvwriter.py +++ b/mooringlicensing/utils/ml_csvwriter.py @@ -2,7 +2,8 @@ from datetime import datetime import pytz import os -from ledger.settings_base import TIME_ZONE +# from ledger.settings_base import TIME_ZONE +from ledger_api_client.settings_base import TIME_ZONE from mooringlicensing.settings import BASE_DIR from mooringlicensing.components.approvals.models import ( Approval, WaitingListAllocation, AnnualAdmissionPermit, @@ -23,10 +24,10 @@ def write_wla(): ] rows = [[wla.lodgement_number, wla.start_date, wla.expiry_date, wla.issue_date.strftime('%Y-%m-%d'), wla.wla_queue_date.strftime('%Y-%m-%d') if wla.wla_queue_date else '', wla.wla_order, - wla.submitter.first_name, wla.submitter.last_name, wla.submitter.residential_address.line1, - wla.submitter.residential_address.locality, wla.submitter.residential_address.state, - wla.submitter.residential_address.country, wla.submitter.residential_address.postcode, '', - wla.submitter.phone_number, wla.submitter.mobile_number, wla.submitter.email, + wla.submitter_obj.first_name, wla.submitter_obj.last_name, wla.submitter_obj.residential_address.line1, + wla.submitter_obj.residential_address.locality, wla.submitter_obj.residential_address.state, + wla.submitter_obj.residential_address.country, wla.submitter_obj.residential_address.postcode, '', + wla.submitter_obj.phone_number, wla.submitter_obj.mobile_number, wla.submitter_obj.email, wla.current_proposal.vessel_ownership.vessel.rego_no if wla.current_proposal.vessel_ownership else '', '', ] for wla in WaitingListAllocation.objects.filter(status='current')] write_file(approval_type, approvals, header_row, rows) @@ -39,10 +40,10 @@ def write_aap(): 'Phone', 'Mobile', 'EMAIL', 'Vessel Rego', 'Company', ] rows = [[aap.lodgement_number, aap.start_date, aap.expiry_date, aap.issue_date.strftime('%Y-%m-%d'), - aap.submitter.first_name, aap.submitter.last_name, aap.submitter.residential_address.line1, - aap.submitter.residential_address.locality, aap.submitter.residential_address.state, - aap.submitter.residential_address.country, aap.submitter.residential_address.postcode, '', - aap.submitter.phone_number, aap.submitter.mobile_number, aap.submitter.email, + aap.submitter_obj.first_name, aap.submitter_obj.last_name, aap.submitter_obj.residential_address.line1, + aap.submitter_obj.residential_address.locality, aap.submitter_obj.residential_address.state, + aap.submitter_obj.residential_address.country, aap.submitter_obj.residential_address.postcode, '', + aap.submitter_obj.phone_number, aap.submitter_obj.mobile_number, aap.submitter_obj.email, aap.current_proposal.vessel_ownership.vessel.rego_no if aap.current_proposal.vessel_ownership else '', '', ] for aap in AnnualAdmissionPermit.objects.filter(status='current')] write_file(approval_type, approvals, header_row, rows) @@ -56,10 +57,10 @@ def write_aup(): ] rows = [[aup.lodgement_number, aup.start_date, aup.expiry_date, aup.issue_date.strftime('%Y-%m-%d'), ','.join(str(moa.mooring) for moa in aup.mooringonapproval_set.filter(mooring__mooring_licence__status='current')), - aup.submitter.first_name, aup.submitter.last_name, aup.submitter.residential_address.line1, - aup.submitter.residential_address.locality, aup.submitter.residential_address.state, - aup.submitter.residential_address.country, aup.submitter.residential_address.postcode, '', - aup.submitter.phone_number, aup.submitter.mobile_number, aup.submitter.email, + aup.submitter_obj.first_name, aup.submitter_obj.last_name, aup.submitter_obj.residential_address.line1, + aup.submitter_obj.residential_address.locality, aup.submitter_obj.residential_address.state, + aup.submitter_obj.residential_address.country, aup.submitter_obj.residential_address.postcode, '', + aup.submitter_obj.phone_number, aup.submitter_obj.mobile_number, aup.submitter_obj.email, aup.current_proposal.vessel_ownership.vessel.rego_no if aup.current_proposal.vessel_ownership else '', '', ] for aup in AuthorisedUserPermit.objects.filter(status='current')] write_file(approval_type, approvals, header_row, rows) @@ -73,10 +74,10 @@ def write_ml(): ] rows = [[ml.lodgement_number, ml.start_date, ml.expiry_date, ml.issue_date.strftime('%Y-%m-%d'), ml.mooring if hasattr(ml, 'mooring') else '', - ml.submitter.first_name, ml.submitter.last_name, ml.submitter.residential_address.line1, - ml.submitter.residential_address.locality, ml.submitter.residential_address.state, - ml.submitter.residential_address.country, ml.submitter.residential_address.postcode, '', - ml.submitter.phone_number, ml.submitter.mobile_number, ml.submitter.email, + ml.submitter_obj.first_name, ml.submitter_obj.last_name, ml.submitter_obj.residential_address.line1, + ml.submitter_obj.residential_address.locality, ml.submitter_obj.residential_address.state, + ml.submitter_obj.residential_address.country, ml.submitter_obj.residential_address.postcode, '', + ml.submitter_obj.phone_number, ml.submitter_obj.mobile_number, ml.submitter_obj.email, ','.join(vessel.rego_no for vessel in ml.vessel_list), ] for ml in MooringLicence.objects.filter(status='current')] write_file(approval_type, approvals, header_row, rows) diff --git a/mooringlicensing/utils/mooring_licence_migrate.py b/mooringlicensing/utils/mooring_licence_migrate.py index d8cb90077..fff2004e0 100644 --- a/mooringlicensing/utils/mooring_licence_migrate.py +++ b/mooringlicensing/utils/mooring_licence_migrate.py @@ -6,8 +6,8 @@ from decimal import Decimal from django.db import transaction, IntegrityError from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, @@ -323,7 +323,7 @@ def migrate(self): ua=ProposalUserAction.objects.create( proposal=proposal, - who=user, + who=user.id, what='Mooring Licence - Migrated Application', ) diff --git a/mooringlicensing/utils/mooring_licence_migrate_pd.py b/mooringlicensing/utils/mooring_licence_migrate_pd.py index 946f54662..897a80494 100644 --- a/mooringlicensing/utils/mooring_licence_migrate_pd.py +++ b/mooringlicensing/utils/mooring_licence_migrate_pd.py @@ -235,19 +235,19 @@ class MooringLicenceReader(): FROM shell_plus: from mooringlicensing.utils.mooring_licence_migrate_pd import MooringLicenceReader - mlr=MooringLicenceReader('PersonDets20221222-125823.txt', 'MooringDets20221222-125546.txt', 'VesselDets20221222-125823.txt', 'UserDets20221222-130353.txt', 'ApplicationDets20221222-125157.txt', 'annual_admissions_booking_report_20230125084027.csv') + mlr=MooringLicenceReader('PersonDets.txt', 'MooringDets.txt', 'VesselDets.txt', 'UserDets.txt', 'ApplicationDets.txt', 'annual_admissions_booking_report.csv') - self.create_users() - self.create_vessels() - self.create_mooring_licences() - self.create_authuser_permits() - self.create_waiting_list() - self.create_dcv() + mlr.create_users() + mlr.create_vessels() + mlr.create_mooring_licences() + mlr.create_authuser_permits() + mlr.create_waiting_list() + mlr.create_dcv() OR from mooringlicensing.utils.mooring_licence_migrate_pd import MooringLicenceReader - mlr=MooringLicenceReader('PersonDets20221222-125823.txt', 'MooringDets20221222-125546.txt', 'VesselDets20221222-125823.txt', 'UserDets20221222-130353.txt', 'ApplicationDets20221222-125157.txt', 'annual_admissions_booking_report_20230125084027.csv') - self.run_migration + mlr=MooringLicenceReader('PersonDets.txt', 'MooringDets.txt', 'VesselDets.txt', 'UserDets.txt', 'ApplicationDets.txt', 'annual_admissions_booking_report.csv') + mlr.run_migration FROM mgt-command: python manage_ds.py mooring_migration_script --filename mooringlicensing/utils/csv/MooringDets20221201-083202.txt @@ -257,6 +257,10 @@ class MooringLicenceReader(): pm = df['permit_number'].value_counts().loc[lambda x: x>1][1:].astype('int') pm.to_excel('/tmp/permit_numbers.xlsx') + From RKS: + + from mooringlicensing.utils.mooring_licence_migrate_pd import MooringLicenceReader + mlr=MooringLicenceReader('PersonDets.txt','MooringDets.txt','VesselDets.txt','UserDets.txt','ApplicationDets.txt','annual_admissions_booking_report.csv',path='/app/shared/clean/clean_22Dec2022/') """ @@ -298,11 +302,12 @@ def __init__(self, fname_user, fname_ml, fname_ves, fname_authuser, fname_wl, fn self.ml_no_ves_rows = [] def __get_phone_number(self, row): - row.home_number.replace(' ', '') - row.work_number.replace(' ', '') - if row.work_number: + if 'work_number' in row and row.work_number: + row.work_number.replace(' ', '') return row.work_number - else: + + elif 'home_number' in row and row.home_number: + row.home_number.replace(' ', '') return row.home_number def __get_work_number(self, row): @@ -679,7 +684,8 @@ def _create_users_df_aa(self, df): email=email, first_name=first_name, last_name=last_name, - phone_number=self.__get_work_number(user_row), + #phone_number=self.__get_work_number(user_row), + phone_number=self.__get_phone_number(user_row), mobile_number=self.__get_mobile_number(user_row) ) @@ -692,7 +698,8 @@ def _create_users_df_aa(self, df): # update user details user.first_name = first_name user.last_name = last_name - user.phone_number = self.__get_work_number(user_row) + #user.phone_number = self.__get_work_number(user_row) + user.phone_number = self.__get_phone_number(user_row), user.mobile_number = self.__get_mobile_number(user_row) country = Country.objects.get(printable_name='Australia') @@ -785,7 +792,7 @@ def try_except(value): postfix = ['Nominated Ves', 'Ad Ves 2', 'Ad Ves 3', 'Ad Ves 4', 'Ad Ves 5', 'Ad Ves 6', 'Ad Ves 7'] for user_id, pers_no in tqdm(self.pers_ids): try: - #if pers_no=='200700': + #if pers_no=='202600': # import ipdb; ipdb.set_trace() #import ipdb; ipdb.set_trace() @@ -811,11 +818,12 @@ def try_except(value): ves_type=ves['Type ' + postfix[i]] rego_no=ves['DoT Rego ' + postfix[i]] pct_interest=ves['%Interest ' + postfix[i]] - tot_length=ves['Total Length ' + postfix[i]] + tot_length=ves['Reg Length ' + postfix[i]] draft=ves['Draft ' + postfix[i]] tonnage=ves['Tonnage ' + postfix[i]] au_sticker=ves['Au Sticker No ' + postfix[i]] c_sticker=ves['C Sticker No ' + postfix[i]] + au_sticker_date=ves['Date Au Sticker Sent ' + postfix[i]] #if rego_no=='BZ314': # import ipdb; ipdb.set_trace() @@ -856,8 +864,8 @@ def try_except(value): vessel_weight=try_except(tonnage), berth_mooring='' ) - #au_vessels.update({rego_no:au_sticker}) - self.vessels_au.update({rego_no:au_sticker}) + #au_vessels.update({rego_no:au_sticker, sticker_sent:}) + self.vessels_au.update({rego_no: dict(au_sticker=au_sticker, au_sticker_sent=au_sticker_date)}) c_vessels.append((rego_no, c_sticker)) #if pers_no=='200700': # import ipdb; ipdb.set_trace() @@ -908,13 +916,14 @@ def ves_fields(ves_row): ves_type=ves['Type ' + postfix[i]] rego_no=ves['DoT Rego ' + postfix[i]] pct_interest=ves['%Interest ' + postfix[i]] - tot_length=ves['Total Length ' + postfix[i]] + tot_length=ves['Reg Length ' + postfix[i]] draft=ves['Draft ' + postfix[i]] tonnage=ves['Tonnage ' + postfix[i]] beam=ves['Beam ' + postfix[i]] ml_sticker=ves['Lic Sticker Number ' + postfix[i]] au_sticker=ves['Au Sticker No ' + postfix[i]] c_sticker=ves['C Sticker No ' + postfix[i]] + au_sticker_date=ves['Date Au Sticker Sent ' + postfix[i]] #if rego_no=='DO904': # import ipdb; ipdb.set_trace() @@ -930,7 +939,8 @@ def ves_fields(ves_row): 'percentage': pct_interest, 'ml_sticker': ml_sticker, 'au_sticker': au_sticker, - 'c_sticker': c_sticker, + 'c_sticker': c_sticker, + 'au_sticker_date': au_sticker_date, } }) @@ -961,8 +971,10 @@ def create_mooring_licences(self): for index, row in tqdm(df.iterrows(), total=df.shape[0]): try: -# if row.pers_no == '211305': -# import ipdb; ipdb.set_trace() + #if row.pers_no == '209722': + # import ipdb; ipdb.set_trace() + #else: + # continue if not row.name or len([_str for _str in ['KINGSTON REEF','NN64','PB 02','RIA'] if row.name in _str])>0: continue @@ -1018,7 +1030,7 @@ def create_mooring_licences(self): ves_type = ves_row['Type ' + postfix] rego_no = ves_row['DoT Rego ' + postfix] pct_interest = ves_row['%Interest ' + postfix] - tot_length = ves_row['Total Length ' + postfix] + tot_length = ves_row['Reg Length ' + postfix] draft = ves_row['Draft ' + postfix] tonnage = ves_row['Tonnage ' + postfix] sticker_number = ves_row['Lic Sticker Number ' + postfix] #if ves_row['Lic Sticker Number ' + postfix] else None @@ -1191,7 +1203,7 @@ def create_mooring_licences(self): approval=approval, proposal_initiated=proposal, vessel_ownership=vessel_ownership, - printing_date=TODAY, + printing_date=None, #TODAY, #mailing_date=sticker_sent, #TODAY, mailing_date=datetime.datetime.strptime(sticker_sent, '%d/%m/%Y').date() if sticker_sent else None, sticker_printing_batch=None, @@ -1247,7 +1259,7 @@ def create_authuser_permits(self): # exclude mooring continue - #if row.pers_no_u == '000011': + #if row.pers_no_u == '210758': # import ipdb; ipdb.set_trace() #if rego_no == 'GM894': @@ -1280,7 +1292,13 @@ def create_authuser_permits(self): #sticker_number = row['sticker'], #rego_no = row['vessel_rego'] rego_no = row.name - sticker_number = self.vessels_au.get(rego_no) + #if sticker_number == 31779: + # import ipdb; ipdb.set_trace() + sticker_info = self.vessels_au.get(rego_no) + if sticker_info: + sticker_number = sticker_info['au_sticker'] + sticker_sent = sticker_info['au_sticker_sent'] + if sticker_number is None: no_au_stickers.append(rego_no) else: @@ -1404,9 +1422,10 @@ def create_authuser_permits(self): approval=approval, proposal_initiated=proposal, vessel_ownership=vessel_ownership, - printing_date=TODAY, + printing_date=None, #TODAY, #mailing_date=sticker_sent, #TODAY, - mailing_date=datetime.datetime.strptime(date_issued, '%d/%m/%Y').date() if date_issued else None, + #mailing_date=datetime.datetime.strptime(date_issued, '%d/%m/%Y').date() if date_issued else None, + mailing_date=datetime.datetime.strptime(sticker_sent, '%d/%m/%Y').date() if sticker_sent else None, sticker_printing_batch=None, sticker_printing_response=None, ) @@ -2071,6 +2090,14 @@ def _migrate_approval(self, data, submitter, applicant=None, proxy_applicant=Non # a.generate_dcv_permit_doc() # print('{}, Created PDF for DCV Approval {}'.format(idx, a)) + + def create_licence_pdfs(self): + MooringLicenceReader.create_pdf_ml() + MooringLicenceReader.create_pdf_aup() + MooringLicenceReader.create_pdf_wl() + MooringLicenceReader.create_pdf_aa() + MooringLicenceReader.create_pdf_dcv() + @classmethod def create_pdf_ml(self): """ MooringLicenceReader.create_pdf_ml() @@ -2114,7 +2141,10 @@ def _create_pdf_licence(approvals_migrated): approvals = approvals_migrated.filter(migrated=True, current_proposal__processing_status=Proposal.PROCESSING_STATUS_APPROVED) for idx, a in enumerate(approvals): - a.generate_dcv_permit_doc() if isinstance(a, DcvPermit) else a.generate_doc() + if isinstance(a, DcvPermit) and len(a.permits.all())==0: + a.generate_dcv_permit_doc() + elif not hasattr(a, 'licence_document') or a.licence_document is None: + a.generate_doc() print(f'{idx}, Created PDF for {permit_name}: {a}') diff --git a/mooringlicensing/utils/mooring_licence_migrate_write_csv.py b/mooringlicensing/utils/mooring_licence_migrate_write_csv.py index e83bf1b9a..1905c6285 100644 --- a/mooringlicensing/utils/mooring_licence_migrate_write_csv.py +++ b/mooringlicensing/utils/mooring_licence_migrate_write_csv.py @@ -11,7 +11,7 @@ ) from mooringlicensing.components.approvals.models import Approval, ApprovalHistory, MooringLicence, VesselOwnershipOnApproval from mooringlicensing.components.organisations.models import Organisation -from ledger.accounts.models import Organisation as ledger_org +# from ledger.accounts.models import Organisation as ledger_org import sys import csv diff --git a/mooringlicensing/utils/waiting_list_migrate.py b/mooringlicensing/utils/waiting_list_migrate.py index 4ac2c8f9c..670f5f354 100644 --- a/mooringlicensing/utils/waiting_list_migrate.py +++ b/mooringlicensing/utils/waiting_list_migrate.py @@ -4,8 +4,8 @@ import datetime from decimal import Decimal from django.db import transaction -from oscar.apps.address.models import Country -from ledger.accounts.models import EmailUser, Address +# from oscar.apps.address.models import Country +# from ledger.accounts.models import EmailUser, Address from mooringlicensing.components.proposals.models import ( Proposal, Vessel, @@ -218,7 +218,7 @@ def migrate(self): ua=ProposalUserAction.objects.create( proposal=proposal, - who=user, + who=user.id, what='Waiting List - Migrated Application', ) diff --git a/mooringlicensing/views.py b/mooringlicensing/views.py index df56e984e..02a359d0f 100644 --- a/mooringlicensing/views.py +++ b/mooringlicensing/views.py @@ -4,8 +4,10 @@ from django.shortcuts import render, redirect from django.views.generic import DetailView from django.views.generic.base import TemplateView -from django.conf import settings +# from django.conf import settings from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin + +from mooringlicensing import settings from mooringlicensing.helpers import is_internal from mooringlicensing.forms import * from mooringlicensing.components.proposals.models import ( @@ -16,8 +18,7 @@ from django.core.management import call_command -logger = logging.getLogger('mooringlicensing') - +logger = logging.getLogger(__name__) class InternalView(UserPassesTestMixin, TemplateView): template_name = 'mooringlicensing/dash/index.html' @@ -63,7 +64,8 @@ class MooringLicensingRoutingView(TemplateView): template_name = 'mooringlicensing/index.html' def get(self, *args, **kwargs): - if self.request.user.is_authenticated(): + # if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: if is_internal(self.request): return redirect('internal') return redirect('external') @@ -100,7 +102,8 @@ def get(self, *args, **kwargs): return super(MooringLicensingRoutingDetailView, self).get(*args, **kwargs) -@login_required(login_url='ds_home') +# @login_required(login_url='ds_home') +@login_required(login_url='home') def first_time(request): context = {} if request.method == 'POST': @@ -146,6 +149,15 @@ def get_context_data(self, **kwargs): return context +class LoginSuccess(TemplateView): + template_name = 'mooringlicensing/login_success.html'; + + def get(self, request, *args, **kwargs): + context = {'LEDGER_UI_URL' : settings.LEDGER_UI_URL} + response = render(request, self.template_name, context) + return response + + class ManagementCommandsView(LoginRequiredMixin, TemplateView): template_name = 'mooringlicensing/mgt-commands.html' @@ -166,7 +178,8 @@ def post(self, request): if command_script: print('running {}'.format(command_script)) - call_command(command_script, params=request.POST) + # call_command(command_script, params=request.POST) + call_command(command_script,) data.update({command_script: 'true'}) return render(request, self.template_name, data) diff --git a/patch_for_admin_0001_initial.patch b/patch_for_admin_0001_initial.patch new file mode 100644 index 000000000..15a2d2911 --- /dev/null +++ b/patch_for_admin_0001_initial.patch @@ -0,0 +1,29 @@ +--- venv/lib/python3.8/site-packages/django/contrib/admin/migrations/0001_initial.py 2022-12-07 13:14:06.680551610 +0800 ++++ admin.0001_initial_patched.py 2022-12-08 10:00:10.313206926 +0800 +@@ -6,7 +6,7 @@ + class Migration(migrations.Migration): + + dependencies = [ +- migrations.swappable_dependency(settings.AUTH_USER_MODEL), ++ # migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '__first__'), + ] + +@@ -27,11 +27,12 @@ + to='contenttypes.ContentType', + verbose_name='content type', + )), +- ('user', models.ForeignKey( +- to=settings.AUTH_USER_MODEL, +- on_delete=models.CASCADE, +- verbose_name='user', +- )), ++ # ('user', models.ForeignKey( ++ # to=settings.AUTH_USER_MODEL, ++ # on_delete=models.CASCADE, ++ # verbose_name='user', ++ # )), ++ ('user_id', models.IntegerField(verbose_name='user',)), + ], + options={ + 'ordering': ['-action_time'], diff --git a/patch_for_admin_0001_initial.patch_revert b/patch_for_admin_0001_initial.patch_revert new file mode 100644 index 000000000..964dd08af --- /dev/null +++ b/patch_for_admin_0001_initial.patch_revert @@ -0,0 +1,29 @@ +--- admin.0001_initial_patched.py 2022-12-08 10:00:10.313206926 +0800 ++++ venv/lib/python3.8/site-packages/django/contrib/admin/migrations/0001_initial.py 2022-12-07 13:14:06.680551610 +0800 +@@ -6,7 +6,7 @@ + class Migration(migrations.Migration): + + dependencies = [ +- # migrations.swappable_dependency(settings.AUTH_USER_MODEL), ++ migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '__first__'), + ] + +@@ -27,12 +27,11 @@ + to='contenttypes.ContentType', + verbose_name='content type', + )), +- # ('user', models.ForeignKey( +- # to=settings.AUTH_USER_MODEL, +- # on_delete=models.CASCADE, +- # verbose_name='user', +- # )), +- ('user_id', models.IntegerField(verbose_name='user',)), ++ ('user', models.ForeignKey( ++ to=settings.AUTH_USER_MODEL, ++ on_delete=models.CASCADE, ++ verbose_name='user', ++ )), + ], + options={ + 'ordering': ['-action_time'], diff --git a/patch_for_reversion_0001.patch b/patch_for_reversion_0001.patch new file mode 100644 index 000000000..1209fb060 --- /dev/null +++ b/patch_for_reversion_0001.patch @@ -0,0 +1,12 @@ +--- venv/lib/python3.8/site-packages/reversion/migrations/0001_squashed_0004_auto_20160611_1202.py 2022-12-23 09:17:55.591980665 +0800 ++++ venv/lib/python3.8/site-packages/reversion/migrations/0001_squashed_0004_auto_20160611_1202_patched.py 2022-12-23 09:17:24.603980783 +0800 +@@ -20,7 +20,8 @@ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date_created', models.DateTimeField(db_index=True, help_text='The date and time this revision was created.', verbose_name='date created')), + ('comment', models.TextField(blank=True, help_text='A text comment on this revision.', verbose_name='comment')), +- ('user', models.ForeignKey(blank=True, help_text='The user who created this revision.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')), ++ # ('user', models.ForeignKey(blank=True, help_text='The user who created this revision.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')), ++ ('user_id', models.IntegerField(blank=True, help_text='The user who created this revision.', null=True,)), + ], + options={ + "ordering": ("-pk",) diff --git a/patch_for_reversion_0001.patch_revert b/patch_for_reversion_0001.patch_revert new file mode 100644 index 000000000..f949e1c6d --- /dev/null +++ b/patch_for_reversion_0001.patch_revert @@ -0,0 +1,12 @@ +--- venv/lib/python3.8/site-packages/reversion/migrations/0001_squashed_0004_auto_20160611_1202_patched.py 2022-12-23 09:17:24.603980783 +0800 ++++ venv/lib/python3.8/site-packages/reversion/migrations/0001_squashed_0004_auto_20160611_1202.py 2022-12-23 09:17:55.591980665 +0800 +@@ -20,8 +20,7 @@ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date_created', models.DateTimeField(db_index=True, help_text='The date and time this revision was created.', verbose_name='date created')), + ('comment', models.TextField(blank=True, help_text='A text comment on this revision.', verbose_name='comment')), +- # ('user', models.ForeignKey(blank=True, help_text='The user who created this revision.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')), +- ('user_id', models.IntegerField(blank=True, help_text='The user who created this revision.', null=True,)), ++ ('user', models.ForeignKey(blank=True, help_text='The user who created this revision.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + "ordering": ("-pk",) diff --git a/requirements.txt b/requirements.txt index 533831928..946af59a9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,32 @@ -Django==1.11.29 +# Django==1.11.29 +Django==3.2.18 ipython==7.19.0 psycopg2==2.8.6 jedi==0.17.2 #git+https://github.com/xzzy/ledger.git@master2#egg=ledger -git+https://github.com/dbca-wa/ledger#egg=ledger +# git+https://github.com/dbca-wa/ledger#egg=ledger +git+https://github.com/dbca-wa/ledger_api_client.git django-extensions==1.9.8 #git+https://github.com/dbca-wa/dpaw-utils.git@0.3a16#egg=dpaw-utils git+https://github.com/dbca-wa/dpaw-utils.git@0.4.2#egg=dpaw-utils #git+https://github.com/django-oscar/django-oscar.git@17a1f6b17aba19e3c0b095e90acc654d463e0cf9#egg=django-oscar -git+https://github.com/django-oscar/django-oscar.git@8a3288da439cc2a878f44ae5c5101043e658d2a2#egg=django-oscar -social-auth-app-django==2.1.0 -git+https://github.com/scottp-dpaw/social-core.git@email_fix#egg=social-auth-core +# git+https://github.com/django-oscar/django-oscar.git@8a3288da439cc2a878f44ae5c5101043e658d2a2#egg=django-oscar +# social-auth-app-django==2.1.0 +# git+https://github.com/scottp-dpaw/social-core.git@email_fix#egg=social-auth-core coverage==4.3.1 coveralls==1.1 reportlab==3.5.55 django_bootstrap3==11.1.0 django-braces>=1.8.1 django-datatables-view==1.13.0 -django-reversion==3.0.0 -django-reversion-compare==0.8.6 +# django-reversion==3.0.0 +django-reversion==5.0.0 +# django-reversion-compare==0.8.6 +django-reversion-compare==0.15.0 #git+https://github.com/bruth/django-preserialize.git@11f9e822250ea3374d5cfba477a00ca886689d9f#egg=django-preserialize git+https://github.com/xzzy/django-preserialize.git#egg=django-preserialize -django-countries==6.1.3 +# django-countries==6.1.3 +django-countries==7.3.2 django-cron==0.5.0 django-dynamic-fixture==1.9.1 openpyxl==3.0.7 @@ -29,21 +34,24 @@ datapackage==0.8.1 jsontableschema==0.10.1 python-dateutil==2.8.1 py4j==0.10.2.1 -djangorestframework==3.5.0 -djangorestframework-gis==0.11 +# djangorestframework==3.5.0 +# djangorestframework-gis==0.11 +djangorestframework==3.13.1 +djangorestframework-gis==1.0 djangorestframework-csv==2.0.0 pycountry==17.1.8 six>=1.10.0 +django-utils-six==2.0 django-ical==1.4 django-taggit==0.21.3 geojson==1.3.3 unicodecsv==0.14.1 PyPDF2==1.26.0 django-extra-fields==0.9 -django-crispy-forms==1.7.0 +django-crispy-forms==1.8.1 django-reset-migrations==0.3.1 django-ckeditor==5.4.0 -djangorestframework-datatables==0.4.0 +djangorestframework-datatables==0.7.0 django-confy==1.0.4 xlwt==1.3.0 xlsxwriter==1.2.2 @@ -51,7 +59,7 @@ gunicorn==19.9.0 #dj-static==0.0.6 whitenoise==5.3.0 mixer==6.1.3 -django-dirtyfields==1.3.1 +django-dirtyfields==1.8.1 python-docx==0.8.10 docxtpl==0.10.0 dj-database-url==0.5.0 @@ -62,4 +70,8 @@ django-multiselectfield==0.1.12 django-smart-selects==1.5.9 pandas==1.4.2 git+https://github.com/dbca-wa/django-media-serv.git#egg=django_media_serv +django-widget-tweaks==1.4.12 +django-phonenumber-field==6.1.0 +babel==2.10.1 +git+https://github.com/dbca-wa/webtemplate2.git#egg=webtemplate_dbca tqdm==4.64.1