diff --git a/wildlifecompliance/components/sanction_outcome/email.py b/wildlifecompliance/components/sanction_outcome/email.py index 22fe6946d..68f5560a5 100644 --- a/wildlifecompliance/components/sanction_outcome/email.py +++ b/wildlifecompliance/components/sanction_outcome/email.py @@ -6,10 +6,9 @@ from django.conf import settings from wildlifecompliance.components.emails.emails import TemplateEmailBase from wildlifecompliance.components.main.email import prepare_attachments, _extract_email_headers -from wildlifecompliance.components.sanction_outcome.pdf_caution_notice import create_caution_notice_pdf_bytes -from wildlifecompliance.components.sanction_outcome.pdf_infringement_notice_blue import create_infringement_notice_blue -from wildlifecompliance.components.sanction_outcome.pdf_letter_of_advice import create_letter_of_advice_pdf_bytes -from wildlifecompliance.components.sanction_outcome.pdf_remediation_notice import create_remediation_notice_pdf_bytes +from wildlifecompliance.components.sanction_outcome.pdf import create_infringement_notice_pdf, \ + create_letter_of_advice_pdf, \ + create_caution_notice_pdf, create_remediation_notice_pdf_bytes logger = logging.getLogger(__name__) @@ -335,7 +334,7 @@ def send_caution_notice(to_address, sanction_outcome, workflow_entry, request, c } pdf_file_name = 'caution_notice_{}_{}.pdf'.format(sanction_outcome.lodgement_number, datetime.datetime.now().strftime("%Y%m%d%H%M%S")) - document = create_caution_notice_pdf_bytes(pdf_file_name, sanction_outcome) + document = create_caution_notice_pdf(pdf_file_name, sanction_outcome) # Attach files (files from the modal, and the PDF file generated above) attachments = prepare_attachments(workflow_entry.documents) @@ -369,7 +368,7 @@ def send_letter_of_advice(to_address, sanction_outcome, workflow_entry, request, } pdf_file_name = 'letter_of_advice_{}_{}.pdf'.format(sanction_outcome.lodgement_number, datetime.datetime.now().strftime("%Y%m%d%H%M%S")) - document = create_letter_of_advice_pdf_bytes(pdf_file_name, sanction_outcome) + document = create_letter_of_advice_pdf(pdf_file_name, sanction_outcome) # Attach files (files from the modal, and the PDF file generated above) attachments = prepare_attachments(workflow_entry.documents) @@ -563,7 +562,7 @@ def send_infringement_notice(to_address, sanction_outcome, workflow_entry, reque def create_infringement_notice_ybw(sanction_outcome, workflow_entry): pdf_file_name_b = 'infringement_notice_b_{}_{}.pdf'.format(sanction_outcome.lodgement_number, datetime.datetime.now().strftime("%Y%m%d%H%M%S")) - document_b = create_infringement_notice_blue(pdf_file_name_b, sanction_outcome) + document_b = create_infringement_notice_pdf(pdf_file_name_b, sanction_outcome) attachments = prepare_attachments(workflow_entry.documents) attachments.append((pdf_file_name_b, document_b._file.read(), 'application/pdf')) doc = workflow_entry.documents.create(name=document_b.name) diff --git a/wildlifecompliance/components/sanction_outcome/pdf.py b/wildlifecompliance/components/sanction_outcome/pdf.py index d8865ff63..a904efdac 100644 --- a/wildlifecompliance/components/sanction_outcome/pdf.py +++ b/wildlifecompliance/components/sanction_outcome/pdf.py @@ -1,347 +1,65 @@ -import os - -from decimal import Decimal as D from io import BytesIO +from django.core.files.base import ContentFile from django.core.files.storage import default_storage -from wildlifecompliance.settings import STATIC_ROOT -from oscar.templatetags.currency_filters import currency -from reportlab.lib import enums -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, ListFlowable, \ - KeepTogether, PageBreak, Flowable, NextPageTemplate, FrameBreak -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle -from reportlab.lib.utils import ImageReader -from reportlab.pdfgen.canvas import Canvas -from reportlab.pdfbase import pdfmetrics -from reportlab.lib.units import inch -from reportlab.lib import colors - -from django.core.files import File -from django.conf import settings - -from ledger.accounts.models import Document -from ledger.checkout.utils import calculate_excl_gst - -DPAW_HEADER_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo.jpg') -DPAW_HEADER_LOGO_SM = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo_small.png') -BPAY_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img', 'BPAY_2012_PORT_BLUE.png') - -HEADER_MARGIN = 10 -HEADER_SMALL_BUFFER = 3 - -PAGE_MARGIN = 20 -PAGE_TOP_MARGIN = 200 - -PAGE_WIDTH, PAGE_HEIGHT = A4 - -DEFAULT_FONTNAME = 'Helvetica' -BOLD_FONTNAME = 'Helvetica-Bold' - -VERY_LARGE_FONTSIZE = 14 -LARGE_FONTSIZE = 12 -MEDIUM_FONTSIZE = 10 -SMALL_FONTSIZE = 8 - -PARAGRAPH_BOTTOM_MARGIN = 5 - -SECTION_BUFFER_HEIGHT = 10 - -DATE_FORMAT = '%d/%m/%Y' - -styles = getSampleStyleSheet() -styles.add(ParagraphStyle(name='InfoTitleLargeCenter', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleVeryLargeCenter', fontName=BOLD_FONTNAME, fontSize=VERY_LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN * 2, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleLargeLeft', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_LEFT, - leftIndent=PAGE_WIDTH / 10, rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='InfoTitleLargeRight', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_RIGHT, - rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='BoldLeft', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='BoldRight', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='Center', alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='Left', alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='Right', alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='LongString', alignment=enums.TA_LEFT,wordWrap='CJK')) - - -class Remittance(Flowable): - def __init__(self,current_x,current_y,invoice): - Flowable.__init__(self) - self.current_x = current_x - self.current_y = current_y - self.invoice = invoice - - def __repr__(self): - return 'remittance' - - def __logo_line(self): - canvas = self.canv - current_y, current_x = self.current_y, self.current_x - canvas.setFont(DEFAULT_FONTNAME, MEDIUM_FONTSIZE) - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO_SM) - - dpaw_header_logo_size = dpaw_header_logo.getSize() - canvas.drawImage(dpaw_header_logo, HEADER_MARGIN, current_y - (dpaw_header_logo_size[1]/1.8),height=dpaw_header_logo_size[1]/1.8, mask='auto', width=dpaw_header_logo_size[0]/1.8) - - current_y = -20 - canvas.setFont(BOLD_FONTNAME, MEDIUM_FONTSIZE) - canvas.drawRightString(current_x * 45,current_y,'Remittance Advice') - - current_y -= 20 - canvas.setFont(DEFAULT_FONTNAME, MEDIUM_FONTSIZE) - canvas.drawString(current_x * 27,current_y,'PLEASE DETACH AND RETURN WITH YOUR PAYMENT') - - current_y -= 20 - canvas.setFont(DEFAULT_FONTNAME, MEDIUM_FONTSIZE) - canvas.drawString(current_x, current_y, 'ABN: 38 052 249 024') - self.current_y = current_y - - def __payment_line(self): - canvas = self.canv - current_y, current_x = self.current_y, self.current_x - bpay_logo = ImageReader(BPAY_LOGO) - #current_y -= 40 - # Pay By Cheque - cheque_x = current_x + 4 * inch - cheque_y = current_y -30 - canvas.setFont(BOLD_FONTNAME, MEDIUM_FONTSIZE) - canvas.drawString(cheque_x, cheque_y, 'Pay By Cheque:') - canvas.setFont(DEFAULT_FONTNAME, 9) - cheque_y -= 15 - canvas.drawString(cheque_x, cheque_y, 'Make cheque payable to: Department of Biodiversity, Conservation and Attractions') - cheque_y -= 15 - canvas.drawString(cheque_x, cheque_y, 'Mail to: Department of Biodiversity, Conservation and Attractions') - cheque_y -= 15 - canvas.drawString(cheque_x + 32, cheque_y, 'Locked Bag 30') - cheque_y -= 15 - canvas.drawString(cheque_x + 32, cheque_y, 'Bentley Delivery Centre WA 6983') - if settings.BPAY_ALLOWED: - # Outer BPAY Box - canvas.rect(current_x,current_y - 25,2.3*inch,-1.2*inch) - canvas.setFillColorCMYK(0.8829,0.6126,0.0000,0.5647) - # Move into bpay box - current_y += 5 - box_pos = current_x + 0.1 * inch - bpay_logo_size = bpay_logo.getSize() - canvas.drawImage(bpay_logo, box_pos, current_y - (bpay_logo_size[1]/12 * 1.7), height= bpay_logo_size[1]/12,width=bpay_logo_size[0]/12, mask='auto') - # Create biller information box - biller_x = box_pos + bpay_logo_size[0]/12 + 1 - canvas.rect(biller_x,(current_y - (bpay_logo_size[1]/12 * 1.7)) + 3,1.65*inch,(bpay_logo_size[1]/12)-5) - # Bpay info - canvas.setFont(BOLD_FONTNAME, MEDIUM_FONTSIZE) - info_y = ((current_y - (bpay_logo_size[1]/12 * 1.7)) + 3) + (0.35 * inch) - canvas.drawString(biller_x + 5, info_y, 'Biller Code: {}'.format(self.invoice.biller_code)) - canvas.drawString(biller_x + 5, info_y - 20, 'Ref: {}'.format(self.invoice.reference)) - # Bpay Info string - canvas.setFont(BOLD_FONTNAME,SMALL_FONTSIZE) - canvas.drawString(box_pos, info_y - 0.55 * inch, 'Telephone & Internet Banking - BPAY') - canvas.setFont(DEFAULT_FONTNAME,6.5) - canvas.drawString(box_pos, info_y - 0.65 * inch, 'Contact your bank or financial institution to make') - canvas.drawString(box_pos, info_y - 0.75 * inch, 'this payment from your cheque, savings, debit or') - canvas.drawString(box_pos, info_y - 0.85 * inch, 'transaction account. More info: www.bpay.com.au') - - self.current_y = current_y - - def __footer_line(self): - canvas = self.canv - current_y, current_x = self.current_y, self.current_x - current_y -= 2 * inch - canvas.setFont(DEFAULT_FONTNAME, LARGE_FONTSIZE) - canvas.setFillColor(colors.black) - canvas.drawString(current_x, current_y, 'Invoice Number') - canvas.drawString(PAGE_WIDTH/4, current_y, 'Invoice Date') - canvas.drawString((PAGE_WIDTH/4) * 2, current_y, 'GST included') - canvas.drawString((PAGE_WIDTH/4) * 3, current_y, 'Invoice Total') - current_y -= 20 - canvas.setFont(DEFAULT_FONTNAME, MEDIUM_FONTSIZE) - # canvas.drawString(current_x, current_y, self.invoice.reference) - # canvas.drawString(PAGE_WIDTH/4, current_y, self.invoice.created.strftime(DATE_FORMAT)) - # canvas.drawString((PAGE_WIDTH/4) * 2, current_y, currency(self.invoice.amount - calculate_excl_gst(self.invoice.amount))) - # canvas.drawString((PAGE_WIDTH/4) * 3, current_y, currency(self.invoice.amount)) - - def draw(self): - if settings.BPAY_ALLOWED: - self.__logo_line() - self.__payment_line() - self.__footer_line() - - -def _create_header(canvas, doc, draw_page_number=True): - canvas.saveState() - canvas.setTitle('Invoice') - canvas.setFont(BOLD_FONTNAME, LARGE_FONTSIZE) - - current_y = PAGE_HEIGHT - HEADER_MARGIN - - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO) - dpaw_header_logo_size = dpaw_header_logo.getSize() - canvas.drawImage(dpaw_header_logo, PAGE_WIDTH / 3, current_y - (dpaw_header_logo_size[1]/2),width=dpaw_header_logo_size[0]/2, height=dpaw_header_logo_size[1]/2, mask='auto') - - current_y -= 70 - canvas.drawCentredString(PAGE_WIDTH / 2, current_y - LARGE_FONTSIZE, 'INFRINGEMENT NOTICE') - - current_y -= 20 - canvas.drawCentredString(PAGE_WIDTH / 2, current_y - LARGE_FONTSIZE, 'ABN: 38 052 249 024') - - # Invoice address details - invoice_details_offset = 37 - current_y -= 20 - # invoice = doc.invoice - sanction_outcome = doc.sanction_outcome - canvas.setFont(BOLD_FONTNAME, SMALL_FONTSIZE) - current_x = PAGE_MARGIN + 5 - canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER), sanction_outcome.get_offender()[0].get_full_name()) - # canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER), invoice.owner.get_full_name()) - # canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 2,invoice.owner.username) - current_x += 452 - #write Invoice details - canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER),'Date') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER),invoice.created.strftime(DATE_FORMAT)) - canvas.drawString(current_x, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 2, 'Page') - canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 2, str(canvas.getPageNumber())) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3, 'Invoice Number') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 3, invoice.reference) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 4, 'Total (AUD)') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 4, currency(invoice.amount)) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 5, 'GST included (AUD)') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 5, currency(invoice.amount - calculate_excl_gst(invoice.amount))) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 6, 'Paid (AUD)') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 6, currency(invoice.payment_amount)) - canvas.drawRightString(current_x + 20, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 7, 'Outstanding (AUD)') - # canvas.drawString(current_x + invoice_details_offset, current_y - (SMALL_FONTSIZE + HEADER_SMALL_BUFFER) * 7, currency(invoice.balance)) - canvas.restoreState() +from wildlifecompliance.doctopdf import create_infringement_notice_pdf_contents, create_letter_of_advice_pdf_contents, \ + create_caution_notice_pdf_contents, create_remediation_notice_pdf_contents -def _create_invoice(invoice_buffer, sanction_outcome): - every_page_frame = Frame(PAGE_MARGIN, PAGE_MARGIN + 250, PAGE_WIDTH - 2 * PAGE_MARGIN, - PAGE_HEIGHT - 450, id='EveryPagesFrame', showBoundary=1) - remit_frame = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, - PAGE_HEIGHT - 600, id='RemitFrame', showBoundary=1) - every_page_template = PageTemplate(id='EveryPages', frames=[every_page_frame,remit_frame], onPage=_create_header) - - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[every_page_template], pagesize=A4) - +def create_infringement_notice_pdf(filename, sanction_outcome): + value = create_infringement_notice_pdf_contents(filename, sanction_outcome) + content = ContentFile(value) - # this is the only way to get data into the onPage callback function - doc.sanction_outcome = sanction_outcome - # owner = sanction_outcome.owner + # START: Save the pdf file to the database + document = sanction_outcome.documents.create(name=filename) + path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), content) + document._file = path + document.save() + # END: Save - elements = [] - #elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 5)) + return document - # Draw Products Table - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('GRID',(0, 0), (-1, -1),1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT') - ]) - # items = sanction_outcome.order.lines.all() - # discounts = sanction_outcome.order.basket_discounts - # if sanction_outcome.text: - # elements.append(Paragraph(sanction_outcome.text, styles['Left'])) - # elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 2)) - data = [ - ['Item','Product', 'Quantity','Unit Price', 'Total'] - ] - val = 1 - s = styles["BodyText"] - s.wordWrap = 'CJK' - # for item in items: - # data.append( - # [ - # val, - # Paragraph(item.description, s), - # item.quantity, - # currency(item.unit_price_incl_tax), - # currency(item.line_price_before_discounts_incl_tax) - # ] - # ) - # val += 1 - # Discounts - data.append( - [ - '', - '', - '', - '' - ] - ) - # for discount in discounts: - # data.append( - # [ - # '', - # discount.offer, - # '', - # '', - # '-${}'.format(discount.amount) - # ] - # ) - # val += 1 - t = Table( - data, - style=invoice_table_style, - hAlign='LEFT', - colWidths=( - 0.7 * inch, - None, - 0.7 * inch, - 1.0 * inch, - 1.0 * inch, - ) - ) - elements.append(t) - elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 2)) - # /Products Table - if sanction_outcome.payment_status != 'paid' and sanction_outcome.payment_status != 'over_paid': - elements.append(Paragraph(settings.INVOICE_UNPAID_WARNING, styles['Left'])) +def create_caution_notice_pdf(filename, sanction_outcome): + value = create_caution_notice_pdf_contents(filename, sanction_outcome) + content = ContentFile(value) - elements.append(Spacer(1, SECTION_BUFFER_HEIGHT * 6)) - - # TEST - para1 = Paragraph('Paragraph test1', styles['Left']) - para2 = Paragraph('Paragraph test2', styles['Right']) - elements.append(para1) - # TEST-end + # START: Save the pdf file to the database + document = sanction_outcome.documents.create(name=filename) + path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), content) + document._file = path + document.save() + # END: Save - # Remitttance Frame - elements.append(FrameBreak()) + return document - boundary = BrokenLine(PAGE_WIDTH - 2 * (PAGE_MARGIN *1.1)) - elements.append(boundary) - elements.append(Spacer(1, SECTION_BUFFER_HEIGHT)) - # TEST - elements.append(para2,) - # TEST-end - - remittance = Remittance(HEADER_MARGIN, HEADER_MARGIN - 10, sanction_outcome) - elements.append(remittance) - #_create_remittance(invoice_buffer,doc) - doc.build(elements) - - return invoice_buffer +def create_letter_of_advice_pdf(filename, sanction_outcome): + value = create_letter_of_advice_pdf_contents(filename, sanction_outcome) + content = ContentFile(value) + # START: Save the pdf file to the database + document = sanction_outcome.documents.create(name=filename) + path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), content) + document._file = path + document.save() + # END: Save -def create_infringement_notice_pdf_bytes(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_invoice(invoice_buffer, sanction_outcome) + return document - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save +def create_remediation_notice_pdf_bytes(filename, sanction_outcome): + value = create_remediation_notice_pdf_contents(filename, sanction_outcome) + content = ContentFile(value) - invoice_buffer.close() + # START: Save the pdf file to the database + document = sanction_outcome.documents.create(name=filename) + path = default_storage.save( + 'wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), + content) + document._file = path + document.save() + # END: Save - return document + return document diff --git a/wildlifecompliance/components/sanction_outcome/pdf_caution_notice.py b/wildlifecompliance/components/sanction_outcome/pdf_caution_notice.py deleted file mode 100644 index b4efd0eeb..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_caution_notice.py +++ /dev/null @@ -1,252 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -from io import BytesIO - -from django.core.files.storage import default_storage -# from ledger.payments.pdf import BrokenLine -# from ledger.payments.pdf import BrokenLine -from ledger.payments.pdf import BrokenLine -from wildlifecompliance.settings import STATIC_ROOT -from reportlab.lib.enums import TA_RIGHT, TA_CENTER -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, PageBreak, \ - NextPageTemplate, Image -from reportlab.lib.styles import ParagraphStyle, StyleSheet1 -from reportlab.lib.utils import ImageReader -from reportlab.lib.units import mm -from reportlab.lib import colors - -from django.conf import settings - -from wildlifecompliance.components.main.pdf_utils import gap, SolidLine, get_font_str - -PAGE_WIDTH, PAGE_HEIGHT = A4 -DEFAULT_FONTNAME = 'Helvetica' -BOLD_FONTNAME = 'Helvetica-Bold' - -DPAW_HEADER_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo.jpg') -DPAW_HEADER_LOGO_SM = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo_small.png') -BPAY_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img', 'BPAY_2012_PORT_BLUE.png') -HEADER_MARGIN = 10 -HEADER_SMALL_BUFFER = 3 -PAGE_TOP_MARGIN = 200 -VERY_LARGE_FONTSIZE = 14 -LARGE_FONTSIZE = 12 -MEDIUM_FONTSIZE = 10 -SMALL_FONTSIZE = 8 -PARAGRAPH_BOTTOM_MARGIN = 5 -SECTION_BUFFER_HEIGHT = 10 -DATE_FORMAT = '%d/%m/%Y' - - -def _create_pdf(invoice_buffer, sanction_outcome): - PAGE_MARGIN = 5 * mm - page_frame_1 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='PagesFrame1', ) #showBoundary=Color(0, 1, 0)) - PAGE_MARGIN2 = 17 * mm - page_frame_2 = Frame(PAGE_MARGIN2, PAGE_MARGIN2, PAGE_WIDTH - 2 * PAGE_MARGIN2, PAGE_HEIGHT - 2 * PAGE_MARGIN2, id='PagesFrame2', ) #showBoundary=Color(0, 0, 1)) - page_template_1 = PageTemplate(id='Page1', frames=[page_frame_1, ], ) - page_template_2 = PageTemplate(id='Page2', frames=[page_frame_2, ], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[page_template_1, page_template_2], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - # Common - FONT_SIZE_L = 11 - FONT_SIZE_M = 10 - FONT_SIZE_S = 8 - - styles = StyleSheet1() - styles.add(ParagraphStyle(name='Normal', - fontName='Helvetica', - fontSize=FONT_SIZE_M, - spaceBefore=7, # space before paragraph - spaceAfter=7, # space after paragraph - leading=12)) # space between lines - styles.add(ParagraphStyle(name='BodyText', - parent=styles['Normal'], - spaceBefore=6)) - styles.add(ParagraphStyle(name='Italic', - parent=styles['BodyText'], - fontName='Helvetica-Italic')) - styles.add(ParagraphStyle(name='Bold', - parent=styles['BodyText'], - fontName='Helvetica-Bold', - alignment = TA_CENTER)) - styles.add(ParagraphStyle(name='Right', - parent=styles['BodyText'], - alignment=TA_RIGHT)) - styles.add(ParagraphStyle(name='Centre', - parent=styles['BodyText'], - alignment=TA_CENTER)) - - # Logo - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO) - dpaw_header_logo_size = dpaw_header_logo.getSize() - width = dpaw_header_logo_size[0]/2 - height = dpaw_header_logo_size[1]/2 - dbca_logo = Image(DPAW_HEADER_LOGO, width=width, height=height) - - # Table - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - - ('SPAN', (0, 0), (1, 0)), - - # Alleged offender - ('SPAN', (0, 1), (0, 5)), - ('SPAN', (1, 1), (2, 1)), - ('SPAN', (1, 2), (2, 2)), - ('SPAN', (1, 3), (2, 3)), - ('SPAN', (1, 4), (2, 4)), - ('SPAN', (1, 5), (2, 5)), - - # When - ('SPAN', (1, 6), (2, 6)), - - # Where - ('SPAN', (1, 7), (2, 7)), - - # Alleged offence - ('SPAN', (0, 8), (0, 9)), - ('SPAN', (1, 8), (2, 8)), - # ('SPAN', (1, 9), (2, 9)), - ('SPAN', (1, 9), (2, 9)), - # ('SPAN', (1, 10), (2, 10)), - - # Officer issuing notice - ('SPAN', (0, 10), (0, 12)), - ('SPAN', (1, 10), (2, 10)), - ('SPAN', (1, 11), (2, 11)), - ('SPAN', (1, 12), (2, 12)), - - # Date - ('SPAN', (1, 13), (2, 13)), - - # Notice to alleged offender - ('SPAN', (1, 14), (2, 14)), - ]) - date_str = gap(10) + '/' + gap(10) + '/ 20' - col_width = [40*mm, 60*mm, 80*mm, ] - - data = [] - data.append([Paragraph('Biodiversity Conservation Act 2016
Caution Notice', styles['Normal']), - '', - Paragraph(u'Caution
notice no. ' + get_font_str(sanction_outcome.lodgement_number), styles['Normal'])]) - - # Alleged offender - offender = sanction_outcome.get_offender() - offender_dob = offender[0].dob.strftime('%d/%m/%Y') if offender[0].dob else '' - data.append([Paragraph('Alleged offender', styles['Bold']), Paragraph('Name: Family name: ' + get_font_str(offender[0].last_name), styles['Normal']), '']) - data.append(['', Paragraph(gap(12) + 'Given names: ' + get_font_str(offender[0].first_name), styles['Normal']), '']) - data.append(['', Paragraph(gap(12) + 'Date of Birth: ' + get_font_str(offender_dob), styles['Normal']), '']) - data.append(['', [Paragraph('or
Body corporate name', styles['Normal']), Spacer(1, 25)], '']) - # data.append(['', [Paragraph('Address', styles['Normal']), Spacer(1, 25), Paragraph('Postcode', styles['Normal'])], '']) - postcode = offender[0].residential_address.postcode if offender[0].residential_address else '' - data.append(['', - [ - Paragraph('Address: ', styles['Normal']), - Paragraph(get_font_str(str(offender[0].residential_address)), styles['Normal']), - Paragraph('Postcode: ' + get_font_str(postcode), styles['Normal']), - ], - '', - ]) - - # When - # data.append([Paragraph('When', styles['Bold']), Paragraph('Date' + date_str + gap(5) + 'Time' + gap(10) + 'am/pm', styles['Normal']), '']) - offence_datetime = sanction_outcome.offence.offence_occurrence_datetime - data.append([ - Paragraph('When', styles['Bold']), - Paragraph('Date: ' + get_font_str(offence_datetime.strftime('%d/%m/%Y')) + gap(5) + 'Time: ' + get_font_str(offence_datetime.strftime('%I:%M %p')), styles['Normal']), - '' - ]) - - # Where - # data.append([Paragraph('Where', styles['Bold']), [Paragraph('Location of offence', styles['Normal']), Spacer(1, 25)], '']) - offence_location = sanction_outcome.offence.location - offence_location_str = str(offence_location) if offence_location else '' - data.append([ - Paragraph('Where', styles['Bold']), - [ - Paragraph('Location of offence', styles['Normal']), - Paragraph(get_font_str(offence_location_str), styles['Normal']), - Spacer(1, 25) - ], - '', - ]) - - # Alleged offence - data.append([Paragraph('Alleged offence', styles['Bold']), - [ - Paragraph('Description of offence', styles['Normal']), - ], - '']) # row index: 8 - # data.append(['', rect, '']) - # data.append(['', '?', '']) - data.append(['', Paragraph('Biodiversity Conservation Act 2016 s.' + gap(10) + 'or
Biodiversity Conservation Regulations 2018 r.', styles['Normal']), '']) - - # Officer issuing notice - # data.append([Paragraph('Officer issuing notice', styles['Bold']), Paragraph('Name', styles['Normal']), '']) # row index: 12 - data.append([ - Paragraph('Officer issuing notice', styles['Bold']), - Paragraph('Name: {}'.format(get_font_str(sanction_outcome.responsible_officer.get_full_name())), styles['Normal']), '' - ]) # row index: 12 - data.append(['', Paragraph('Signature', styles['Normal']), '']) - data.append(['', Paragraph('Officer no.', styles['Normal']), '']) - - # Date - issue_date = get_font_str(sanction_outcome.date_of_issue.strftime('%d/%m/%Y')) - issue_time = get_font_str(sanction_outcome.time_of_issue.strftime('%I:%M %p')) - data.append([Paragraph('Date', styles['Bold']), Paragraph('Date of notice: ' + issue_date + ' ' + issue_time, styles['Normal']), '']) - - # Notice to alleged offender - body = [] - body.append(Paragraph('It is alleged that you have committed the above offence.

', styles['Normal'])) - body.append(Paragraph('If you object to the issue of this notice you can apply in writing to the Approved Officer at the below address for a review within 28 days after the date of this notice.', styles['Normal'])) - body.append(Paragraph('Approved Officer — Biodiversity Conservation Act 2016
Department of Biodiversity, Conservation and Attractions
Locked Bag 104
Bentley Delivery Centre WA 6983', styles['Normal'])) - data.append([Paragraph('Notice to alleged offender', styles['Bold']), body, '']) - - # Create 1st table - t1 = Table(data, style=invoice_table_style, colWidths=col_width) - - # Append tables to the elements to build - gap_between_tables = 1.5*mm - elements = [] - elements.append(dbca_logo) - elements.append(Spacer(0, 5*mm)) - elements.append(t1) - elements.append(NextPageTemplate(['Page2', ])) - elements.append(PageBreak()) - elements.append(dbca_logo) - elements.append(Spacer(0, 5*mm)) - # ORIGINAL NOTES OF INCIDENT: - title_original_notes = Paragraph('ORIGINAL NOTES OF INCIDENT:', styles['Normal']) - elements.append(title_original_notes) - elements.append(Spacer(0, 10*mm)) - for i in range(0, 25): - elements.append(BrokenLine(170*mm, 8*mm)) - elements.append(Paragraph('Signature: ' + gap(80) + 'Date:', styles['Normal'])) - - doc.build(elements) - return invoice_buffer - - -def create_caution_notice_pdf_bytes(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_pdf(invoice_buffer, sanction_outcome) - - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save - - invoice_buffer.close() - - return document - - diff --git a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_blue.py b/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_blue.py deleted file mode 100644 index db2d5b520..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_blue.py +++ /dev/null @@ -1,137 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -from io import BytesIO - -from django.core.files.base import ContentFile -from django.core.files.storage import default_storage -from reportlab.lib import enums -from reportlab.lib.colors import Color -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, PageBreak -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle -from reportlab.lib.units import mm -from reportlab.lib import colors - -from django.conf import settings - -from wildlifecompliance.components.main.pdf_utils import gap, get_infringement_notice_table -from wildlifecompliance.doctopdf import create_infringement_notice_pdf_contents -from wildlifecompliance.settings import STATIC_ROOT - -DPAW_HEADER_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo.jpg') -DPAW_HEADER_LOGO_SM = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo_small.png') -BPAY_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img', 'BPAY_2012_PORT_BLUE.png') - -HEADER_MARGIN = 10 -HEADER_SMALL_BUFFER = 3 - -PAGE_MARGIN = 20 * mm # mm? - -PAGE_WIDTH, PAGE_HEIGHT = A4 - -DEFAULT_FONTNAME = 'Helvetica' -BOLD_FONTNAME = 'Helvetica-Bold' - -VERY_LARGE_FONTSIZE = 14 -LARGE_FONTSIZE = 12 -MEDIUM_FONTSIZE = 10 -SMALL_FONTSIZE = 8 - -PARAGRAPH_BOTTOM_MARGIN = 5 - -SECTION_BUFFER_HEIGHT = 10 - -DATE_FORMAT = '%d/%m/%Y' - -styles = getSampleStyleSheet() -styles.add(ParagraphStyle(name='InfoTitleLargeCenter', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleVeryLargeCenter', fontName=BOLD_FONTNAME, fontSize=VERY_LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN * 2, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleLargeLeft', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_LEFT, - leftIndent=PAGE_WIDTH / 10, rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='InfoTitleLargeRight', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_RIGHT, - rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='BoldLeft', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='BoldRight', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='Center', alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='Left', alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='Right', alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='LongString', alignment=enums.TA_LEFT,wordWrap='CJK')) - - -def _create_pdf(invoice_buffer, sanction_outcome): - every_page_frame = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='EveryPagesFrame',) # showBoundary=Color(0, 1, 0)) - every_page_frame2 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='EveryPagesFrame2',) # showBoundary=Color(0, 0, 1)) - # every_page_template = PageTemplate(id='EveryPages', frames=[every_page_frame,], onPage=_create_header) - every_page_template = PageTemplate(id='EveryPages', frames=[every_page_frame,], ) - every_page_template2 = PageTemplate(id='EveryPages2', frames=[every_page_frame2,], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[every_page_template, every_page_template2,], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - t1 = get_infringement_notice_table(sanction_outcome) - - data_tbl2 = [] - # Notice to alleged offender (1) - body = [] - body.append(Paragraph('It is alleged that you have committed the above offence.

', styles['Normal'])) - body.append(Paragraph('If you do not want to be prosecuted in court for the offence, pay the modified penalty within 28 days after the date of this notice.', styles['Normal'])) - body.append(Paragraph('How to pay', styles['Normal'])) - body.append(Paragraph('By post:Send a cheque or money order (payable to ‘Approved Officer — Biodiversity Conservation Act 2016’) to:', styles['Normal'])) - body.append(Paragraph('Approved Officer — Biodiversity Conservation Act 2016
Department of Biodiversity, Conservation and Attractions
Locked Bag 104
Bentley Delivery Centre WA 6983', styles['Normal'])) - body.append(Spacer(1, 10)) - body.append(Paragraph('In person: Pay the cashier at any office of the Department of Biodiversity, Conservation and Attractions, or pay over the telephone by credit card by calling the general telephone number of any office of the Department of Biodiversity, Conservation and Attractions.', styles['Normal'])) - data_tbl2.append([Paragraph('Notice to alleged offender', styles['Normal']), body, '']) - - # Notice to alleged offender (2) - body = [] - body.append(Paragraph('If you do not pay the modified penalty within 28 days, you may be prosecuted or enforcement action may be taken under the Fines, Penalties and Infringement Notices Enforcement Act 1994. Under that Act, some or all of the following action may be taken — your driver’s licence may be suspended; your vehicle licence may be suspended or cancelled; your details may be published on a website; your vehicle may be immobilised or have its number plates removed; your property may be seized and sold.', styles['Normal'])) - body.append(Spacer(1, 10)) - body.append(Paragraph('If you need more time. to pay the modified penalty, you can apply for an extension of time by writing to the Approved Officer at the above postal address.', styles['Normal'])) - data_tbl2.append(['', body, '']) - - # Notice to alleged offender (3) - body = [] - body.append(Paragraph('If you want this matter to be dealt with by prosecution in court, sign here ' + gap(80) + ' and post this notice to the Approved Officer at the above postal address within 28 days after the date of this notice.', styles['Normal'])) - data_tbl2.append(['', body, '']) - - # Create 2nd table - invoice_table_style2 = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - # Notice to alleged offender - ('SPAN', (0, 0), (0, 2)), - ('SPAN', (1, 0), (2, 0)), - ('SPAN', (1, 1), (2, 1)), - ('SPAN', (1, 2), (2, 2)), - ]) - col_width = [40*mm, 60*mm, 80*mm,] - t2 = Table(data_tbl2, style=invoice_table_style2, colWidths=col_width) - - elements = [] - # elements.append(NextPageTemplate('EveryPages2')) - elements.append(t1) - elements.append(PageBreak()) - elements.append(t2) - - doc.build(elements) - return invoice_buffer - - -def create_infringement_notice_blue(filename, sanction_outcome): - value = create_infringement_notice_pdf_contents(filename, sanction_outcome) - content = ContentFile(value) - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), content) - document._file = path - document.save() - # END: Save - - return document - - diff --git a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_white.py b/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_white.py deleted file mode 100644 index 3b39caa9f..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_white.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -from io import BytesIO -from django.core.files.storage import default_storage -from reportlab.lib import enums, colors -from reportlab.lib.pagesizes import A4 -from reportlab.lib.units import mm -from reportlab.lib.utils import ImageReader -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBreak, Paragraph, Spacer, TableStyle, Table, \ - Image -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle, StyleSheet1 -from wildlifecompliance.components.sanction_outcome.pdf_infringement_notice_blue import BOLD_FONTNAME, LARGE_FONTSIZE, \ - VERY_LARGE_FONTSIZE, MEDIUM_FONTSIZE, PAGE_MARGIN, PAGE_WIDTH, \ - PARAGRAPH_BOTTOM_MARGIN, PAGE_HEIGHT, DPAW_HEADER_LOGO -from wildlifecompliance.components.main.pdf_utils import get_infringement_notice_table, BrokenLine, SolidLine, gap, \ - YesNoCheckbox, ParagraphCheckbox, get_font_str - -styles = getSampleStyleSheet() -styles.add(ParagraphStyle(name='InfoTitleLargeCenter', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleVeryLargeCenter', fontName=BOLD_FONTNAME, fontSize=VERY_LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN * 2, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleLargeLeft', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_LEFT, - leftIndent=PAGE_WIDTH / 10, rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='InfoTitleLargeRight', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_RIGHT, - rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='BoldLeft', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='BoldRight', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='Center', alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='Left', alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='Right', alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='LongString', alignment=enums.TA_LEFT,wordWrap='CJK')) - - -def _create_pdf(invoice_buffer, sanction_outcome): - MARGIN_TOP = 5 - MARGIN_BOTTOM = 5 - every_page_frame = Frame(PAGE_MARGIN, MARGIN_TOP, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - (MARGIN_TOP + MARGIN_BOTTOM), id='EveryPagesFrame',) # showBoundary=Color(0, 1, 0)) - # every_page_frame2 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='EveryPagesFrame2',) # showBoundary=Color(0, 0, 1)) - every_page_template = PageTemplate(id='EveryPages', frames=[every_page_frame,], ) - # every_page_template2 = PageTemplate(id='EveryPages2', frames=[every_page_frame2,], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[every_page_template, ], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - # doc = BaseDocTemplate(invoice_buffer, pageTemplates=[every_page_template, every_page_template2,], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - # Logo - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO) - dpaw_header_logo_size = dpaw_header_logo.getSize() - width = dpaw_header_logo_size[0]/2 - height = dpaw_header_logo_size[1]/2 - dbca_logo = Image(DPAW_HEADER_LOGO, width=width, height=height) - - # 1st page - t1 = get_infringement_notice_table(sanction_outcome) - - # 2nd page - title_evidential_notes = Paragraph('EVIDENTIAL NOTES:', styles['Normal']) - - # table - col_width = [40*mm, 35*mm, 22*mm, 31*mm, 37*mm, ] - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), - ('GRID', (0, 0), (-1, -1), 0.5, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('SPAN', (1, 0), (4, 0)), - ('SPAN', (1, 1), (4, 1)), - ('SPAN', (1, 2), (4, 2)), - ('SPAN', (1, 3), (4, 3)), - ('SPAN', (2, 5), (3, 5)), - ('SPAN', (2, 6), (4, 6)), - ]) - data = [] - rego = sanction_outcome.registration_number if sanction_outcome.registration_number else '' - data.append([ - Paragraph('Registration number
vehicle/vessel:
', styles['Normal']), - Paragraph(get_font_str(rego), styles['Normal']), - '', '', '', - ]) - data.append([ - Paragraph('Make:', styles['Normal']), - '', '', '', '', - ]) - data.append([ - Paragraph('Model:', styles['Normal']), - '', '', '', '', - ]) - data.append([ - Paragraph('Type: i.e SUV etc', styles['Normal']), - '', '', '', '', - ]) - - data.append([ - Paragraph('Photograph(s) taken:', styles['Normal']), - YesNoCheckbox(11, 11, fontName='Helvetica'), - Paragraph('Attached:', styles['Normal']), - YesNoCheckbox(11, 11, fontName='Helvetica'), - Paragraph('Number:', styles['Normal']), - ]) - data.append([ - Paragraph('Gender:
Tick relevant box
', styles['Normal']), - ParagraphCheckbox('Male', styles['Normal'],), - ParagraphCheckbox('Female', styles['Normal'], ), - '', - ParagraphCheckbox('Other/unknown', styles['Normal'], ), - ]) - data.append([ - Paragraph('Identification
produced:
', styles['Normal']), - YesNoCheckbox(11, 11, fontName='Helvetica'), - Paragraph('If Yes, provide details:', styles['Normal']), - '', '', - ]) - t2 = Table(data, style=invoice_table_style, colWidths=col_width) - - title_original_notes = Paragraph('ORIGINAL NOTES OF INCIDENT:', styles['Normal']) - - elements = [] - elements.append(dbca_logo) - elements.append(t1) - elements.append(PageBreak()) - elements.append(dbca_logo) - elements.append(title_evidential_notes) - elements.append(Spacer(0, 5*mm)) - elements.append(t2) - elements.append(Spacer(0, 5*mm)) - elements.append(title_original_notes) - for i in range(0, 23): - elements.append(SolidLine(440, 0, dashed=True, wrap_height=20)) - elements.append(Spacer(0, 10*mm)) - elements.append(Paragraph('Signature:' + gap(50) + 'Date:', styles['Normal'])) - - doc.build(elements) - return invoice_buffer - - -def create_infringement_notice_white(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_pdf(invoice_buffer, sanction_outcome) - - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save - - invoice_buffer.close() - - return document - - diff --git a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_yellow.py b/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_yellow.py deleted file mode 100644 index 0f2e0fa5e..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_infringement_notice_yellow.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -from io import BytesIO -from django.core.files.storage import default_storage -from reportlab.lib import enums -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, PageBreak, Paragraph -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle, StyleSheet1 -from wildlifecompliance.components.sanction_outcome.pdf_infringement_notice_blue import BOLD_FONTNAME, LARGE_FONTSIZE, VERY_LARGE_FONTSIZE, MEDIUM_FONTSIZE, PAGE_MARGIN, PAGE_WIDTH, \ - PARAGRAPH_BOTTOM_MARGIN, PAGE_HEIGHT -from wildlifecompliance.components.main.pdf_utils import get_infringement_notice_table - -styles = getSampleStyleSheet() -styles.add(ParagraphStyle(name='InfoTitleLargeCenter', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleVeryLargeCenter', fontName=BOLD_FONTNAME, fontSize=VERY_LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN * 2, alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='InfoTitleLargeLeft', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_LEFT, - leftIndent=PAGE_WIDTH / 10, rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='InfoTitleLargeRight', fontName=BOLD_FONTNAME, fontSize=LARGE_FONTSIZE, - spaceAfter=PARAGRAPH_BOTTOM_MARGIN, alignment=enums.TA_RIGHT, - rightIndent=PAGE_WIDTH / 10)) -styles.add(ParagraphStyle(name='BoldLeft', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='BoldRight', fontName=BOLD_FONTNAME, fontSize=MEDIUM_FONTSIZE, alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='Center', alignment=enums.TA_CENTER)) -styles.add(ParagraphStyle(name='Left', alignment=enums.TA_LEFT)) -styles.add(ParagraphStyle(name='Right', alignment=enums.TA_RIGHT)) -styles.add(ParagraphStyle(name='LongString', alignment=enums.TA_LEFT,wordWrap='CJK')) - - -def _create_pdf(invoice_buffer, sanction_outcome): - every_page_frame = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='EveryPagesFrame',) # showBoundary=Color(0, 1, 0)) - every_page_frame2 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='EveryPagesFrame2',) # showBoundary=Color(0, 0, 1)) - every_page_template = PageTemplate(id='EveryPages', frames=[every_page_frame,], ) - every_page_template2 = PageTemplate(id='EveryPages2', frames=[every_page_frame2,], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[every_page_template, every_page_template2,], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - t1 = get_infringement_notice_table(sanction_outcome) - - elements = [] - elements.append(t1) - elements.append(PageBreak()) - - doc.build(elements) - return invoice_buffer - - -def create_infringement_notice_yellow(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_pdf(invoice_buffer, sanction_outcome) - - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save - - invoice_buffer.close() - - return document - - diff --git a/wildlifecompliance/components/sanction_outcome/pdf_letter_of_advice.py b/wildlifecompliance/components/sanction_outcome/pdf_letter_of_advice.py deleted file mode 100644 index ea33a81f0..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_letter_of_advice.py +++ /dev/null @@ -1,357 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -from decimal import Decimal as D -from io import BytesIO - -from django.core.files.storage import default_storage -from wildlifecompliance.settings import STATIC_ROOT -from oscar.templatetags.currency_filters import currency -from reportlab.lib import enums -from reportlab.lib.colors import Color -from reportlab.lib.enums import TA_RIGHT, TA_CENTER -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, ListFlowable, \ - KeepTogether, PageBreak, Flowable, NextPageTemplate, FrameBreak, Image -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle, StyleSheet1 -from reportlab.lib.utils import ImageReader -from reportlab.pdfgen.canvas import Canvas -from reportlab.pdfbase import pdfmetrics -from reportlab.lib.units import inch, mm -from reportlab.lib import colors - -from django.core.files import File -from django.conf import settings - -from ledger.accounts.models import Document, unicode_compatible -from ledger.checkout.utils import calculate_excl_gst - -from wildlifecompliance.components.main.pdf_utils import gap, SolidLine, get_font_str - -PAGE_WIDTH, PAGE_HEIGHT = A4 -DEFAULT_FONTNAME = 'Helvetica' -BOLD_FONTNAME = 'Helvetica-Bold' - -DPAW_HEADER_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo.jpg') -DPAW_HEADER_LOGO_SM = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo_small.png') -BPAY_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img', 'BPAY_2012_PORT_BLUE.png') -HEADER_MARGIN = 10 -HEADER_SMALL_BUFFER = 3 -PAGE_TOP_MARGIN = 200 -VERY_LARGE_FONTSIZE = 14 -LARGE_FONTSIZE = 12 -MEDIUM_FONTSIZE = 10 -SMALL_FONTSIZE = 8 -PARAGRAPH_BOTTOM_MARGIN = 5 -SECTION_BUFFER_HEIGHT = 10 -DATE_FORMAT = '%d/%m/%Y' - - -def _create_pdf(invoice_buffer, sanction_outcome): - PAGE_MARGIN = 5 * mm - page_frame_1 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='PagesFrame1', ) #showBoundary=Color(0, 1, 0)) - page_template_1 = PageTemplate(id='Page1', frames=[page_frame_1, ], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[page_template_1, ], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - # Common - FONT_SIZE_L = 11 - FONT_SIZE_M = 10 - FONT_SIZE_S = 8 - - styles = StyleSheet1() - style_normal = ParagraphStyle(name='Normal', - fontName='Helvetica', - fontSize=FONT_SIZE_M, - spaceBefore=7, # space before paragraph - spaceAfter=7, # space after paragraph - leading=12) # space between lines - styles.add(style_normal) - styles.add(ParagraphStyle(name='BodyText', - parent=styles['Normal'], - spaceBefore=6)) - styles.add(ParagraphStyle(name='Italic', - parent=styles['BodyText'], - fontName='Helvetica-Italic')) - styles.add(ParagraphStyle(name='Bold', - parent=styles['BodyText'], - fontName='Helvetica-Bold', - alignment = TA_CENTER)) - styles.add(ParagraphStyle(name='Right', - parent=styles['BodyText'], - alignment=TA_RIGHT)) - styles.add(ParagraphStyle(name='Centre', - parent=styles['BodyText'], - alignment=TA_CENTER)) - - # Logo - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO) - dpaw_header_logo_size = dpaw_header_logo.getSize() - width = dpaw_header_logo_size[0]/2 - height = dpaw_header_logo_size[1]/2 - dbca_logo = Image(DPAW_HEADER_LOGO, width=width, height=height) - - # Table - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('VALIGN', (1, 3), (1, 3), 'MIDDLE'), - ('GRID', (0, 0), (-1, -1), 0.5, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('SPAN', (1, 1), (2, 1)), - ('SPAN', (0, 2), (0, 3)), - ('SPAN', (1, 4), (2, 4)), - ('SPAN', (1, 5), (2, 5)), - ('SPAN', (1, 6), (2, 6)), - ('SPAN', (1, 7), (2, 7)), - ('SPAN', (1, 8), (2, 8)), - ]) - date_str = gap(10) + '/' + gap(10) + '/' - col_width = [30*mm, 125*mm, 25*mm, ] - - data = [] - data.append([ - Paragraph('Western Australia', style_normal), - Paragraph('Biodiversity Conservation Act 2016
Obtaining Records and Directions', style_normal), - Paragraph('No.
' + get_font_str(str(sanction_outcome.lodgement_number)), style_normal), - ]) - offender = sanction_outcome.get_offender() - data.append([ - Paragraph('To', style_normal), - [ - Paragraph('Given names: ' + get_font_str(offender[0].first_name), style_normal), - Paragraph('Surname: ' + get_font_str(offender[0].last_name), style_normal), - Spacer(0, 3*mm), - ], - '', - ]) - fields = [offender[0].residential_address.line1, offender[0].residential_address.line2, offender[0].residential_address.line3] if offender[0].residential_address else [] - fields = [unicode_compatible(f).encode('utf-8').decode('unicode-escape').strip() for f in fields if f] - no_and_street = ', '.join(fields) - postcode = offender[0].residential_address.postcode if offender[0].residential_address else '' - town_suburb = offender[0].residential_address.locality if offender[0].residential_address else '' - data.append([ - Paragraph('Address', style_normal), - [ - Paragraph('No and Street:
' + get_font_str(str(no_and_street)), style_normal), - Paragraph('Town/Suburb:
' + get_font_str(town_suburb), style_normal), - ], - Paragraph('Post Code:
' + get_font_str(postcode), style_normal), - ]) - offender_dob = offender[0].dob.strftime('%d/%m/%Y') if offender[0].dob else '' - data.append(([ - '', - Paragraph('Date of Birth: ' + get_font_str(offender_dob), style_normal), - [ - Paragraph('Gender:', style_normal), - Paragraph('M' + gap(3) + 'F' + gap(3) + 'U', style_normal), - ], - ])) - data.append([ - Paragraph('Direction

* Delete as appropriate.

# Enter details of direction given.
', style_normal), - [Paragraph('Under the Biodiversity Conservation Act 2016 s204(2) (a), (b), (d)*, I direct you to - #
', style_normal), - Spacer(0, 20*mm), - Paragraph('Under the Biodiversity Conservation Act 2016 s205(2), I direct you to - #', style_normal), - Spacer(0, 20*mm), - ], - '', - ]) - data.append([ - Paragraph('Warning', style_normal), - Paragraph('If you do not obey this direction you may be liable to a fine of $10,000.00.', style_normal), - '', - ]) - region_district = ' / '.join(list(filter(None, [get_font_str(sanction_outcome.district), get_font_str(sanction_outcome.region)]))) - data.append([ - Paragraph('Issuing officer’s signature and details', style_normal), - [ - Paragraph('I issue this direction on this date and at this time
' - 'Date: ' + get_font_str(sanction_outcome.date_of_issue.strftime('%d/%m/%Y')) + '
' - 'Time: ' + get_font_str(sanction_outcome.time_of_issue.strftime('%I:%M %p')), style_normal), - Paragraph('Name and AO Number: ' + get_font_str(sanction_outcome.responsible_officer.get_full_name()), style_normal), - Paragraph('Signature', style_normal), - Paragraph('District/Region: ' + region_district, style_normal), - ], - '', - ]) - # Paragraph('District/Region: ' + get_font_str(sanction_outcome.district) + ' / ' + get_font_str(sanction_outcome.region), style_normal), - data.append([ - Paragraph('Witnessing officer', style_normal), - [ - Paragraph('Name and AO Number:', style_normal), - Paragraph('Signature: ', style_normal), - Paragraph('District/Region:', style_normal), - ], - '', - ]) - data.append([ - Paragraph('Recipient\'s signature [Optional]', style_normal), - Paragraph('I acknowledge receiving this direction. I understand what it says', style_normal), - '' - ]) - t1 = Table(data, style=invoice_table_style, colWidths=col_width, ) - - # Table 2 - col_width = [30*mm, 150*mm, ] - col_width_internal = [15*mm, 130*mm, ] - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('GRID', (0, 0), (-1, -1), 0.5, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ('TOPPADDING', (0, 0), (-1, -1), 2), - ('BOTTOMPADDING', (0, 0), (-1, -1), 2), - ('BACKGROUND', (0, 0), (1, 0), (0.8, 0.8, 0.8)), - ('SPAN', (0, 0), (1, 0)), - ('SPAN', (0, 1), (1, 1)), - ('BACKGROUND', (0, 5), (1, 5), (0.8, 0.8, 0.8)), - ('SPAN', (0, 5), (1, 5)), - ('SPAN', (0, 6), (1, 6)), - ]) - data = [] - data.append([ - Paragraph('Section 204 - Obtaining records:', styles['Centre']), - '', - ]) - data.append([ - Paragraph('For inspection purposes a wildlife officer may do one or more of the following -', styles['Centre']), - '', - ]) - tbl_style_internal = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ]) - data.append([ - Paragraph('s204(2)(a)', style_normal), - Table([[ - Paragraph('a)', styles['Right']), - Paragraph('direct a person who has the custody or control of a record to give the wildlife officer the record or a copy of it;', style_normal), - ]], style=tbl_style_internal, colWidths=col_width_internal), - ]) - data.append([ - Paragraph('s204(2)(b)', style_normal), - Table([[ - Paragraph('b)', styles['Right']), - Paragraph('direct a person who has the custody or control of a record, computer or thing to make or print out a copy of the record or to operate the computer or thing;', style_normal), - ]], style=tbl_style_internal, colWidths=col_width_internal), - ]) - data.append([ - Paragraph('s204(2)(d)', style_normal), - Table([[ - Paragraph('d)', styles['Right']), - Paragraph('direct a person who is or appears to be in control of a record that the wildlife officer reasonably suspects is a relevant record to give the wildlife officer a translation, code, password or other information necessary to gain access to or interpret and understand the record.', style_normal), - ]], style=tbl_style_internal, colWidths=col_width_internal), - ]) - data.append([ - Paragraph('Section 205 - Directions', styles['Centre']), - '', - ]) - data.append([ - Paragraph('A wildlife officer may do one or more of the following -', styles['Centre']), - '', - ]) - data.append([ - Paragraph('s205(2)(a)(i)
s205(2)(a)(ii)', style_normal), - [ Paragraph('For inspection purposes direct an occupier of a place or vehicle, or a person who is or appears to be in possession or control of a thing, to give to the wildlife officer, orally or in writing -', style_normal), - Table([ - [ - Paragraph('(i)', styles['Right']), - Paragraph( - 'any information in the person’s possession or control as to the name and address of the owner of the place, vehicle or thing; and', - style_normal), - ], - [ - Paragraph('(ii)', styles['Right']), - Paragraph( - 'any other information in the person’s possession or control that is relevant to an inspection', - style_normal), - ], - ], style=tbl_style_internal, colWidths=col_width_internal), - ], - ]) - data.append([ - Paragraph('s205(2)(b)', style_normal), - Paragraph('For inspection purposes direct a person who is or appears to be in possession or control of an organism or potential carrier to give the wildlife officer any information in the person’s possession or control as to the name and address of any person from whom the organism or potential carrier or to whom a similar organism or potential carrier has been supplied;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(c)', style_normal), - Paragraph('For inspection purposes direct an occupier of a place or vehicle to answer questions;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(d)', style_normal), - Paragraph('For inspection purposes direct an occupier of a place or vehicle to produce a specified thing or a thing of a specified kind;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(e)', style_normal), - Paragraph('For inspection purposes direct an occupier of a place or vehicle to open or unlock any thing in or on the place or vehicle to which the wildlife officer requires access;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(f)', style_normal), - Paragraph('Direct an occupier of a place or vehicle to give the wildlife officer a plan, or access to a plan, of the place or vehicle;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(g)', style_normal), - Paragraph('Direct an occupier of a place or vehicle, or a person who is or appears to be in possession or control of a thing, to give the wildlife officer any assistance that the wildlife officer reasonably needs to carry out the wildlife officer’s functions in relation to the place, vehicle or thing;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(h)', style_normal), - Paragraph('Direct an occupier of a vehicle to move the vehicle to a specified place for inspection or treatment;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(i)', style_normal), - Paragraph('direct a person who is or could be carrying an organism or potential carrier to go to a specified place for inspection or treatment;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(j)', style_normal), - Paragraph('Direct a person who is or appears to be in control of a consignment of goods or a potential carrier to move the consignment or potential carrier to a specified place for inspection or treatment;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(k)', style_normal), - Paragraph('Direct a person who is or appears to be in control of an organism to do anything necessary to identify the organism;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(l)', style_normal), - Paragraph('Direct a person who is or appears to be in control of an animal to restrain, muster, round up, yard, draft or otherwise move or handle the animal or to remove the animal to a specified place for inspection or treatment;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(m)', style_normal), - Paragraph('Direct a person who is or appears to be in control of any goods, vehicle, package or container to label it;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(n)', style_normal), - Paragraph('Direct a person who is or appears to be in control of an organism, potential carrier or other thing prohibited, controlled, regulated or managed under this Act to keep that organism, potential carrier or other thing in the possession of that person until further directed by the wildlife officer;', style_normal), - ]) - data.append([ - Paragraph('s205(2)(o)', style_normal), - Paragraph('Direct a person who is or appears to be in control of an organism, potential carrier or other thing prohibited, controlled, regulated or managed under this Act to leave that organism, potential carrier or other thing at a specified place until further directed by the wildlife officer.', style_normal), - ]) - t2 = Table(data, style=invoice_table_style, colWidths=col_width, ) - - - # Append tables to the elements to build - elements = [] - elements.append(dbca_logo) - elements.append(Spacer(0, 5*mm)) - elements.append(t1) - elements.append(PageBreak()) - elements.append(Spacer(0, 10*mm)) - elements.append(t2) - - doc.build(elements) - return invoice_buffer - - -def create_letter_of_advice_pdf_bytes(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_pdf(invoice_buffer, sanction_outcome) - - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save - - invoice_buffer.close() - - return document diff --git a/wildlifecompliance/components/sanction_outcome/pdf_remediation_notice.py b/wildlifecompliance/components/sanction_outcome/pdf_remediation_notice.py deleted file mode 100644 index 495f93d4d..000000000 --- a/wildlifecompliance/components/sanction_outcome/pdf_remediation_notice.py +++ /dev/null @@ -1,183 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -from decimal import Decimal as D -from io import BytesIO - -from django.core.files.storage import default_storage -from wildlifecompliance.settings import STATIC_ROOT -from oscar.templatetags.currency_filters import currency -from reportlab.lib import enums -from reportlab.lib.colors import Color -from reportlab.lib.enums import TA_RIGHT, TA_CENTER -from reportlab.lib.pagesizes import A4 -from reportlab.platypus import BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, \ - KeepTogether, PageBreak, Flowable, NextPageTemplate, FrameBreak, Image -from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle, StyleSheet1 -from reportlab.lib.utils import ImageReader -from reportlab.pdfgen.canvas import Canvas -from reportlab.pdfbase import pdfmetrics -from reportlab.lib.units import inch, mm -from reportlab.lib import colors - -from django.core.files import File -from django.conf import settings - -from ledger.accounts.models import Document -from ledger.checkout.utils import calculate_excl_gst - -from wildlifecompliance.components.main.pdf_utils import gap, SolidLine, get_font_str - -PAGE_WIDTH, PAGE_HEIGHT = A4 -DEFAULT_FONTNAME = 'Helvetica' -BOLD_FONTNAME = 'Helvetica-Bold' - -DPAW_HEADER_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo.jpg') -DPAW_HEADER_LOGO_SM = os.path.join(STATIC_ROOT, 'payments', 'img','dbca_logo_small.png') -BPAY_LOGO = os.path.join(STATIC_ROOT, 'payments', 'img', 'BPAY_2012_PORT_BLUE.png') -HEADER_MARGIN = 10 -HEADER_SMALL_BUFFER = 3 -PAGE_TOP_MARGIN = 200 -VERY_LARGE_FONTSIZE = 14 -LARGE_FONTSIZE = 12 -MEDIUM_FONTSIZE = 10 -SMALL_FONTSIZE = 8 -PARAGRAPH_BOTTOM_MARGIN = 5 -SECTION_BUFFER_HEIGHT = 10 -DATE_FORMAT = '%d/%m/%Y' - - -def _create_pdf(invoice_buffer, sanction_outcome): - PAGE_MARGIN = 5 * mm - page_frame_1 = Frame(PAGE_MARGIN, PAGE_MARGIN, PAGE_WIDTH - 2 * PAGE_MARGIN, PAGE_HEIGHT - 2 * PAGE_MARGIN, id='PagesFrame1', ) #showBoundary=Color(0, 1, 0)) - page_template_1 = PageTemplate(id='Page1', frames=[page_frame_1, ], ) - doc = BaseDocTemplate(invoice_buffer, pageTemplates=[page_template_1, ], pagesize=A4,) # showBoundary=Color(1, 0, 0)) - - # Common - FONT_SIZE_L = 11 - FONT_SIZE_M = 10 - FONT_SIZE_S = 8 - - styles = StyleSheet1() - styles.add(ParagraphStyle(name='Normal', - fontName='Helvetica', - fontSize=FONT_SIZE_M, - spaceBefore=7, # space before paragraph - spaceAfter=7, # space after paragraph - leading=12)) # space between lines - styles.add(ParagraphStyle(name='BodyText', - parent=styles['Normal'], - spaceBefore=6)) - styles.add(ParagraphStyle(name='Italic', - parent=styles['BodyText'], - fontName='Helvetica-Italic')) - styles.add(ParagraphStyle(name='Bold', - parent=styles['BodyText'], - fontName='Helvetica-Bold', - alignment = TA_CENTER)) - styles.add(ParagraphStyle(name='Right', - parent=styles['BodyText'], - alignment=TA_RIGHT)) - styles.add(ParagraphStyle(name='Centre', - parent=styles['BodyText'], - alignment=TA_CENTER)) - - # Logo - dpaw_header_logo = ImageReader(DPAW_HEADER_LOGO) - dpaw_header_logo_size = dpaw_header_logo.getSize() - width = dpaw_header_logo_size[0]/2 - height = dpaw_header_logo_size[1]/2 - dbca_logo = Image(DPAW_HEADER_LOGO, width=width, height=height) - - # Title - title_remediation_notice = Paragraph('REMEDIATION NOTICE', styles['Centre']) - - # Table - invoice_table_style = TableStyle([ - ('VALIGN', (0, 0), (-1, -1), 'TOP'), - ('GRID', (0, 0), (-1, -1), 1, colors.black), - ('ALIGN', (0, 0), (-1, -1), 'LEFT'), - ]) - date_str = gap(10) + '/' + gap(10) + '/ 20' - col_width = [180*mm, ] - - offender = sanction_outcome.get_offender() - - data = [] - p_number = get_font_str(str(offender[0].phone_number) + '(p)') if offender[0].phone_number else '' - m_number = get_font_str(offender[0].mobile_number + '(m)') if offender[0].mobile_number else '' - phone_number = ' | '.join(list(filter(None, [p_number, m_number]))) - dob = offender[0].dob.strftime('%d/%m/%Y') if offender[0].dob else '' - data.append([[ - Paragraph('Pursuant to the Biodiversity Conservation Act 2016, the CEO considers that you are a person bound by a relevant instrument.
' - 'Contact details of person to whom this Notice is issued:', styles['Normal']), - Paragraph('Full name: ' + get_font_str(offender[0].get_full_name()) + gap(5) + 'Date of Birth: ' + get_font_str(dob), styles['Normal']), - Paragraph('Postal/Residential address: ' + get_font_str(offender[0].residential_address), styles['Normal']), - Paragraph('Telephone number: ' + phone_number, styles['Normal']), - Paragraph('Email address: ' + get_font_str(offender[0].email), styles['Normal']), - ], ]) - data.append([[ - Paragraph('The CEO is of the opinion that you have contravened the relevant instrument listed below:', styles['Normal']), - Table([ - [Paragraph('Relevant Instrument', styles['Normal']), Paragraph('Reference Number of Instrument/Notice', styles['Normal'])], - [Paragraph('Biodiversity Conservation Covenant', styles['Normal']), ''], - [Paragraph('Environment Pest Notice', styles['Normal']), ''], - [Paragraph('Habitat Conservation Notice', styles['Normal']), ''], - ], style=invoice_table_style, rowHeights=[6*mm, 12*mm, 12*mm, 12*mm]) - ], ]) - data.append([[ - Paragraph('You are required to undertake one or more of the following action(s) to comply with the named instrument:', styles['Normal']), - Paragraph('a) Stop anything that is being done in contravention of the instrument; and
' - 'b) Do anything required by the instrument to be done that has not been done; and
' - 'c) Carry out work that is necessary to remedy anything done in contravention of the instrument; and
' - 'd) Do anything incidental to action referred to in (a), (b), or (c) above', styles['Normal']) - ], ]) - - # Actions - actions = [] - actions.append(Paragraph('List the remedial actions necessary to afford compliance with the relevant instrument:', styles['Normal'])) - for ra in sanction_outcome.remediation_actions.all(): - actions.append(Paragraph(get_font_str(ra.action), styles['Normal'])) - data.append([actions,]) - data.append([[ - Paragraph('IMPORTANT: You must comply with the remediation action(s) listed above within the period specified in this notice.', styles['Normal']), - Paragraph('If you do not comply within the specified period, the CEO may take any necessary remedial action and may recover the reasonable costs incurred in taking remedial action from you, as a person(s) bound by the relevant instrument, in a court of competent jurisdiction as a debt to the State.', styles['Normal']), - Paragraph('Postal Address of the CEO:
Department of Biodiversity, Conservation and Attractions
Locked Bag 104
Bentley Delivery Centre WA 6983', styles['Normal']), - ], ]) - data.append([[ - Paragraph('Notes::', styles['Normal']), - Paragraph('A separate Remediation Notice must be issued to each person bound by the instrument.:', styles['Normal'], bulletText='-'), - Paragraph('For the purpose of taking remedial action a wildlife officer may enter on land with or without vehicles, plant or equipment and remain on that land for as long as is necessary to complete the remedial action.', styles['Normal'], bulletText='-'), - Paragraph('A wildlife officer must not exercise a power to enter unless they first obtain the consent of the owner or occupier of the land, or the owner/occupier has been given reasonable notice of the proposed entry and has not objected to the entry, or the entry is in accordance with an entry warrant. Section 202 of the Biodiversity Conservation Act 2016 applies. (Occupiers Rights)', styles['Normal'], bulletText='-',) - ], ]) - - t1 = Table(data, style=invoice_table_style, colWidths=col_width, rowHeights=[60*mm, 60*mm, 40*mm, 90*mm, 60*mm, 60*mm,]) - - # Append tables to the elements to build - gap_between_tables = 1.5*mm - elements = [] - elements.append(dbca_logo) - elements.append(title_remediation_notice) - elements.append(t1) - - doc.build(elements) - return invoice_buffer - - -def create_remediation_notice_pdf_bytes(filename, sanction_outcome): - with BytesIO() as invoice_buffer: - _create_pdf(invoice_buffer, sanction_outcome) - - # Get the value of the BytesIO buffer - value = invoice_buffer.getvalue() - - # START: Save the pdf file to the database - document = sanction_outcome.documents.create(name=filename) - path = default_storage.save('wildlifecompliance/{}/{}/documents/{}'.format(sanction_outcome._meta.model_name, sanction_outcome.id, filename), invoice_buffer) - document._file = path - document.save() - # END: Save - - invoice_buffer.close() - - return document diff --git a/wildlifecompliance/doctopdf.py b/wildlifecompliance/doctopdf.py index 01eacc052..7b5da65f8 100644 --- a/wildlifecompliance/doctopdf.py +++ b/wildlifecompliance/doctopdf.py @@ -9,74 +9,170 @@ logger = logging.getLogger(__name__) -def create_infringement_notice_pdf_contents(pdf_filename, sanction_outcome): - acos = AllegedCommittedOffence.objects.filter(sanction_outcome=sanction_outcome, included=True) - if acos.count() == 1: - alleged_offence = acos[0].alleged_offence - else: - # Should not reach here. For an infringement notice, there should be only one alleged offence. - str = 'Sanction Outcome: {} has {} alleged offences. There should be 1.'.format(sanction_outcome.lodgement_nubmer, acos.count()) - logger.error(str) - raise Exception(str) - - # Retrieve the latest template which matches the Act and SanctionOutcome type. +def convert_to_pdf(doc, pdf_filename): + temp_directory = settings.BASE_DIR + "/tmp/" try: - so_outcome_template = SanctionOutcomeWordTemplate.objects.filter(act=alleged_offence.act, sanction_outcome_type=sanction_outcome.type).order_by('-id').first() + os.stat(temp_directory) except: - raise Exception('Template not found for the Act: {} and Sanction Outcome type: {}'.format(alleged_offence.act, sanction_outcome.type)) - - path_to_template = so_outcome_template._file.path + os.mkdir(temp_directory) + f_name = temp_directory + pdf_filename + new_doc_file = f_name + '.docx' + new_pdf_file = f_name + '.pdf' + doc.save(new_doc_file) + os.system("libreoffice --headless --convert-to pdf " + new_doc_file + " --outdir " + temp_directory) + file_contents = None + with open(new_pdf_file, 'rb') as f: + file_contents = f.read() + os.remove(new_doc_file) + os.remove(new_pdf_file) + return file_contents - doc = DocxTemplate(path_to_template) +def retrieve_context(sanction_outcome): try: offender = sanction_outcome.get_offender()[0] except: raise Exception('No offender found for the Sanction Outcome: {}'.format(sanction_outcome.lodgement_number)) + + offender_family_name = offender.last_name if offender.last_name else '' + offender_given_name = offender.first_name if offender.first_name else '' offender_dob = offender.dob.strftime('%d/%m/%Y') if offender.dob else '' offender_postcode = offender.residential_address.postcode if offender.residential_address else '' + offender_residential_address = offender.residential_address if offender.residential_address else '' offender_email = offender.email if offender.email else '' rego = sanction_outcome.registration_number if sanction_outcome.registration_number else '' - offence_datetime = sanction_outcome.offence.offence_occurrence_datetime if sanction_outcome.offence.offence_occurrence_datetime else '' + offence_date = sanction_outcome.offence.offence_occurrence_datetime.strftime('%d/%m/%Y') if sanction_outcome.offence.offence_occurrence_datetime else '' + offence_time = sanction_outcome.offence.offence_occurrence_datetime.strftime('%I:%M %p') if sanction_outcome.offence.offence_occurrence_datetime else '' + offence_location = sanction_outcome.offence.location if sanction_outcome.offence.location else '' responsible_officer_name = sanction_outcome.responsible_officer.get_full_name() if sanction_outcome.responsible_officer else '' - issue_date = sanction_outcome.date_of_issue.strftime('%d/%m/%Y') - issue_time = sanction_outcome.time_of_issue.strftime('%I:%M %p') - + issue_date = sanction_outcome.date_of_issue.strftime('%d/%m/%Y') if sanction_outcome.date_of_issue else '' + issue_time = sanction_outcome.time_of_issue.strftime('%I:%M %p') if sanction_outcome.time_of_issue else '' context = { 'lodgement_number': sanction_outcome.lodgement_number, - 'offender_family_name': offender.last_name, - 'offender_given_name': offender.first_name, + 'offender_family_name': offender_family_name, + 'offender_given_name': offender_given_name, 'offender_date_of_birth': offender_dob, 'offender_postcode': offender_postcode, - 'offender_residential_address': offender.residential_address, + 'offender_residential_address': offender_residential_address, 'offender_email': offender_email, 'registration_number': rego, - 'offence_location': sanction_outcome.offence.location, - 'offence_date': offence_datetime.strftime('%d/%m/%Y'), - 'offence_time': offence_datetime.strftime('%I:%M %p'), - 'sanction_outcome_description': sanction_outcome.description, + 'offence_location': offence_location, + 'offence_date': offence_date, + 'offence_time': offence_time, + 'offence_details': sanction_outcome.offence.details, 'responsible_officer_name': responsible_officer_name, 'issue_date': issue_date, 'issue_time': issue_time, } + return context + + +def create_infringement_notice_pdf_contents(pdf_filename, sanction_outcome): + acos = AllegedCommittedOffence.objects.filter(sanction_outcome=sanction_outcome, included=True) + if acos.count() == 1: + alleged_offence = acos[0].alleged_offence + else: + # Should not reach here. For an infringement notice, there should be only one alleged offence. + str = 'Sanction Outcome: {} has {} alleged offences. There should be 1.'.format(sanction_outcome.lodgement_nubmer, acos.count()) + logger.error(str) + raise Exception(str) + # Retrieve the latest template which matches the Act and SanctionOutcome type. + so_outcome_template = SanctionOutcomeWordTemplate.objects.filter(act=alleged_offence.act, sanction_outcome_type=sanction_outcome.type).order_by('-id').first() + if not so_outcome_template: + raise Exception('Template not found for the Act: {} and Sanction Outcome type: {}'.format(alleged_offence.act, sanction_outcome.type)) + + path_to_template = so_outcome_template._file.path + doc = DocxTemplate(path_to_template) + context = retrieve_context(sanction_outcome) doc.render(context) - temp_directory = settings.BASE_DIR + "/tmp/" - try: - os.stat(temp_directory) - except: - os.mkdir(temp_directory) + file_contents = convert_to_pdf(doc, pdf_filename) + return file_contents - f_name = temp_directory + pdf_filename - new_doc_file = f_name + '.docx' - new_pdf_file = f_name + '.pdf' - doc.save(new_doc_file) - os.system("libreoffice --headless --convert-to pdf " + new_doc_file + " --outdir " + temp_directory) - file_contents = None - with open(new_pdf_file, 'rb') as f: - file_contents = f.read() - os.remove(new_doc_file) - os.remove(new_pdf_file) +def create_caution_notice_pdf_contents(pdf_filename, sanction_outcome): + acos = AllegedCommittedOffence.objects.filter(sanction_outcome=sanction_outcome, included=True) + if acos.count() > 0: + alleged_offences = [item.alleged_offence for item in acos.all()] + else: + # Should not reach here. For an infringement notice, there should be only one alleged offence. + str = 'Sanction Outcome: {} has no alleged offences. Caution notices cannot be created.'.format(sanction_outcome.lodgement_nubmer) + logger.error(str) + raise Exception(str) + + # Check the ACT type. Act types cannot be mixed in a Sanction Outcome + act = None + for ao in alleged_offences: + if not act: + act = ao.act + else: + if act != ao.act: + raise Exception('Different ACT types are present in the Sanction Outcome: {}'.format(sanction_outcome.lodgement_number)) + + so_outcome_template = SanctionOutcomeWordTemplate.objects.filter(act=act, sanction_outcome_type=sanction_outcome.type).order_by('-id').first() + if not so_outcome_template: + raise Exception('Template not found for the Act: {} and Sanction Outcome type: {}'.format(act, sanction_outcome.type)) + + path_to_template = so_outcome_template._file.path + doc = DocxTemplate(path_to_template) + context = retrieve_context(sanction_outcome) + doc.render(context) + + file_contents = convert_to_pdf(doc, pdf_filename) return file_contents + + +def create_letter_of_advice_pdf_contents(pdf_filename, sanction_outcome): + acos = AllegedCommittedOffence.objects.filter(sanction_outcome=sanction_outcome, included=True) + if acos.count() > 0: + alleged_offences = [item.alleged_offence for item in acos.all()] + else: + # Should not reach here. For an infringement notice, there should be only one alleged offence. + str = 'Sanction Outcome: {} has no alleged offences. Caution notices cannot be created.'.format(sanction_outcome.lodgement_nubmer) + logger.error(str) + raise Exception(str) + + # Check the ACT type. Act types cannot be mixed in a Sanction Outcome + act = None + for ao in alleged_offences: + if not act: + act = ao.act + else: + if act != ao.act: + raise Exception('Different ACT types are present in the Sanction Outcome: {}'.format(sanction_outcome.lodgement_number)) + + so_outcome_template = SanctionOutcomeWordTemplate.objects.filter(act=act, sanction_outcome_type=sanction_outcome.type).order_by('-id').first() + if not so_outcome_template: + raise Exception('Template not found for the Act: {} and Sanction Outcome type: {}'.format(act, sanction_outcome.type)) + + path_to_template = so_outcome_template._file.path + doc = DocxTemplate(path_to_template) + context = retrieve_context(sanction_outcome) + doc.render(context) + + file_contents = convert_to_pdf(doc, pdf_filename) + return file_contents + + +def create_remediation_notice_pdf_contents(pdf_filename, sanction_outcome): + acos = AllegedCommittedOffence.objects.filter(sanction_outcome=sanction_outcome, included=True) + if acos.count() > 0: + alleged_offences = [item.alleged_offence for item in acos.all()] + else: + # Should not reach here. For an infringement notice, there should be only one alleged offence. + str = 'Sanction Outcome: {} has no alleged offences. Caution notices cannot be created.'.format(sanction_outcome.lodgement_nubmer) + logger.error(str) + raise Exception(str) + + so_outcome_template = SanctionOutcomeWordTemplate.objects.filter(sanction_outcome_type=sanction_outcome.type).order_by('-id').first() + if not so_outcome_template: + raise Exception('Template not found for the Sanction Outcome type: {}'.format(sanction_outcome.type)) + + path_to_template = so_outcome_template._file.path + doc = DocxTemplate(path_to_template) + context = retrieve_context(sanction_outcome) + doc.render(context) + + file_contents = convert_to_pdf(doc, pdf_filename) + return file_contents \ No newline at end of file diff --git a/wildlifecompliance/frontend/wildlifecompliance/src/components/internal/sanction_outcome/sanction_outcome_modal.vue b/wildlifecompliance/frontend/wildlifecompliance/src/components/internal/sanction_outcome/sanction_outcome_modal.vue index 59a0b9676..9d9644d63 100644 --- a/wildlifecompliance/frontend/wildlifecompliance/src/components/internal/sanction_outcome/sanction_outcome_modal.vue +++ b/wildlifecompliance/frontend/wildlifecompliance/src/components/internal/sanction_outcome/sanction_outcome_modal.vue @@ -327,6 +327,9 @@ export default { action: "", due_date: null }, + + act_name_of_selected_ao: '', // when the user ticks the first one, only other alleged offences from the same Act will be available. + // No mixing of BCA and CALM // List for dropdown options_for_types: [], @@ -386,10 +389,20 @@ export default { // Chenck if this alleged offence has already a connection to the current offender selected let current_offender_has_connection = false; - if (full.connected || full.removed){ + // CALM and BCA should not be both included in a sanction outcome at the same time. + let unselectable = true + if (vm.sanction_outcome.type == 'infringement_notice'){ + unselectable = false + } else if (!vm.act_name_of_selected_ao || vm.act_name_of_selected_ao == full.section_regulation.act){ + unselectable = false + } + let selected_str = '' + if (vm.aco_ids_included.includes(full.id.toString())){ + selected_str = 'checked="checked"' + } + if (full.connected || full.removed || unselectable){ // Disabled - if (vm.sanction_outcome.type == 'infringement_notice'){ ret_line += ''; } else if (vm.sanction_outcome.type == '' || vm.sanction_outcome.type == null) { @@ -399,16 +412,15 @@ export default { ret_line += ''; } } else { - // Enabled - if (vm.sanction_outcome.type == 'infringement_notice'){ - ret_line += ''; + ret_line += ''; } else if (vm.sanction_outcome.type == '' || vm.sanction_outcome.type == null) { // Should not reach here ret_line += ''; } else { - ret_line += ''; + //ret_line += ''; + ret_line += ''; } } @@ -565,18 +577,33 @@ export default { loadOffence: 'loadOffence', }), aco_include_clicked: function() { + console.log('in aco_include_clicked') let vm = this; vm.aco_ids_included = []; vm.aco_ids_excluded = []; let checkboxes = $(".alleged_offence_include"); + console.log(checkboxes) for (let i = 0; i < checkboxes.length; i++) { if (checkboxes[i].checked) { vm.aco_ids_included.push(checkboxes[i].value); + } else { vm.aco_ids_excluded.push(checkboxes[i].value); } } + if (vm.aco_ids_included.length){ + for (let alleged_offence of vm.sanction_outcome.current_offence.alleged_offences){ + if (alleged_offence.id == vm.aco_ids_included[0]){ + vm.act_name_of_selected_ao = alleged_offence.section_regulation.act + } + } + } else { + // No alleged offences selected + vm.act_name_of_selected_ao = '' + } + console.log('act_name_of_selected_ao: ' + vm.act_name_of_selected_ao) vm.check_if_is_parking_offence(); + vm.constructAllegedOffencesTable(); }, check_if_is_parking_offence: function() { this.is_parking_offence = false; diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-BC-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-BC-Act.docx new file mode 100644 index 000000000..8f0d5774f Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-BC-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-CALM-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-CALM-Act.docx new file mode 100644 index 000000000..9f2b28452 Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Caution-Notice-CALM-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-BC-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-BC-Act.docx new file mode 100644 index 000000000..5dab57f08 Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-BC-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-CALM-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-CALM-Act.docx new file mode 100644 index 000000000..52170fc84 Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Infringement-Notice-CALM-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-BC-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-BC-Act.docx new file mode 100644 index 000000000..3d6c3b20f Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-BC-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-CALM-Act.docx b/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-CALM-Act.docx new file mode 100644 index 000000000..7b758f889 Binary files /dev/null and b/wildlifecompliance/static/wildlifecompliance/templates/Letter-Of-Advice-CALM-Act.docx differ diff --git a/wildlifecompliance/static/wildlifecompliance/templates/Remediation Notice.docx b/wildlifecompliance/static/wildlifecompliance/templates/Remediation-Notice.docx similarity index 100% rename from wildlifecompliance/static/wildlifecompliance/templates/Remediation Notice.docx rename to wildlifecompliance/static/wildlifecompliance/templates/Remediation-Notice.docx