diff --git a/alcs-frontend/src/app/features/application/applicant-info/application-details/cove-details/cove-details.component.html b/alcs-frontend/src/app/features/application/applicant-info/application-details/cove-details/cove-details.component.html
index 343a3368b3..9baa11c58f 100644
--- a/alcs-frontend/src/app/features/application/applicant-info/application-details/cove-details/cove-details.component.html
+++ b/alcs-frontend/src/app/features/application/applicant-info/application-details/cove-details/cove-details.component.html
@@ -11,7 +11,7 @@
{{ transferee.displayName }}
{{ transferee.organizationName }}
-
+
{{ transferee.phoneNumber | mask : '(000) 000-0000' }}
@@ -19,18 +19,18 @@
{{ transferee.email }}
-
+
How many hectares will the covenant impact?
{{ _applicationSubmission.coveAreaImpacted }}
ha
-
+
What is the purpose of this covenant?
{{ _applicationSubmission.purpose }}
-
+
Explain how the covenant impacts the use of agricultural land for farm purposes.
@@ -38,7 +38,7 @@
{{ _applicationSubmission.coveFarmImpact }}
-
+
Proposal Map / Site Plan
@@ -46,7 +46,7 @@
{{ map.fileName }}
-
+
Do you have a draft copy of the covenant?
@@ -54,7 +54,7 @@
{{ _applicationSubmission.coveHasDraft ? 'Yes' : 'No' }}
-
+
Draft Covenant
@@ -65,7 +65,6 @@
diff --git a/bin/migrate-oats-data/applications/__init__.py b/bin/migrate-oats-data/applications/__init__.py
index c4deade59c..f0ad56389a 100644
--- a/bin/migrate-oats-data/applications/__init__.py
+++ b/bin/migrate-oats-data/applications/__init__.py
@@ -9,6 +9,6 @@
init_applications,
)
-from .set_hide_from_portal_on_application import (
- set_hide_from_portal_on_application,
-)
+from .set_application_visibility import set_application_visibility
+
+from .application_decision_date import process_alcs_application_decision_date
diff --git a/bin/migrate-oats-data/applications/application_decision_date.py b/bin/migrate-oats-data/applications/application_decision_date.py
new file mode 100644
index 0000000000..e55b8f45be
--- /dev/null
+++ b/bin/migrate-oats-data/applications/application_decision_date.py
@@ -0,0 +1,105 @@
+from common import (
+ BATCH_UPLOAD_SIZE,
+ setup_and_get_logger,
+ add_timezone_and_keep_date_part,
+)
+from db import inject_conn_pool
+from psycopg2.extras import RealDictCursor, execute_batch
+
+etl_name = "process_alcs_application_decision_date"
+logger = setup_and_get_logger(etl_name)
+
+
+@inject_conn_pool
+def process_alcs_application_decision_date(conn=None, batch_size=BATCH_UPLOAD_SIZE):
+ """
+ Imports decision_date from oats to alcs.application.decision_date. alcs.application.decision_date is the date of the first decision
+ In OATS the first decision date is stored in oats.oats_alr_appl_decisions. All subsequent decisions in OATS are the linked to reconsiderations and not application directly.
+ """
+
+ logger.info(f"Start {etl_name}")
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
+ with open(
+ "applications/sql/application_decision_date/application_decision_date_count.sql",
+ "r",
+ encoding="utf-8",
+ ) as sql_file:
+ count_query = sql_file.read()
+ cursor.execute(count_query)
+ count_total = dict(cursor.fetchone())["count"]
+
+ logger.info(f" Total Application data to update: {count_total}")
+
+ failed_inserts = 0
+ successful_updates_count = 0
+ last_application_id = 0
+
+ with open(
+ "applications/sql/application_decision_date/application_decision_date.sql",
+ "r",
+ encoding="utf-8",
+ ) as sql_file:
+ application_sql = sql_file.read()
+ while True:
+ cursor.execute(
+ f"{application_sql} WHERE decision_date_for_applications.alr_application_id > {last_application_id} ORDER BY decision_date_for_applications.alr_application_id;"
+ )
+
+ rows = cursor.fetchmany(batch_size)
+
+ if not rows:
+ break
+ try:
+ records_to_be_updated_count = len(rows)
+
+ _update_fee_fields_records(conn, batch_size, cursor, rows)
+
+ successful_updates_count = (
+ successful_updates_count + records_to_be_updated_count
+ )
+ last_application_id = dict(rows[-1])["alr_application_id"]
+
+ logger.debug(
+ f"retrieved/updated items count: {records_to_be_updated_count}; total successfully updated applications so far {successful_updates_count}; last updated application_id: {last_application_id}"
+ )
+ except Exception as err:
+ # this is NOT going to be caused by actual data update failure. This code is only executed when the code error appears or connection to DB is lost
+ logger.exception(err)
+ conn.rollback()
+ failed_inserts = count_total - successful_updates_count
+ last_application_id = last_application_id + 1
+
+ logger.info(
+ f"Finished {etl_name}: total amount of successful updates {successful_updates_count}, total failed updates {failed_inserts}"
+ )
+
+
+def _update_fee_fields_records(conn, batch_size, cursor, rows):
+ query = _get_update_query_from_oats_alr_applications_fields()
+ parsed_fee_data_list = _prepare_oats_alr_applications_data(rows)
+
+ if len(parsed_fee_data_list) > 0:
+ execute_batch(cursor, query, parsed_fee_data_list, page_size=batch_size)
+
+ conn.commit()
+
+
+def _get_update_query_from_oats_alr_applications_fields():
+ query = """
+ UPDATE alcs.application
+ SET decision_date = COALESCE(%(decision_date)s, decision_date)
+ WHERE
+ file_number = %(alr_application_id)s::TEXT;
+ """
+ return query
+
+
+def _prepare_oats_alr_applications_data(row_data_list):
+ data_list = []
+ for row in row_data_list:
+ data = dict(row)
+ data["decision_date"] = add_timezone_and_keep_date_part(
+ row.get("decision_date")
+ )
+ data_list.append(data)
+ return data_list
diff --git a/bin/migrate-oats-data/applications/migrate_application.py b/bin/migrate-oats-data/applications/migrate_application.py
index 52fbc7d2a6..4e2422bfae 100644
--- a/bin/migrate-oats-data/applications/migrate_application.py
+++ b/bin/migrate-oats-data/applications/migrate_application.py
@@ -10,7 +10,7 @@
clean_primary_contacts,
insert_application_submission_review,
clean_reviews,
- update_application_submissions
+ update_application_submissions,
)
from .base_applications import process_applications, clean_applications
from .app_prep import process_alcs_application_prep_fields
@@ -49,7 +49,8 @@
update_application_reconsiderations,
)
-from .set_hide_from_portal_on_application import set_hide_from_portal_on_application
+from .set_application_visibility import set_application_visibility
+from .application_decision_date import process_alcs_application_decision_date
def process_application_etl(batch_size):
@@ -58,12 +59,13 @@ def process_application_etl(batch_size):
process_alcs_app_submissions(batch_size)
update_application_submissions()
insert_application_submission_review(batch_size)
+ process_alcs_application_decision_date(batch_size)
process_application_statuses(batch_size)
process_application_parcels(batch_size)
process_application_owners(batch_size)
process_app_staff_journal(batch_size)
process_application_decisions(batch_size)
- set_hide_from_portal_on_application()
+ set_application_visibility()
process_application_submission_status_emails()
@@ -96,7 +98,6 @@ def process_application_decisions(batch_size):
link_application_conditions(batch_size)
-
def clean_application_decisions_etl():
# modifications do not have clean since all of them were created in ALCS and ETL is not introducing new records.
clean_application_conditions_to_components()
diff --git a/bin/migrate-oats-data/applications/set_application_visibility.py b/bin/migrate-oats-data/applications/set_application_visibility.py
new file mode 100644
index 0000000000..663942ccd1
--- /dev/null
+++ b/bin/migrate-oats-data/applications/set_application_visibility.py
@@ -0,0 +1,158 @@
+from common import setup_and_get_logger
+from db import inject_conn_pool
+from psycopg2.extras import RealDictCursor
+
+
+etl_name = "set_application_visibility"
+logger = setup_and_get_logger(etl_name)
+
+
+@inject_conn_pool
+def set_application_visibility(conn=None):
+ """This function is responsible for setting source column on application and mapping it to public portal visibility visibility."""
+ _set_source_on_applications(conn)
+ _set_hide_from_portal_on_application(conn)
+
+
+def _set_hide_from_portal_on_application(conn):
+ """
+ This function is responsible for setting hide_from_portal colum on application table based on the source.
+
+ Args:
+ conn (psycopg2.extensions.connection): PostgreSQL database connection. Provided by the decorator.
+ """
+ logger.info(f"Start {etl_name}")
+
+ try:
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
+ count_visible_to_portal_query = f"""
+ SELECT count(*) FROM alcs.application a
+ WHERE a."source" = 'APPLICANT';
+ """
+ cursor.execute(count_visible_to_portal_query)
+ visible_to_portal_count = dict(cursor.fetchone())["count"]
+
+ count_hidden_from_portal_query = f"""
+ SELECT count(*) FROM alcs.application a
+ WHERE a."source" <> 'APPLICANT';
+ """
+ cursor.execute(count_hidden_from_portal_query)
+ hidden_from_portal_count = dict(cursor.fetchone())["count"]
+
+ count_total = visible_to_portal_count + hidden_from_portal_count
+
+ logger.info(f"Total application to set hide_from_portal: {count_total}")
+
+ set_hide_from_portal_false_query = f"""
+ UPDATE alcs.application
+ SET hide_from_portal = FALSE
+ WHERE alcs.application."source" = 'APPLICANT';
+ """
+ cursor.execute(set_hide_from_portal_false_query)
+ updated_visible_to_portal_count = cursor.rowcount
+
+ set_hide_from_portal_true_query = f"""
+ UPDATE alcs.application
+ SET hide_from_portal = TRUE
+ WHERE alcs.application."source" <> 'APPLICANT';
+ """
+ cursor.execute(set_hide_from_portal_true_query)
+ updated_hidden_from_portal_count = cursor.rowcount
+
+ updated_total_count = (
+ updated_visible_to_portal_count + updated_hidden_from_portal_count
+ )
+
+ logger.info(
+ f"Total hide_from_portal flag on applications updated: {updated_total_count}"
+ )
+ conn.commit()
+ except Exception as err:
+ logger.exception(err)
+ cursor.close()
+ conn.close()
+
+ logger.info(f"Finished {etl_name}")
+
+
+def _set_source_on_applications(conn):
+ """"""
+ logger.info(f"Start {etl_name} => set_source_on_applications")
+
+ try:
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
+ count_created_by_applicant_query = f"""
+ SELECT count(*) FROM oats.oats_alr_applications oaa
+ JOIN alcs.application app ON app.file_number = oaa.alr_application_id::TEXT
+ WHERE oaa.who_created = 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(count_created_by_applicant_query)
+ created_by_applicant_count = dict(cursor.fetchone())["count"]
+
+ count_created_by_oats_query = f"""
+ SELECT count(*) FROM oats.oats_alr_applications oaa
+ JOIN alcs.application app ON app.file_number = oaa.alr_application_id::TEXT
+ WHERE oaa.who_created <> 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(count_created_by_oats_query)
+ created_by_oats_count = dict(cursor.fetchone())["count"]
+
+ count_created_in_alcs_query = f"""
+ SELECT count(*) FROM alcs.application a
+ WHERE a."source" <> 'APPLICANT'
+ AND a.audit_created_by <> 'oats_etl';
+ """
+ cursor.execute(count_created_in_alcs_query)
+ created_by_alcs_count = dict(cursor.fetchone())["count"]
+
+ count_total = (
+ created_by_applicant_count
+ + created_by_oats_count
+ + created_by_alcs_count
+ )
+
+ logger.info(f"Total application to set hide_from_portal: {count_total}")
+
+ update_created_by_applicant_query = f"""
+ UPDATE alcs.application
+ SET "source" = 'APPLICANT'
+ FROM oats.oats_alr_applications oaa
+ WHERE alcs.application.file_number = oaa.alr_application_id::TEXT
+ AND oaa.who_created = 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(update_created_by_applicant_query)
+ updated_created_by_applicant_count = cursor.rowcount
+
+ update_created_by_oats_query = f"""
+ UPDATE alcs.application
+ SET "source" = 'OATS'
+ FROM oats.oats_alr_applications oaa
+ WHERE alcs.application.file_number = oaa.alr_application_id::TEXT
+ AND oaa.who_created <> 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(update_created_by_oats_query)
+ updated_created_by_oats_count = cursor.rowcount
+
+ update_created_by_alcs_query = f"""
+ UPDATE alcs.application
+ SET "source" = 'APPLICANT'
+ WHERE alcs.application."source" <> 'APPLICANT'
+ AND alcs.application.audit_created_by <> 'oats_etl';
+ """
+ cursor.execute(update_created_by_alcs_query)
+ updated_created_by_alcs_count = cursor.rowcount
+
+ total_processed = (
+ updated_created_by_applicant_count
+ + updated_created_by_oats_count
+ + updated_created_by_alcs_count
+ )
+
+ logger.info(f"Total source on 'application' updated: {total_processed}")
+ conn.commit()
+ except Exception as err:
+ logger.exception(err)
+ cursor.close()
+ conn.close()
+
+ logger.info(f"Finished {etl_name}")
diff --git a/bin/migrate-oats-data/applications/set_hide_from_portal_on_application.py b/bin/migrate-oats-data/applications/set_hide_from_portal_on_application.py
deleted file mode 100644
index 60fdfc6a31..0000000000
--- a/bin/migrate-oats-data/applications/set_hide_from_portal_on_application.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from common import setup_and_get_logger
-from db import inject_conn_pool
-from psycopg2.extras import RealDictCursor
-
-
-etl_name = "set_hide_from_portal_on_application"
-logger = setup_and_get_logger(etl_name)
-
-
-@inject_conn_pool
-def set_hide_from_portal_on_application(conn=None):
- """
- This function is responsible for setting hide_from_portal colum on application table where oats.who_create is not PROXY user
-
- Args:
- conn (psycopg2.extensions.connection): PostgreSQL database connection. Provided by the decorator.
- """
- logger.info(f"Start {etl_name}")
-
- try:
- with conn.cursor(cursor_factory=RealDictCursor) as cursor:
- count_query = f"""
- SELECT count(*) FROM oats.oats_alr_applications oaa
- JOIN alcs.application a ON a.file_number = oaa.alr_application_id::TEXT
- WHERE oaa.who_created <> 'PROXY_OATS_APPLICANT';
- """
- cursor.execute(count_query)
- count_total = dict(cursor.fetchone())["count"]
- logger.info(f"Total application to set hide_from_portal: {count_total}")
-
- update_query = f"""
- UPDATE alcs.application AS app
- SET hide_from_portal = TRUE
- FROM oats.oats_alr_applications AS oaa
- WHERE app.file_number = oaa.alr_application_id::TEXT
- AND oaa.who_created <> 'PROXY_OATS_APPLICANT';
- """
- cursor.execute(update_query)
- logger.info(
- f"Total hide_from_portal flag on applications updated: {cursor.rowcount}"
- )
- conn.commit()
- except Exception as err:
- logger.exception(err)
- cursor.close()
- conn.close()
-
- logger.info(f"Finished {etl_name}")
diff --git a/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date.sql b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date.sql
new file mode 100644
index 0000000000..a75eb51f30
--- /dev/null
+++ b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date.sql
@@ -0,0 +1,12 @@
+WITH decision_date_for_applications AS (
+ SELECT oaad.alr_appl_decision_id,
+ oaad.alr_application_id,
+ oaad.decision_date
+ FROM oats.oats_alr_appl_decisions oaad
+ JOIN alcs.application oaa ON oaad.alr_application_id::TEXT = oaa.file_number
+ WHERE oaad.decision_date IS NOT NULL
+)
+SELECT alr_appl_decision_id,
+ alr_application_id,
+ decision_date
+FROM decision_date_for_applications
\ No newline at end of file
diff --git a/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_count.sql b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_count.sql
new file mode 100644
index 0000000000..dd248c8c8b
--- /dev/null
+++ b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_count.sql
@@ -0,0 +1,10 @@
+WITH decision_date_for_applications AS (
+ SELECT oaad.alr_appl_decision_id,
+ oaad.alr_application_id,
+ oaad.decision_date
+ FROM oats.oats_alr_appl_decisions oaad
+ JOIN alcs.application oaa ON oaad.alr_application_id::TEXT = oaa.file_number
+ WHERE oaad.decision_date IS NOT NULL
+)
+SELECT count(*)
+FROM decision_date_for_applications
\ No newline at end of file
diff --git a/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_validation.sql b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_validation.sql
new file mode 100644
index 0000000000..a95198fb8f
--- /dev/null
+++ b/bin/migrate-oats-data/applications/sql/application_decision_date/application_decision_date_validation.sql
@@ -0,0 +1,21 @@
+WITH nois_with_one_or_zero_component_only AS (
+ SELECT oaa.alr_application_id
+ FROM oats.alcs_etl_applications_nois oaa
+ WHERE oaa.application_class_code IN ('LOA', 'BLK', 'SCH', 'NAN')
+ and oaa.alr_change_code <> 'SRW'
+),
+oats_decision_date_for_applications AS (
+ SELECT oaad.alr_appl_decision_id,
+ oaa.alr_application_id,
+ oaad.decision_date
+ FROM oats.oats_alr_appl_decisions oaad
+ JOIN applications_with_one_or_zero_component_only oaa ON oaad.alr_application_id = oaa.alr_application_id
+ WHERE oaad.decision_date IS NOT NULL
+)
+SELECT oats_decision_date.alr_appl_decision_id,
+ oats_decision_date.alr_application_id,
+ oats_decision_date.decision_date,
+ appl.decision_date
+FROM oats_decision_date_for_applications AS oats_decision_date
+ JOIN alcs.application appl ON oats_decision_date.alr_application_id::text = appl.file_number
+WHERE oats_decision_date.decision_date != appl.decision_date AT TIME ZONE 'UTC'
\ No newline at end of file
diff --git a/bin/migrate-oats-data/applications/sql/insert-batch-application.sql b/bin/migrate-oats-data/applications/sql/insert-batch-application.sql
index 2af920d2ee..dfb4802884 100644
--- a/bin/migrate-oats-data/applications/sql/insert-batch-application.sql
+++ b/bin/migrate-oats-data/applications/sql/insert-batch-application.sql
@@ -57,6 +57,7 @@ WITH
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust Saturna Island' THEN 'Islands Trust Saturna Island (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust Sidney Island' THEN 'Islands Trust Sidney Island (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust- Comox Strathcona' THEN 'Islands Trust Comox Strathcona (Historical)'
+ WHEN oats_gov.oats_gov_name LIKE 'Comox-Strathcona (Historical)' THEN 'Comox-Strathcona Regional District (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust- Nanaimo' THEN 'Islands Trust Nanaimo (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust-Capital' THEN 'Islands Trust Capital (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust-Powell River' THEN 'Islands Trust Powell River (Historical)'
diff --git a/bin/migrate-oats-data/applications/submissions/review/lg_submission_review.py b/bin/migrate-oats-data/applications/submissions/review/lg_submission_review.py
index 66d649fa8b..195c78c8a4 100644
--- a/bin/migrate-oats-data/applications/submissions/review/lg_submission_review.py
+++ b/bin/migrate-oats-data/applications/submissions/review/lg_submission_review.py
@@ -143,9 +143,10 @@ def _map_data(row):
def _map_ocp_designation(row):
- if row["community_pln_compliance_ind"] == "X":
+ compliance_ind = row.get("community_pln_compliance_ind")
+ if compliance_ind == "X":
return False
- elif row["community_pln_compliance_ind"] == "N":
+ elif compliance_ind in ["N", "Y"]:
return True
else:
return None
@@ -161,9 +162,10 @@ def _map_ocp_consistency(row):
def _map_zoning(row):
- if row["zoning_compliance_ind"] == "X":
+ compliance_ind = row.get("zoning_compliance_ind")
+ if compliance_ind == "X":
return False
- elif row["zoning_compliance_ind"] == "N":
+ elif compliance_ind in ["N", "Y"]:
return True
else:
return None
diff --git a/bin/migrate-oats-data/noi/__init__.py b/bin/migrate-oats-data/noi/__init__.py
index 17d9fc895c..e60755682e 100644
--- a/bin/migrate-oats-data/noi/__init__.py
+++ b/bin/migrate-oats-data/noi/__init__.py
@@ -1,3 +1,3 @@
from .notice_of_intent_init import init_notice_of_intents, clean_notice_of_intents
from .notice_of_intent_migration import process_notice_of_intent
-from .set_hide_from_portal_on_noi import set_hide_from_portal_on_notice_of_intent
+from .set_notice_of_intent_visibility import set_notice_of_intent_visibility
diff --git a/bin/migrate-oats-data/noi/notice_of_intent_migration.py b/bin/migrate-oats-data/noi/notice_of_intent_migration.py
index 93cf8f3fe2..776e074801 100644
--- a/bin/migrate-oats-data/noi/notice_of_intent_migration.py
+++ b/bin/migrate-oats-data/noi/notice_of_intent_migration.py
@@ -80,7 +80,7 @@
clean_notice_of_intent_conditions_to_components,
)
-from .set_hide_from_portal_on_noi import set_hide_from_portal_on_notice_of_intent
+from .set_notice_of_intent_visibility import set_notice_of_intent_visibility
def init_notice_of_intent(batch_size):
@@ -173,7 +173,7 @@ def process_notice_of_intent(batch_size):
process_notice_of_intent_decisions(batch_size)
- set_hide_from_portal_on_notice_of_intent()
+ set_notice_of_intent_visibility()
# this script must be the last one
process_notice_of_intent_submission_status_emails()
diff --git a/bin/migrate-oats-data/noi/oats_to_alcs_notice_of_intent_table_etl/notice_of_intent_decision_date.py b/bin/migrate-oats-data/noi/oats_to_alcs_notice_of_intent_table_etl/notice_of_intent_decision_date.py
index 97b4152883..1e5a787dde 100644
--- a/bin/migrate-oats-data/noi/oats_to_alcs_notice_of_intent_table_etl/notice_of_intent_decision_date.py
+++ b/bin/migrate-oats-data/noi/oats_to_alcs_notice_of_intent_table_etl/notice_of_intent_decision_date.py
@@ -22,7 +22,7 @@ def process_alcs_notice_of_intent_decision_date(
logger.info(f"Start {etl_name}")
with conn.cursor(cursor_factory=RealDictCursor) as cursor:
with open(
- "noi/sql/notice_of_intent_base/notice_of_intent_base.count.sql",
+ "noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date_count.sql",
"r",
encoding="utf-8",
) as sql_file:
diff --git a/bin/migrate-oats-data/noi/set_hide_from_portal_on_noi.py b/bin/migrate-oats-data/noi/set_hide_from_portal_on_noi.py
deleted file mode 100644
index 2e45514cc7..0000000000
--- a/bin/migrate-oats-data/noi/set_hide_from_portal_on_noi.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from common import setup_and_get_logger
-from db import inject_conn_pool
-from psycopg2.extras import RealDictCursor
-
-
-etl_name = "set_hide_from_portal_on_notice_of_intent"
-logger = setup_and_get_logger(etl_name)
-
-
-@inject_conn_pool
-def set_hide_from_portal_on_notice_of_intent(conn=None):
- """
- This function is responsible for setting hide_from_portal colum on notice_of_intent table where oats.who_create is not PROXY user
-
- Args:
- conn (psycopg2.extensions.connection): PostgreSQL database connection. Provided by the decorator.
- """
- logger.info(f"Start {etl_name}")
-
- try:
- with conn.cursor(cursor_factory=RealDictCursor) as cursor:
- count_query = f"""
- SELECT count(*) FROM oats.oats_alr_applications oaa
- JOIN alcs.notice_of_intent a ON a.file_number = oaa.alr_application_id::TEXT
- WHERE oaa.who_created <> 'PROXY_OATS_APPLICANT';
- """
- cursor.execute(count_query)
- count_total = dict(cursor.fetchone())["count"]
- logger.info(f"Total application to set hide_from_portal: {count_total}")
-
- update_query = f"""
- UPDATE alcs.notice_of_intent AS noi
- SET hide_from_portal = TRUE
- FROM oats.oats_alr_applications AS oaa
- WHERE noi.file_number = oaa.alr_application_id::TEXT
- AND oaa.who_created <> 'PROXY_OATS_APPLICANT';
- """
- cursor.execute(update_query)
- logger.info(
- f"Total hide_from_portal flag on notifications updated: {cursor.rowcount}"
- )
- conn.commit()
- except Exception as err:
- logger.exception(err)
- cursor.close()
- conn.close()
-
- logger.info(f"Finished {etl_name}")
diff --git a/bin/migrate-oats-data/noi/set_notice_of_intent_visibility.py b/bin/migrate-oats-data/noi/set_notice_of_intent_visibility.py
new file mode 100644
index 0000000000..7b3c2ce334
--- /dev/null
+++ b/bin/migrate-oats-data/noi/set_notice_of_intent_visibility.py
@@ -0,0 +1,160 @@
+from common import setup_and_get_logger
+from db import inject_conn_pool
+from psycopg2.extras import RealDictCursor
+
+
+etl_name = "set_notice_of_intent_visibility"
+logger = setup_and_get_logger(etl_name)
+
+
+@inject_conn_pool
+def set_notice_of_intent_visibility(conn=None):
+ """This function is responsible for setting source column on application and mapping it to public portal visibility visibility."""
+ _set_source_on_notice_of_intent(conn)
+ _set_hide_from_portal_on_notice_of_intent(conn)
+
+
+def _set_hide_from_portal_on_notice_of_intent(conn):
+ """
+ This function is responsible for setting hide_from_portal colum on notice_of_intent table based on the source.
+
+ Args:
+ conn (psycopg2.extensions.connection): PostgreSQL database connection. Provided by the decorator.
+ """
+ logger.info(f"Start {etl_name}")
+
+ try:
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
+ count_visible_to_portal_query = f"""
+ SELECT count(*) FROM alcs.notice_of_intent a
+ WHERE a."source" = 'APPLICANT';
+ """
+ cursor.execute(count_visible_to_portal_query)
+ visible_to_portal_count = dict(cursor.fetchone())["count"]
+
+ count_hidden_from_portal_query = f"""
+ SELECT count(*) FROM alcs.notice_of_intent a
+ WHERE a."source" <> 'APPLICANT';
+ """
+ cursor.execute(count_hidden_from_portal_query)
+ hidden_from_portal_count = dict(cursor.fetchone())["count"]
+
+ count_total = visible_to_portal_count + hidden_from_portal_count
+
+ logger.info(f"Total application to set hide_from_portal: {count_total}")
+
+ set_hide_from_portal_false_query = f"""
+ UPDATE alcs.notice_of_intent
+ SET hide_from_portal = FALSE
+ WHERE "source" = 'APPLICANT';
+ """
+ cursor.execute(set_hide_from_portal_false_query)
+ updated_visible_to_portal_count = cursor.rowcount
+
+ set_hide_from_portal_true_query = f"""
+ UPDATE alcs.notice_of_intent
+ SET hide_from_portal = TRUE
+ WHERE "source" <> 'APPLICANT';
+ """
+ cursor.execute(set_hide_from_portal_true_query)
+ updated_hidden_from_portal_count = cursor.rowcount
+
+ updated_total_count = (
+ updated_visible_to_portal_count + updated_hidden_from_portal_count
+ )
+
+ logger.info(
+ f"Total hide_from_portal flag on notice_of_intent updated: {updated_total_count}"
+ )
+ conn.commit()
+ except Exception as err:
+ logger.exception(err)
+ cursor.close()
+ conn.close()
+
+ logger.info(f"Finished {etl_name}")
+
+
+def _set_source_on_notice_of_intent(conn):
+ """"""
+ logger.info(f"Start {etl_name} => _set_source_on_notice_of_intent")
+
+ try:
+ with conn.cursor(cursor_factory=RealDictCursor) as cursor:
+ count_created_by_applicant_query = f"""
+ SELECT count(*) FROM oats.oats_alr_applications oaa
+ JOIN alcs.notice_of_intent noi ON noi.file_number = oaa.alr_application_id::TEXT
+ WHERE oaa.who_created = 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(count_created_by_applicant_query)
+ created_by_applicant_count = dict(cursor.fetchone())["count"]
+
+ count_created_by_oats_query = f"""
+ SELECT count(*) FROM oats.oats_alr_applications oaa
+ JOIN alcs.notice_of_intent noi ON noi.file_number = oaa.alr_application_id::TEXT
+ WHERE oaa.who_created <> 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(count_created_by_oats_query)
+ created_by_oats_count = dict(cursor.fetchone())["count"]
+
+ count_created_in_alcs_query = f"""
+ SELECT count(*) FROM alcs.notice_of_intent a
+ WHERE a."source" <> 'APPLICANT'
+ AND a.audit_created_by <> 'oats_etl';
+ """
+ cursor.execute(count_created_in_alcs_query)
+ created_by_alcs_count = dict(cursor.fetchone())["count"]
+
+ count_total = (
+ created_by_applicant_count
+ + created_by_oats_count
+ + created_by_alcs_count
+ )
+
+ logger.info(f"Total application to set hide_from_portal: {count_total}")
+
+ update_created_by_applicant_query = f"""
+ UPDATE alcs.notice_of_intent
+ SET "source" = 'APPLICANT'
+ FROM oats.oats_alr_applications oaa
+ WHERE alcs.notice_of_intent.file_number = oaa.alr_application_id::TEXT
+ AND oaa.who_created = 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(update_created_by_applicant_query)
+ updated_created_by_applicant_count = cursor.rowcount
+
+ update_created_by_oats_query = f"""
+ UPDATE alcs.notice_of_intent
+ SET "source" = 'OATS'
+ FROM oats.oats_alr_applications oaa
+ WHERE alcs.notice_of_intent.file_number = oaa.alr_application_id::TEXT
+ AND oaa.who_created <> 'PROXY_OATS_APPLICANT';
+ """
+ cursor.execute(update_created_by_oats_query)
+ updated_created_by_oats_count = cursor.rowcount
+
+ update_created_by_alcs_query = f"""
+ UPDATE alcs.notice_of_intent
+ SET "source" = 'APPLICANT'
+ WHERE alcs.notice_of_intent."source" <> 'APPLICANT'
+ AND alcs.notice_of_intent.audit_created_by <> 'oats_etl';
+ """
+ cursor.execute(update_created_by_alcs_query)
+ updated_created_by_alcs_count = cursor.rowcount
+
+ total_processed = (
+ updated_created_by_applicant_count
+ + updated_created_by_oats_count
+ + updated_created_by_alcs_count
+ )
+
+ logger.info(
+ f"Total source on 'notice_of_intent' updated: {total_processed}"
+ )
+ conn.commit()
+ except Exception as err:
+ logger.exception(err)
+ cursor.close()
+ conn.close()
+
+ logger.info(f"Finished {etl_name}")
diff --git a/bin/migrate-oats-data/noi/sql/insert_noi.sql b/bin/migrate-oats-data/noi/sql/insert_noi.sql
index 5444c66958..327ad82c53 100644
--- a/bin/migrate-oats-data/noi/sql/insert_noi.sql
+++ b/bin/migrate-oats-data/noi/sql/insert_noi.sql
@@ -52,6 +52,7 @@ alcs_gov AS (
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust Saturna Island' THEN 'Islands Trust Saturna Island (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust Sidney Island' THEN 'Islands Trust Sidney Island (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust- Comox Strathcona' THEN 'Islands Trust Comox Strathcona (Historical)'
+ WHEN oats_gov.oats_gov_name LIKE 'Comox-Strathcona (Historical)' THEN 'Comox-Strathcona Regional District (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust- Nanaimo' THEN 'Islands Trust Nanaimo (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust-Capital' THEN 'Islands Trust Capital (Historical)'
WHEN oats_gov.oats_gov_name LIKE 'Islands Trust-Powell River' THEN 'Islands Trust Powell River (Historical)'
diff --git a/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date.sql b/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date.sql
index b82ea08191..f265d220df 100644
--- a/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date.sql
+++ b/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date.sql
@@ -1,17 +1,10 @@
-WITH nois_with_one_or_zero_component_only AS (
- SELECT oaa.alr_application_id
- FROM oats.alcs_etl_applications_nois oaa
- WHERE oaa.application_class_code = 'NOI'
- and oaa.alr_change_code <> 'SRW'
-),
-decision_date_for_nois AS (
+WITH decision_date_for_nois AS (
SELECT oaad.alr_appl_decision_id,
- oaa.alr_application_id,
+ oaad.alr_application_id,
oaad.decision_date
FROM oats.oats_alr_appl_decisions oaad
- JOIN oats.oats_alr_applications oaa ON oaad.alr_application_id = oaa.alr_application_id
- WHERE oaa.application_class_code = 'NOI'
- AND oaad.decision_date IS NOT NULL
+ JOIN alcs.notice_of_intent noi ON oaad.alr_application_id::TEXT = noi.file_number
+ WHERE oaad.decision_date IS NOT NULL
)
SELECT alr_appl_decision_id,
alr_application_id,
diff --git a/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date_count.sql b/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date_count.sql
new file mode 100644
index 0000000000..52cd335dbb
--- /dev/null
+++ b/bin/migrate-oats-data/noi/sql/notice_of_intent_base/notice_of_intent_decision_date/notice_of_intent_decision_date_count.sql
@@ -0,0 +1,10 @@
+WITH decision_date_for_nois AS (
+ SELECT oaad.alr_appl_decision_id,
+ oaad.alr_application_id,
+ oaad.decision_date
+ FROM oats.oats_alr_appl_decisions oaad
+ JOIN alcs.notice_of_intent noi ON oaad.alr_application_id::TEXT = noi.file_number
+ WHERE oaad.decision_date IS NOT NULL
+)
+SELECT count(*)
+FROM decision_date_for_nois
\ No newline at end of file
diff --git a/portal-frontend/src/app/features/applications/alcs-edit-submission/alcs-edit-submission.component.ts b/portal-frontend/src/app/features/applications/alcs-edit-submission/alcs-edit-submission.component.ts
index 11e5929c6d..7710c7f394 100644
--- a/portal-frontend/src/app/features/applications/alcs-edit-submission/alcs-edit-submission.component.ts
+++ b/portal-frontend/src/app/features/applications/alcs-edit-submission/alcs-edit-submission.component.ts
@@ -245,7 +245,7 @@ export class AlcsEditSubmissionComponent implements OnInit, OnDestroy, AfterView
async onDownloadPdf(fileNumber: string | undefined) {
if (fileNumber) {
- await this.pdfGenerationService.generateSubmission(fileNumber);
+ await this.pdfGenerationService.generateAppSubmission(fileNumber);
}
}
diff --git a/portal-frontend/src/app/features/applications/edit-submission/edit-submission.component.ts b/portal-frontend/src/app/features/applications/edit-submission/edit-submission.component.ts
index eb88423a7b..c0863a6f81 100644
--- a/portal-frontend/src/app/features/applications/edit-submission/edit-submission.component.ts
+++ b/portal-frontend/src/app/features/applications/edit-submission/edit-submission.component.ts
@@ -289,7 +289,7 @@ export class EditSubmissionComponent implements OnInit, OnDestroy, AfterViewInit
async onDownloadPdf(fileNumber: string | undefined) {
if (fileNumber) {
- await this.pdfGenerationService.generateSubmission(fileNumber);
+ await this.pdfGenerationService.generateAppSubmission(fileNumber);
}
}
diff --git a/portal-frontend/src/app/features/applications/edit-submission/review-and-submit/review-and-submit.component.ts b/portal-frontend/src/app/features/applications/edit-submission/review-and-submit/review-and-submit.component.ts
index d8195e4445..3fba56b498 100644
--- a/portal-frontend/src/app/features/applications/edit-submission/review-and-submit/review-and-submit.component.ts
+++ b/portal-frontend/src/app/features/applications/edit-submission/review-and-submit/review-and-submit.component.ts
@@ -60,7 +60,7 @@ export class ReviewAndSubmitComponent extends StepComponent implements OnInit, O
async onDownloadPdf(fileNumber: string | undefined) {
if (fileNumber) {
- await this.pdfGenerationService.generateSubmission(fileNumber);
+ await this.pdfGenerationService.generateAppSubmission(fileNumber);
}
}
}
diff --git a/portal-frontend/src/app/features/applications/view-submission/view-application-submission.component.ts b/portal-frontend/src/app/features/applications/view-submission/view-application-submission.component.ts
index 83a64af5ef..3d273f824d 100644
--- a/portal-frontend/src/app/features/applications/view-submission/view-application-submission.component.ts
+++ b/portal-frontend/src/app/features/applications/view-submission/view-application-submission.component.ts
@@ -115,7 +115,7 @@ export class ViewApplicationSubmissionComponent implements OnInit, OnDestroy {
async onDownloadSubmissionPdf(fileNumber: string | undefined) {
if (fileNumber) {
- await this.pdfGenerationService.generateSubmission(fileNumber);
+ await this.pdfGenerationService.generateAppSubmission(fileNumber);
}
}
}
diff --git a/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.spec.ts b/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.spec.ts
index dc910ac2ac..d73e471d43 100644
--- a/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.spec.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.spec.ts
@@ -3,11 +3,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatDialogModule } from '@angular/material/dialog';
import { RouterTestingModule } from '@angular/router/testing';
-import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { CodeService } from '../../../services/code/code.service';
import { NoticeOfIntentDocumentService } from '../../../services/notice-of-intent-document/notice-of-intent-document.service';
import { NoticeOfIntentSubmissionDraftService } from '../../../services/notice-of-intent-submission/notice-of-intent-submission-draft.service';
-import { NoticeOfIntentSubmissionService } from '../../../services/notice-of-intent-submission/notice-of-intent-submission.service';
import { PdfGenerationService } from '../../../services/pdf-generation/pdf-generation.service';
import { ToastService } from '../../../services/toast/toast.service';
import { AlcsEditSubmissionComponent } from './alcs-edit-submission.component';
@@ -15,11 +13,8 @@ import { AlcsEditSubmissionComponent } from './alcs-edit-submission.component';
describe('AlcsEditSubmissionComponent', () => {
let component: AlcsEditSubmissionComponent;
let fixture: ComponentFixture;
- let mockSubmissionService: DeepMocked;
beforeEach(async () => {
- mockSubmissionService = createMock();
-
await TestBed.configureTestingModule({
declarations: [AlcsEditSubmissionComponent],
providers: [
@@ -43,10 +38,6 @@ describe('AlcsEditSubmissionComponent', () => {
provide: PdfGenerationService,
useValue: {},
},
- {
- provide: NoticeOfIntentSubmissionService,
- useValue: mockSubmissionService,
- },
],
imports: [RouterTestingModule, MatAutocompleteModule, MatDialogModule],
schemas: [NO_ERRORS_SCHEMA],
diff --git a/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.ts b/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.ts
index 15714dc0b3..c042ae32ca 100644
--- a/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/alcs-edit-submission/alcs-edit-submission.component.ts
@@ -8,7 +8,6 @@ import { NoticeOfIntentDocumentDto } from '../../../services/notice-of-intent-do
import { NoticeOfIntentDocumentService } from '../../../services/notice-of-intent-document/notice-of-intent-document.service';
import { NoticeOfIntentSubmissionDraftService } from '../../../services/notice-of-intent-submission/notice-of-intent-submission-draft.service';
import { NoticeOfIntentSubmissionDetailedDto } from '../../../services/notice-of-intent-submission/notice-of-intent-submission.dto';
-import { NoticeOfIntentSubmissionService } from '../../../services/notice-of-intent-submission/notice-of-intent-submission.service';
import { ToastService } from '../../../services/toast/toast.service';
import { CustomStepperComponent } from '../../../shared/custom-stepper/custom-stepper.component';
import { OverlaySpinnerService } from '../../../shared/overlay-spinner/overlay-spinner.service';
@@ -59,7 +58,6 @@ export class AlcsEditSubmissionComponent implements OnInit, OnDestroy, AfterView
constructor(
private noticeOfIntentSubmissionDraftService: NoticeOfIntentSubmissionDraftService,
- private noticeOfIntentSubmissionService: NoticeOfIntentSubmissionService,
private noticeOfIntentDocumentService: NoticeOfIntentDocumentService,
private activatedRoute: ActivatedRoute,
private dialog: MatDialog,
diff --git a/portal-frontend/src/app/features/notice-of-intents/edit-submission/review-and-submit/review-and-submit.component.ts b/portal-frontend/src/app/features/notice-of-intents/edit-submission/review-and-submit/review-and-submit.component.ts
index 2c4b8a1528..7c378d718c 100644
--- a/portal-frontend/src/app/features/notice-of-intents/edit-submission/review-and-submit/review-and-submit.component.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/edit-submission/review-and-submit/review-and-submit.component.ts
@@ -57,7 +57,7 @@ export class ReviewAndSubmitComponent extends StepComponent implements OnInit, O
async onDownloadPdf(fileNumber: string | undefined) {
if (fileNumber) {
- await this.pdfGenerationService.generateSubmission(fileNumber);
+ await this.pdfGenerationService.generateAppSubmission(fileNumber);
}
}
}
diff --git a/portal-frontend/src/app/features/notice-of-intents/notice-of-intent-details/notice-of-intent-details.component.ts b/portal-frontend/src/app/features/notice-of-intents/notice-of-intent-details/notice-of-intent-details.component.ts
index 1a8be450de..dc207c8289 100644
--- a/portal-frontend/src/app/features/notice-of-intents/notice-of-intent-details/notice-of-intent-details.component.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/notice-of-intent-details/notice-of-intent-details.component.ts
@@ -1,8 +1,6 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
-import { ApplicationOwnerDto } from '../../../services/application-owner/application-owner.dto';
-import { PARCEL_OWNERSHIP_TYPE } from '../../../services/application-parcel/application-parcel.dto';
import { LocalGovernmentDto } from '../../../services/code/code.dto';
import { CodeService } from '../../../services/code/code.service';
import { NoticeOfIntentDocumentDto } from '../../../services/notice-of-intent-document/notice-of-intent-document.dto';
diff --git a/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.spec.ts b/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.spec.ts
index 05314f9cdf..dba8aecae4 100644
--- a/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.spec.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.spec.ts
@@ -2,6 +2,7 @@ import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
+import { PdfGenerationService } from '../../../services/pdf-generation/pdf-generation.service';
import { ConfirmationDialogService } from '../../../shared/confirmation-dialog/confirmation-dialog.service';
import { BehaviorSubject } from 'rxjs';
import { NoticeOfIntentDocumentService } from '../../../services/notice-of-intent-document/notice-of-intent-document.service';
@@ -17,11 +18,13 @@ describe('ViewNoticeOfIntentSubmissionComponent', () => {
let mockNoiDocumentService: DeepMocked;
let mockActivatedRoute: DeepMocked;
let mockDialogService: DeepMocked;
+ let mockPDFGenerationService: DeepMocked;
beforeEach(async () => {
mockNoiSubmissionService = createMock();
mockNoiDocumentService = createMock();
mockActivatedRoute = createMock();
+ mockPDFGenerationService = createMock();
mockActivatedRoute.paramMap = new BehaviorSubject(new Map());
@@ -44,6 +47,10 @@ describe('ViewNoticeOfIntentSubmissionComponent', () => {
provide: ConfirmationDialogService,
useValue: mockDialogService,
},
+ {
+ provide: PdfGenerationService,
+ useValue: mockPDFGenerationService,
+ },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
diff --git a/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.ts b/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.ts
index 94f8c28728..93a45aa022 100644
--- a/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.ts
+++ b/portal-frontend/src/app/features/notice-of-intents/view-submission/view-notice-of-intent-submission.component.ts
@@ -1,6 +1,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
+import { PdfGenerationService } from '../../../services/pdf-generation/pdf-generation.service';
import { ConfirmationDialogService } from '../../../shared/confirmation-dialog/confirmation-dialog.service';
import { NoticeOfIntentDocumentDto } from '../../../services/notice-of-intent-document/notice-of-intent-document.dto';
import { NoticeOfIntentDocumentService } from '../../../services/notice-of-intent-document/notice-of-intent-document.service';
@@ -26,6 +27,7 @@ export class ViewNoticeOfIntentSubmissionComponent implements OnInit, OnDestroy
private noiSubmissionService: NoticeOfIntentSubmissionService,
private noiDocumentService: NoticeOfIntentDocumentService,
private confirmationDialogService: ConfirmationDialogService,
+ private pdfGenerationService: PdfGenerationService,
private route: ActivatedRoute,
private router: Router
) {}
@@ -80,7 +82,9 @@ export class ViewNoticeOfIntentSubmissionComponent implements OnInit, OnDestroy
});
}
- onDownloadSubmissionPdf(fileNumber: string) {
- //TODO: When we add PDFs
+ async onDownloadSubmissionPdf(fileNumber: string) {
+ if (fileNumber) {
+ await this.pdfGenerationService.generateNoiSubmission(fileNumber);
+ }
}
}
diff --git a/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.spec.ts b/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.spec.ts
index 1c54b380fe..c589044fc7 100644
--- a/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.spec.ts
+++ b/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.spec.ts
@@ -52,7 +52,7 @@ describe('PdfGenerationService', () => {
const getPdfFile = jest.spyOn(fileModule, 'openPdfFile');
getPdfFile.mockReturnValue(undefined);
- await service.generateSubmission('fake');
+ await service.generateAppSubmission('fake');
expect(mockHttpClient.get).toHaveBeenCalledTimes(1);
expect(mockHttpClient.get).toBeCalledWith(`${environment.apiUrl}/pdf-generation/fake/submission`, {
diff --git a/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.ts b/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.ts
index cb1472283b..4f5f27ca7b 100644
--- a/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.ts
+++ b/portal-frontend/src/app/services/pdf-generation/pdf-generation.service.ts
@@ -24,36 +24,36 @@ export class PdfGenerationService {
private overlayService: OverlaySpinnerService
) {}
- async generateSubmission(fileNumber: string) {
- try {
- this.overlayService.showSpinner();
- const httpOptions = {
- responseType: 'blob' as 'json',
- };
- const data = await firstValueFrom(
- this.httpClient.get(`${this.serviceUrl}/${fileNumber}/submission`, httpOptions)
- );
-
- return openPdfFile(`${fileNumber}_${moment().format('MMM_DD_YYYY_hh-mm_Z')}`, data);
- } catch (e) {
- console.error(e);
- this.toastService.showErrorToast('Failed to generate pdf, please try again later');
- } finally {
- this.overlayService.hideSpinner();
- }
+ async generateAppSubmission(fileNumber: string) {
+ return await this.downloadPdf(
+ `${this.serviceUrl}/${fileNumber}/submission`,
+ `${fileNumber}_${moment().format('MMM_DD_YYYY_hh-mm_Z')}`
+ );
+ }
- return;
+ async generateNoiSubmission(fileNumber: string) {
+ return await this.downloadPdf(
+ `${this.serviceUrl}/${fileNumber}/noi-submission`,
+ `${fileNumber}_${moment().format('MMM_DD_YYYY_hh-mm_Z')}`
+ );
}
async generateReview(fileNumber: string) {
+ return await this.downloadPdf(
+ `${this.serviceUrl}/${fileNumber}/review`,
+ `${fileNumber}-Review_${moment().format('MMM_DD_YYYY_hh-mm_Z')}`
+ );
+ }
+
+ private async downloadPdf(url: string, fileName: string) {
try {
this.overlayService.showSpinner();
const httpOptions = {
responseType: 'blob' as 'json',
};
- const data = await firstValueFrom(this.httpClient.get(`${this.serviceUrl}/${fileNumber}/review`, httpOptions));
+ const data = await firstValueFrom(this.httpClient.get(url, httpOptions));
- return openPdfFile(`${fileNumber}-Review_${moment().format('MMM_DD_YYYY_hh-mm_Z')}`, data);
+ return openPdfFile(fileName, data);
} catch (e) {
console.error(e);
this.toastService.showErrorToast('Failed to generate pdf, please try again later');
diff --git a/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.spec.ts b/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.spec.ts
index 85b4d0f0d2..a1d18e56f9 100644
--- a/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.spec.ts
+++ b/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.spec.ts
@@ -53,6 +53,7 @@ describe('ApplicationAdvancedSearchService', () => {
andWhere: jest.fn().mockReturnThis(),
setParameters: jest.fn().mockReturnThis(),
leftJoin: jest.fn().mockReturnThis(),
+ withDeleted: jest.fn().mockReturnThis(),
};
const module: TestingModule = await Test.createTestingModule({
diff --git a/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.ts b/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.ts
index 9b8533ef8d..a777635829 100644
--- a/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.ts
+++ b/services/apps/alcs/src/alcs/search/application/application-advanced-search.service.ts
@@ -80,6 +80,10 @@ export class ApplicationAdvancedSearchService {
private compileApplicationGroupBySearchQuery(query) {
query = query
+ // FIXME: This is a quick fix for the search performance issues. It temporarily allows
+ // submissions with deleted submission types to be shown. For now, there are no
+ // deleted submission types, so this should be fine, but should be fixed soon.
+ .withDeleted()
.innerJoinAndMapOne(
'appSearch.applicationType',
'appSearch.applicationType',
diff --git a/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.spec.ts b/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.spec.ts
index 9099e7fa6a..ec12ea3290 100644
--- a/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.spec.ts
+++ b/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.spec.ts
@@ -52,6 +52,7 @@ describe('NoticeOfIntentService', () => {
andWhere: jest.fn().mockReturnThis(),
setParameters: jest.fn().mockReturnThis(),
leftJoin: jest.fn().mockReturnThis(),
+ withDeleted: jest.fn().mockReturnThis(),
};
const module: TestingModule = await Test.createTestingModule({
diff --git a/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.ts b/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.ts
index 4c8a8e0d32..f1c8a912ec 100644
--- a/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.ts
+++ b/services/apps/alcs/src/alcs/search/notice-of-intent/notice-of-intent-advanced-search.service.ts
@@ -75,6 +75,10 @@ export class NoticeOfIntentAdvancedSearchService {
query: SelectQueryBuilder,
) {
query = query
+ // FIXME: This is a quick fix for the search performance issues. It temporarily allows
+ // submissions with deleted submission types to be shown. For now, there are no
+ // deleted submission types, so this should be fine, but should be fixed soon.
+ .withDeleted()
.innerJoinAndMapOne(
'noiSearch.noticeOfIntentType',
'noiSearch.noticeOfIntentType',
diff --git a/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.spec.ts b/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.spec.ts
index 4bf9118d6c..05cacfbc75 100644
--- a/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.spec.ts
+++ b/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.spec.ts
@@ -52,6 +52,7 @@ describe('NotificationAdvancedSearchService', () => {
andWhere: jest.fn().mockReturnThis(),
setParameters: jest.fn().mockReturnThis(),
leftJoin: jest.fn().mockReturnThis(),
+ withDeleted: jest.fn().mockReturnThis(),
};
const module: TestingModule = await Test.createTestingModule({
diff --git a/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.ts b/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.ts
index aa07e6a865..228e62a7c7 100644
--- a/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.ts
+++ b/services/apps/alcs/src/alcs/search/notification/notification-advanced-search.service.ts
@@ -74,6 +74,10 @@ export class NotificationAdvancedSearchService {
query: SelectQueryBuilder,
) {
query = query
+ // FIXME: This is a quick fix for the search performance issues. It temporarily allows
+ // submissions with deleted submission types to be shown. For now, there are no
+ // deleted submission types, so this should be fine, but should be fixed soon.
+ .withDeleted()
.innerJoinAndMapOne(
'notificationSearch.notificationType',
'notificationSearch.notificationType',
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.spec.ts b/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.spec.ts
index 39462d1bc0..20240d2878 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.spec.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.spec.ts
@@ -1,17 +1,14 @@
import { createMock, DeepMocked } from '@golevelup/nestjs-testing';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
-import { create } from 'handlebars';
import { Repository } from 'typeorm';
import { NoticeOfIntentSubmissionStatusService } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-submission-status.service';
import { User } from '../../user/user.entity';
-import { CovenantTransferee } from '../application-submission/covenant-transferee/covenant-transferee.entity';
-import { CovenantTransfereeService } from '../application-submission/covenant-transferee/covenant-transferee.service';
import { NoticeOfIntentOwnerService } from '../notice-of-intent-submission/notice-of-intent-owner/notice-of-intent-owner.service';
import { NoticeOfIntentParcelService } from '../notice-of-intent-submission/notice-of-intent-parcel/notice-of-intent-parcel.service';
import { NoticeOfIntentSubmission } from '../notice-of-intent-submission/notice-of-intent-submission.entity';
import { NoticeOfIntentSubmissionService } from '../notice-of-intent-submission/notice-of-intent-submission.service';
-import { GenerateSubmissionDocumentService } from '../pdf-generation/generate-submission-document.service';
+import { GenerateNoiSubmissionDocumentService } from '../pdf-generation/generate-noi-submission-document.service';
import { NoticeOfIntentSubmissionDraftService } from './notice-of-intent-submission-draft.service';
describe('NoticeOfIntentSubmissionDraftService', () => {
@@ -20,7 +17,7 @@ describe('NoticeOfIntentSubmissionDraftService', () => {
let mockNoiSubmissionService: DeepMocked;
let mockParcelService: DeepMocked;
let mockAppOwnerService: DeepMocked;
- let mockGenerateSubmissionDocumentService: DeepMocked;
+ let mockGenerateSubmissionDocumentService: DeepMocked;
let mockNoiSubmissionStatusService: DeepMocked;
let mockUser;
@@ -57,7 +54,7 @@ describe('NoticeOfIntentSubmissionDraftService', () => {
useValue: mockAppOwnerService,
},
{
- provide: GenerateSubmissionDocumentService,
+ provide: GenerateNoiSubmissionDocumentService,
useValue: mockGenerateSubmissionDocumentService,
},
{
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.ts b/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.ts
index 0694fa8a79..2331ac4795 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission-draft/notice-of-intent-submission-draft.service.ts
@@ -9,6 +9,7 @@ import { NoticeOfIntentParcelUpdateDto } from '../notice-of-intent-submission/no
import { NoticeOfIntentParcelService } from '../notice-of-intent-submission/notice-of-intent-parcel/notice-of-intent-parcel.service';
import { NoticeOfIntentSubmission } from '../notice-of-intent-submission/notice-of-intent-submission.entity';
import { NoticeOfIntentSubmissionService } from '../notice-of-intent-submission/notice-of-intent-submission.service';
+import { GenerateNoiSubmissionDocumentService } from '../pdf-generation/generate-noi-submission-document.service';
@Injectable()
export class NoticeOfIntentSubmissionDraftService {
@@ -21,6 +22,7 @@ export class NoticeOfIntentSubmissionDraftService {
private noticeOfIntentParcelService: NoticeOfIntentParcelService,
private noticeOfIntentOwnerService: NoticeOfIntentOwnerService,
private noticeOfIntentSubmissionStatusService: NoticeOfIntentSubmissionStatusService,
+ private generateNoiSubmissionDocumentService: GenerateNoiSubmissionDocumentService,
) {}
async getOrCreateDraft(fileNumber: string, user: User) {
@@ -89,9 +91,8 @@ export class NoticeOfIntentSubmissionDraftService {
parcels: [],
owners: [],
});
- const savedSubmission = await this.noticeOfIntentSubmissionRepository.save(
- newSubmission,
- );
+ const savedSubmission =
+ await this.noticeOfIntentSubmissionRepository.save(newSubmission);
const statuses =
await this.noticeOfIntentSubmissionStatusService.getCopiedStatuses(
originalSubmission.uuid,
@@ -217,12 +218,11 @@ export class NoticeOfIntentSubmissionDraftService {
draft.isDraft = false;
await this.noticeOfIntentSubmissionRepository.save(draft);
- //Generate PDF
- //TODO: Turn this back on for PDFs
- // await this.generateSubmissionDocumentService.generateUpdate(
- // fileNumber,
- // user,
- // );
+ await this.generateNoiSubmissionDocumentService.generateUpdate(
+ fileNumber,
+ user,
+ );
+
this.logger.debug(`Published Draft for file number ${fileNumber}`);
}
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.controller.ts b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.controller.ts
index 3672193f25..60ae112991 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.controller.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.controller.ts
@@ -199,6 +199,7 @@ export class NoticeOfIntentSubmissionController {
await this.noticeOfIntentSubmissionService.submitToAlcs(
validatedApplicationSubmission,
+ req.user.entity,
);
const { primaryContact, submissionGovernment } =
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.module.ts b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.module.ts
index d67b02181b..e42578b6f5 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.module.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.module.ts
@@ -13,6 +13,7 @@ import { ParcelOwnershipType } from '../../common/entities/parcel-ownership-type
import { OwnerType } from '../../common/owner-type/owner-type.entity';
import { DocumentModule } from '../../document/document.module';
import { FileNumberModule } from '../../file-number/file-number.module';
+import { PdfGenerationModule } from '../pdf-generation/pdf-generation.module';
import { NoticeOfIntentOwnerController } from './notice-of-intent-owner/notice-of-intent-owner.controller';
import { NoticeOfIntentOwner } from './notice-of-intent-owner/notice-of-intent-owner.entity';
import { NoticeOfIntentOwnerService } from './notice-of-intent-owner/notice-of-intent-owner.service';
@@ -41,6 +42,7 @@ import { NoticeOfIntentSubmissionService } from './notice-of-intent-submission.s
forwardRef(() => BoardModule),
LocalGovernmentModule,
FileNumberModule,
+ PdfGenerationModule,
],
controllers: [
NoticeOfIntentSubmissionController,
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.spec.ts b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.spec.ts
index ab2c5b609c..7f32f328de 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.spec.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.spec.ts
@@ -20,6 +20,7 @@ import { NoticeOfIntentService } from '../../alcs/notice-of-intent/notice-of-int
import { NoticeOfIntentSubmissionProfile } from '../../common/automapper/notice-of-intent-submission.automapper.profile';
import { FileNumberService } from '../../file-number/file-number.service';
import { User } from '../../user/user.entity';
+import { GenerateNoiSubmissionDocumentService } from '../pdf-generation/generate-noi-submission-document.service';
import { ValidatedNoticeOfIntentSubmission } from './notice-of-intent-submission-validator.service';
import {
NoticeOfIntentSubmission,
@@ -38,6 +39,7 @@ describe('NoticeOfIntentSubmissionService', () => {
let mockNoiDocService: DeepMocked;
let mockFileNumberService: DeepMocked;
let mockNoiStatusService: DeepMocked;
+ let mockGenerateNoiSubmissionDocumentService: DeepMocked;
let mockNoiSubmission;
let mockQueryBuilder;
@@ -49,6 +51,7 @@ describe('NoticeOfIntentSubmissionService', () => {
mockNoiDocService = createMock();
mockFileNumberService = createMock();
mockNoiStatusService = createMock();
+ mockGenerateNoiSubmissionDocumentService = createMock();
const module: TestingModule = await Test.createTestingModule({
imports: [
@@ -71,6 +74,10 @@ describe('NoticeOfIntentSubmissionService', () => {
provide: NoticeOfIntentService,
useValue: mockNoiService,
},
+ {
+ provide: GenerateNoiSubmissionDocumentService,
+ useValue: mockGenerateNoiSubmissionDocumentService,
+ },
{
provide: LocalGovernmentService,
useValue: mockLGService,
@@ -252,6 +259,7 @@ describe('NoticeOfIntentSubmissionService', () => {
await expect(
service.submitToAlcs(
noticeOfIntentSubmission as ValidatedNoticeOfIntentSubmission,
+ new User(),
),
).rejects.toMatchObject(
new BaseServiceException(
@@ -267,14 +275,19 @@ describe('NoticeOfIntentSubmissionService', () => {
mockNoiStatusService.setStatusDate.mockResolvedValue(
new NoticeOfIntentSubmissionToSubmissionStatus(),
);
+ mockGenerateNoiSubmissionDocumentService.generateAndAttach.mockResolvedValue();
mockNoiService.submit.mockResolvedValue(mockNoticeOfIntent);
await service.submitToAlcs(
mockNoiSubmission as ValidatedNoticeOfIntentSubmission,
+ new User(),
);
expect(mockNoiService.submit).toBeCalledTimes(1);
expect(mockNoiStatusService.setStatusDate).toHaveBeenCalledTimes(1);
+ expect(
+ mockGenerateNoiSubmissionDocumentService.generateAndAttach,
+ ).toHaveBeenCalledTimes(1);
});
it('should populate noi subtypes', async () => {
@@ -283,6 +296,8 @@ describe('NoticeOfIntentSubmissionService', () => {
const fileNumber = 'fake';
const localGovernmentUuid = 'fake-uuid';
+ mockGenerateNoiSubmissionDocumentService.generateAndAttach.mockResolvedValue();
+
const mockDate = new Date('2022-01-01');
jest.useFakeTimers().setSystemTime(mockDate);
@@ -318,6 +333,7 @@ describe('NoticeOfIntentSubmissionService', () => {
mockNoiService.submit.mockResolvedValue(mockNoticeOfIntent);
await service.submitToAlcs(
mockNoiSubmission as ValidatedNoticeOfIntentSubmission,
+ new User(),
);
expect(mockNoiService.submit).toHaveBeenCalledTimes(1);
@@ -337,6 +353,9 @@ describe('NoticeOfIntentSubmissionService', () => {
'AEPM',
],
});
+ expect(
+ mockGenerateNoiSubmissionDocumentService.generateAndAttach,
+ ).toHaveBeenCalledTimes(1);
});
it('should update fields if notice of intent exists', async () => {
diff --git a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.ts b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.ts
index 3831d7c01c..b42428f12e 100644
--- a/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.ts
+++ b/services/apps/alcs/src/portal/notice-of-intent-submission/notice-of-intent-submission.service.ts
@@ -1,8 +1,8 @@
import { BaseServiceException } from '@app/common/exceptions/base.exception';
+import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
import { Mapper } from 'automapper-core';
import { InjectMapper } from 'automapper-nestjs';
-import { Injectable, Logger } from '@nestjs/common';
-import { InjectRepository } from '@nestjs/typeorm';
import {
FindOptionsRelations,
FindOptionsWhere,
@@ -10,10 +10,10 @@ import {
Not,
Repository,
} from 'typeorm';
-import { ApplicationDocument } from '../../alcs/application/application-document/application-document.entity';
import { LocalGovernmentService } from '../../alcs/local-government/local-government.service';
import { NoticeOfIntentDocument } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.entity';
import { NoticeOfIntentDocumentService } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.service';
+import { NoticeOfIntentSubmissionStatusType } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-status-type.entity';
import { NOI_SUBMISSION_STATUS } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-status.dto';
import { NoticeOfIntentSubmissionStatusService } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-submission-status.service';
import { NoticeOfIntentService } from '../../alcs/notice-of-intent/notice-of-intent.service';
@@ -24,6 +24,8 @@ import { FileNumberService } from '../../file-number/file-number.service';
import { User } from '../../user/user.entity';
import { FALLBACK_APPLICANT_NAME } from '../../utils/owner.constants';
import { filterUndefined } from '../../utils/undefined';
+import { GenerateNoiSubmissionDocumentService } from '../pdf-generation/generate-noi-submission-document.service';
+import { GenerateReviewDocumentService } from '../pdf-generation/generate-review-document.service';
import { ValidatedNoticeOfIntentSubmission } from './notice-of-intent-submission-validator.service';
import {
NoticeOfIntentSubmissionDetailedDto,
@@ -31,10 +33,9 @@ import {
NoticeOfIntentSubmissionUpdateDto,
} from './notice-of-intent-submission.dto';
import {
- PORTAL_TO_ALCS_STRUCTURE_MAP,
NoticeOfIntentSubmission,
+ PORTAL_TO_ALCS_STRUCTURE_MAP,
} from './notice-of-intent-submission.entity';
-import { NoticeOfIntentSubmissionStatusType } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-status-type.entity';
@Injectable()
export class NoticeOfIntentSubmissionService {
@@ -57,6 +58,8 @@ export class NoticeOfIntentSubmissionService {
@InjectRepository(NoticeOfIntentSubmissionStatusType)
private noticeOfIntentStatusRepository: Repository,
private noticeOfIntentService: NoticeOfIntentService,
+ @Inject(forwardRef(() => GenerateNoiSubmissionDocumentService))
+ private generateNoiSubmissionDocumentService: GenerateNoiSubmissionDocumentService,
private localGovernmentService: LocalGovernmentService,
private noticeOfIntentDocumentService: NoticeOfIntentDocumentService,
private fileNumberService: FileNumberService,
@@ -311,6 +314,7 @@ export class NoticeOfIntentSubmissionService {
async submitToAlcs(
noticeOfIntentSubmission: ValidatedNoticeOfIntentSubmission,
+ user: User,
) {
try {
const subtypes = this.populateNoiSubtype(noticeOfIntentSubmission);
@@ -330,6 +334,11 @@ export class NoticeOfIntentSubmissionService {
submittedNoi.dateSubmittedToAlc,
);
+ await this.generateNoiSubmissionDocumentService.generateAndAttach(
+ submittedNoi.fileNumber,
+ user,
+ );
+
return submittedNoi;
} catch (ex) {
this.logger.error(ex);
diff --git a/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.spec.ts b/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.spec.ts
new file mode 100644
index 0000000000..65dc3f6284
--- /dev/null
+++ b/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.spec.ts
@@ -0,0 +1,215 @@
+import { CdogsService } from '@app/common/cdogs/cdogs.service';
+import { createMock, DeepMocked } from '@golevelup/nestjs-testing';
+import { HttpStatus } from '@nestjs/common';
+import { Test, TestingModule } from '@nestjs/testing';
+import * as dayjs from 'dayjs';
+import * as timezone from 'dayjs/plugin/timezone';
+import * as utc from 'dayjs/plugin/utc';
+import { SUBMISSION_STATUS } from '../../alcs/application/application-submission-status/submission-status.dto';
+import { LocalGovernment } from '../../alcs/local-government/local-government.entity';
+import { LocalGovernmentService } from '../../alcs/local-government/local-government.service';
+import { NoticeOfIntentDocument } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.entity';
+import { NoticeOfIntentDocumentService } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.service';
+import { NoticeOfIntentSubmissionToSubmissionStatus } from '../../alcs/notice-of-intent/notice-of-intent-submission-status/notice-of-intent-status.entity';
+import { NoticeOfIntentType } from '../../alcs/notice-of-intent/notice-of-intent-type/notice-of-intent-type.entity';
+import { NoticeOfIntent } from '../../alcs/notice-of-intent/notice-of-intent.entity';
+import { NoticeOfIntentService } from '../../alcs/notice-of-intent/notice-of-intent.service';
+import { DOCUMENT_TYPE } from '../../document/document-code.entity';
+import { DOCUMENT_SOURCE } from '../../document/document.dto';
+import { Document } from '../../document/document.entity';
+import { User } from '../../user/user.entity';
+import { NoticeOfIntentOwnerService } from '../notice-of-intent-submission/notice-of-intent-owner/notice-of-intent-owner.service';
+import { NoticeOfIntentParcelService } from '../notice-of-intent-submission/notice-of-intent-parcel/notice-of-intent-parcel.service';
+import { NoticeOfIntentSubmission } from '../notice-of-intent-submission/notice-of-intent-submission.entity';
+import { NoticeOfIntentSubmissionService } from '../notice-of-intent-submission/notice-of-intent-submission.service';
+import { GenerateNoiSubmissionDocumentService } from './generate-noi-submission-document.service';
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+describe('GenerateNoiSubmissionDocumentService', () => {
+ let service: GenerateNoiSubmissionDocumentService;
+ let mockCdogsService: DeepMocked;
+ let mockNoiSubmissionService: DeepMocked;
+ let mockNoiLocalGovernmentService: DeepMocked;
+ let mockNoiService: DeepMocked;
+ let mockNoiParcelService: DeepMocked;
+ let mockNoinOwnerService: DeepMocked;
+ let mockNoiDocumentService: DeepMocked;
+
+ let mockSubmissionStatus;
+
+ beforeEach(async () => {
+ mockCdogsService = createMock();
+ mockNoiSubmissionService = createMock();
+ mockNoiLocalGovernmentService = createMock();
+ mockNoiService = createMock();
+ mockNoiParcelService = createMock();
+ mockNoinOwnerService = createMock();
+ mockNoiDocumentService = createMock();
+
+ mockNoiLocalGovernmentService.getByUuid.mockResolvedValue(
+ new LocalGovernment(),
+ );
+
+ const module: TestingModule = await Test.createTestingModule({
+ providers: [
+ GenerateNoiSubmissionDocumentService,
+ { provide: CdogsService, useValue: mockCdogsService },
+ {
+ provide: NoticeOfIntentSubmissionService,
+ useValue: mockNoiSubmissionService,
+ },
+ {
+ provide: LocalGovernmentService,
+ useValue: mockNoiLocalGovernmentService,
+ },
+ { provide: NoticeOfIntentService, useValue: mockNoiService },
+ {
+ provide: NoticeOfIntentParcelService,
+ useValue: mockNoiParcelService,
+ },
+ {
+ provide: NoticeOfIntentOwnerService,
+ useValue: mockNoinOwnerService,
+ },
+ {
+ provide: NoticeOfIntentDocumentService,
+ useValue: mockNoiDocumentService,
+ },
+ ],
+ }).compile();
+
+ mockSubmissionStatus = new NoticeOfIntentSubmissionToSubmissionStatus({
+ statusTypeCode: SUBMISSION_STATUS.IN_REVIEW_BY_ALC,
+ submissionUuid: 'fake',
+ });
+
+ service = module.get(
+ GenerateNoiSubmissionDocumentService,
+ );
+ });
+
+ it('should be defined', () => {
+ expect(service).toBeDefined();
+ });
+
+ it('should call cdogs service to generate pdf for pfrs', async () => {
+ mockCdogsService.generateDocument.mockResolvedValue({} as any);
+
+ mockNoiSubmissionService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntentSubmission({
+ fileNumber: 'fake',
+ localGovernmentUuid: 'fake-lg',
+ typeCode: 'PFRS',
+ status: mockSubmissionStatus,
+ soilProposedStructures: [],
+ }),
+ );
+ mockNoiDocumentService.list.mockResolvedValue([]);
+ mockNoiService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntent({
+ type: new NoticeOfIntentType({ portalLabel: 'fake-label' }),
+ }),
+ );
+ mockNoiParcelService.fetchByFileId.mockResolvedValue([]);
+ mockNoinOwnerService.fetchByApplicationFileId.mockResolvedValue([]);
+ const user = { user: { entity: 'Bruce' } };
+ const userEntity = new User({
+ name: user.user.entity,
+ });
+
+ const res = await service.generate('fake', userEntity);
+
+ expect(mockCdogsService.generateDocument).toBeCalledTimes(1);
+ expect(mockNoiLocalGovernmentService.getByUuid).toBeCalledTimes(1);
+ expect(res).toBeDefined();
+ });
+
+ it('should fail if wrong submission type used', async () => {
+ mockCdogsService.generateDocument.mockResolvedValue({} as any);
+
+ mockNoiSubmissionService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntentSubmission({
+ fileNumber: 'fake',
+ localGovernmentUuid: 'fake-lg',
+ typeCode: 'not a type',
+ status: mockSubmissionStatus,
+ soilProposedStructures: [],
+ }),
+ );
+ mockNoiDocumentService.list.mockResolvedValue([]);
+ mockNoiService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntent({
+ type: new NoticeOfIntentType({ portalLabel: 'fake-label' }),
+ }),
+ );
+ mockNoiParcelService.fetchByFileId.mockResolvedValue([]);
+ mockNoinOwnerService.fetchByApplicationFileId.mockResolvedValue([]);
+ const user = { user: { entity: 'Bruce' } };
+ const userEntity = new User({
+ name: user.user.entity,
+ });
+
+ const res = await service.generate('fake', userEntity);
+
+ expect(res).toBeUndefined();
+ expect(mockCdogsService.generateDocument).toBeCalledTimes(0);
+ });
+
+ it('should clear visibility for existing submissions on update', async () => {
+ mockCdogsService.generateDocument.mockResolvedValue({
+ status: HttpStatus.OK,
+ data: [],
+ } as any);
+
+ mockNoiSubmissionService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntentSubmission({
+ fileNumber: 'fake',
+ localGovernmentUuid: 'fake-lg',
+ typeCode: 'PFRS',
+ status: mockSubmissionStatus,
+ soilProposedStructures: [],
+ }),
+ );
+ mockNoiDocumentService.list.mockResolvedValue([
+ new NoticeOfIntentDocument({
+ typeCode: DOCUMENT_TYPE.ORIGINAL_SUBMISSION,
+ document: new Document({
+ source: DOCUMENT_SOURCE.APPLICANT,
+ fileName: 'Cats',
+ }),
+ }),
+ new NoticeOfIntentDocument({ document: new Document() }),
+ ]);
+ mockNoiService.getByFileNumber.mockResolvedValue(
+ new NoticeOfIntent({
+ type: new NoticeOfIntentType({ portalLabel: 'fake-label' }),
+ }),
+ );
+ mockNoiParcelService.fetchByFileId.mockResolvedValue([]);
+ mockNoinOwnerService.fetchByApplicationFileId.mockResolvedValue([]);
+ const user = { user: { entity: 'Bruce' } };
+ const userEntity = new User({
+ name: user.user.entity,
+ });
+ mockNoiDocumentService.update.mockResolvedValue(
+ new NoticeOfIntentDocument(),
+ );
+ mockNoiDocumentService.attachDocumentAsBuffer.mockResolvedValue(
+ new NoticeOfIntentDocument(),
+ );
+
+ await service.generateUpdate('fake', userEntity);
+
+ expect(mockCdogsService.generateDocument).toBeCalledTimes(1);
+ expect(mockNoiDocumentService.update).toHaveBeenCalledTimes(1);
+ expect(
+ mockNoiDocumentService.update.mock.calls[0][0].visibilityFlags,
+ ).toEqual([]);
+ expect(mockNoiLocalGovernmentService.getByUuid).toBeCalledTimes(1);
+ expect(mockNoiDocumentService.attachDocumentAsBuffer).toHaveBeenCalledTimes(
+ 1,
+ );
+ });
+});
diff --git a/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.ts b/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.ts
new file mode 100644
index 0000000000..91afa395b9
--- /dev/null
+++ b/services/apps/alcs/src/portal/pdf-generation/generate-noi-submission-document.service.ts
@@ -0,0 +1,380 @@
+import { CdogsService } from '@app/common/cdogs/cdogs.service';
+import {
+ forwardRef,
+ HttpStatus,
+ Inject,
+ Injectable,
+ Logger,
+} from '@nestjs/common';
+import * as config from 'config';
+import * as dayjs from 'dayjs';
+import { VISIBILITY_FLAG } from '../../alcs/application/application-document/application-document.entity';
+import { LocalGovernment } from '../../alcs/local-government/local-government.entity';
+import { LocalGovernmentService } from '../../alcs/local-government/local-government.service';
+import { NoticeOfIntentDocument } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.entity';
+import { NoticeOfIntentDocumentService } from '../../alcs/notice-of-intent/notice-of-intent-document/notice-of-intent-document.service';
+import { NoticeOfIntentService } from '../../alcs/notice-of-intent/notice-of-intent.service';
+import { OWNER_TYPE } from '../../common/owner-type/owner-type.entity';
+import { DOCUMENT_TYPE } from '../../document/document-code.entity';
+import { DOCUMENT_SOURCE, DOCUMENT_SYSTEM } from '../../document/document.dto';
+import { User } from '../../user/user.entity';
+import { formatBooleanToYesNoString } from '../../utils/boolean-formatter';
+import { NoticeOfIntentOwnerService } from '../notice-of-intent-submission/notice-of-intent-owner/notice-of-intent-owner.service';
+import { NoticeOfIntentParcel } from '../notice-of-intent-submission/notice-of-intent-parcel/notice-of-intent-parcel.entity';
+import { NoticeOfIntentParcelService } from '../notice-of-intent-submission/notice-of-intent-parcel/notice-of-intent-parcel.service';
+import { NoticeOfIntentSubmission } from '../notice-of-intent-submission/notice-of-intent-submission.entity';
+import { NoticeOfIntentSubmissionService } from '../notice-of-intent-submission/notice-of-intent-submission.service';
+
+export enum NOI_SUBMISSION_TYPES {
+ POFO = 'POFO',
+ ROSO = 'ROSO',
+ PFRS = 'PFRS',
+}
+
+class PdfTemplate {
+ templateName: string;
+ payload: any;
+}
+
+const NO_DATA = 'No Data';
+const NOT_APPLICABLE = 'Not Applicable';
+
+@Injectable()
+export class GenerateNoiSubmissionDocumentService {
+ private logger = new Logger(GenerateNoiSubmissionDocumentService.name);
+
+ constructor(
+ private documentGenerationService: CdogsService,
+ @Inject(forwardRef(() => NoticeOfIntentSubmissionService))
+ private noiSubmissionService: NoticeOfIntentSubmissionService,
+ private localGovernmentService: LocalGovernmentService,
+ private noticeOfIntentService: NoticeOfIntentService,
+ @Inject(forwardRef(() => NoticeOfIntentParcelService))
+ private parcelService: NoticeOfIntentParcelService,
+ @Inject(forwardRef(() => NoticeOfIntentOwnerService))
+ private ownerService: NoticeOfIntentOwnerService,
+ private noiDocumentService: NoticeOfIntentDocumentService,
+ ) {}
+
+ async generate(fileNumber: string, user: User) {
+ const submission = await this.noiSubmissionService.getByFileNumber(
+ fileNumber,
+ user,
+ );
+
+ const template = await this.getPdfTemplateBySubmissionType(submission);
+
+ if (template) {
+ return await this.documentGenerationService.generateDocument(
+ `${fileNumber}_submission_Date_Time`,
+ `${config.get('CDOGS.TEMPLATE_FOLDER')}/noi-submissions/${
+ template.templateName
+ }`,
+ template.payload,
+ );
+ }
+ return;
+ }
+
+ async generateAndAttach(fileNumber: string, user: User) {
+ const generatedRes = await this.generate(fileNumber, user);
+
+ if (generatedRes && generatedRes.status === HttpStatus.OK) {
+ await this.noiDocumentService.attachDocumentAsBuffer({
+ fileNumber: fileNumber,
+ fileName: `${fileNumber}_APP_Submission.pdf`,
+ user: user,
+ file: generatedRes.data,
+ mimeType: 'application/pdf',
+ fileSize: generatedRes.data.length,
+ documentType: DOCUMENT_TYPE.ORIGINAL_SUBMISSION,
+ source: DOCUMENT_SOURCE.APPLICANT,
+ system: DOCUMENT_SYSTEM.PORTAL,
+ visibilityFlags: [
+ VISIBILITY_FLAG.APPLICANT,
+ VISIBILITY_FLAG.COMMISSIONER,
+ VISIBILITY_FLAG.GOVERNMENT,
+ ],
+ });
+ }
+ }
+
+ async generateUpdate(fileNumber: string, user: User) {
+ const generatedRes = await this.generate(fileNumber, user);
+
+ if (generatedRes && generatedRes.status === HttpStatus.OK) {
+ const documents = await this.noiDocumentService.list(fileNumber);
+
+ const submissionDocuments = documents.filter(
+ (document) =>
+ [DOCUMENT_SOURCE.APPLICANT, DOCUMENT_SOURCE.ALC].includes(
+ document.document.source as DOCUMENT_SOURCE,
+ ) &&
+ [
+ DOCUMENT_TYPE.ORIGINAL_SUBMISSION,
+ DOCUMENT_TYPE.UPDATED_SUBMISSION,
+ ].includes(document.typeCode as DOCUMENT_TYPE),
+ );
+
+ //Clear Visibility Flags of existing documents
+ for (const submissionDocument of submissionDocuments) {
+ await this.noiDocumentService.update({
+ uuid: submissionDocument.uuid,
+ visibilityFlags: [],
+ user,
+ source: submissionDocument.document.source as DOCUMENT_SOURCE,
+ fileName: submissionDocument.document.fileName,
+ documentType: submissionDocument.type?.code as DOCUMENT_TYPE,
+ });
+ }
+
+ await this.noiDocumentService.attachDocumentAsBuffer({
+ fileNumber: fileNumber,
+ fileName: `${fileNumber}_Submission_Updated`,
+ user: user,
+ file: generatedRes.data,
+ mimeType: 'application/pdf',
+ fileSize: generatedRes.data.length,
+ documentType: DOCUMENT_TYPE.UPDATED_SUBMISSION,
+ source: DOCUMENT_SOURCE.ALC,
+ system: DOCUMENT_SYSTEM.PORTAL,
+ visibilityFlags: [
+ VISIBILITY_FLAG.APPLICANT,
+ VISIBILITY_FLAG.COMMISSIONER,
+ VISIBILITY_FLAG.GOVERNMENT,
+ ],
+ });
+ }
+ }
+
+ private async getPdfTemplateBySubmissionType(
+ submission: NoticeOfIntentSubmission,
+ ): Promise {
+ const documents = await this.noiDocumentService.list(submission.fileNumber);
+
+ let payload: any = await this.prepareSubmissionPdfData(
+ submission,
+ documents,
+ );
+
+ switch (submission.typeCode as NOI_SUBMISSION_TYPES) {
+ case NOI_SUBMISSION_TYPES.ROSO:
+ payload = this.populateSoilFields(payload, submission, documents);
+ return { payload, templateName: 'noi-roso-submission-template.docx' };
+ case NOI_SUBMISSION_TYPES.POFO:
+ payload = this.populateSoilFields(payload, submission, documents);
+ return { payload, templateName: 'noi-pofo-submission-template.docx' };
+ case NOI_SUBMISSION_TYPES.PFRS:
+ payload = this.populateSoilFields(payload, submission, documents);
+ return { payload, templateName: 'noi-pfrs-submission-template.docx' };
+ default:
+ this.logger.error(
+ `Could not find template for application submission type ${submission.typeCode}`,
+ );
+ return;
+ }
+ }
+
+ private async prepareSubmissionPdfData(
+ submission: NoticeOfIntentSubmission,
+ documents: NoticeOfIntentDocument[],
+ ) {
+ const noticeOfIntent = await this.noticeOfIntentService.getByFileNumber(
+ submission.fileNumber,
+ );
+
+ let localGovernment: LocalGovernment | undefined;
+ if (submission.localGovernmentUuid) {
+ localGovernment = await this.localGovernmentService.getByUuid(
+ submission.localGovernmentUuid,
+ );
+ }
+
+ const parcels = await this.parcelService.fetchByFileId(
+ submission.fileNumber,
+ );
+
+ const owners = await this.ownerService.fetchByApplicationFileId(
+ submission.fileNumber,
+ );
+
+ const primaryContact = owners.find(
+ (e) => e.uuid === submission.primaryContactOwnerUuid,
+ );
+
+ const otherDocuments = documents.filter(
+ (e) =>
+ !e.typeCode ||
+ [
+ DOCUMENT_TYPE.PHOTOGRAPH,
+ DOCUMENT_TYPE.PROFESSIONAL_REPORT,
+ DOCUMENT_TYPE.OTHER,
+ ].includes((e.typeCode ?? 'undefined') as DOCUMENT_TYPE),
+ );
+
+ const proposalMap = documents.filter(
+ (document) => document.type?.code === DOCUMENT_TYPE.PROPOSAL_MAP,
+ );
+
+ const data = {
+ noData: NO_DATA,
+ generatedDateTime: dayjs
+ .tz(new Date(), 'Canada/Pacific')
+ .format('MMM DD, YYYY hh:mm:ss Z'),
+ purpose: submission.purpose,
+ fileNumber: submission.fileNumber,
+ localGovernment: localGovernment?.name,
+ status: submission.status.statusType,
+ applicant: submission.applicant,
+ selectedThirdPartyAgent: primaryContact?.type.code === OWNER_TYPE.AGENT,
+ primaryContactFirstName: primaryContact?.firstName,
+ primaryContactLastName: primaryContact?.lastName,
+ primaryContactOrganizationName: primaryContact?.organizationName,
+ primaryContactEmail: primaryContact?.email,
+ primaryContactPhoneNumber: primaryContact?.phoneNumber,
+ primaryContactType: primaryContact?.type?.label,
+ organizationText:
+ primaryContact?.type.code === OWNER_TYPE.CROWN
+ ? 'Department'
+ : 'Organization (If Applicable)',
+ isGovernmentSetup:
+ !localGovernment || localGovernment.bceidBusinessGuid !== null,
+
+ // Land use
+ parcelsAgricultureDescription: submission.parcelsAgricultureDescription,
+ parcelsAgricultureImprovementDescription:
+ submission.parcelsAgricultureImprovementDescription,
+ parcelsNonAgricultureUseDescription:
+ submission.parcelsNonAgricultureUseDescription,
+ northLandUseType: submission.northLandUseType,
+ northLandUseTypeDescription: submission.northLandUseTypeDescription,
+ eastLandUseType: submission.eastLandUseType,
+ eastLandUseTypeDescription: submission.eastLandUseTypeDescription,
+ southLandUseType: submission.southLandUseType,
+ southLandUseTypeDescription: submission.southLandUseTypeDescription,
+ westLandUseType: submission.westLandUseType,
+ westLandUseTypeDescription: submission.westLandUseTypeDescription,
+
+ //Common File Types
+ proposalMap: proposalMap.find((d) => d)?.document.fileName,
+
+ // Other attachments
+ otherAttachments: otherDocuments.map((e) => ({
+ type: e.type?.description ?? '',
+ description: e.description ?? '',
+ name: e.document.fileName,
+ noData: NO_DATA,
+ })),
+
+ applicationTypePortalLabel: noticeOfIntent?.type.portalLabel,
+ parcels: this.mapParcelsWithOwners(parcels),
+ };
+
+ return data;
+ }
+
+ private mapParcelsWithOwners(parcels: NoticeOfIntentParcel[]) {
+ return parcels.map((e) => ({
+ ...e,
+ pid: this.formatPid(e.pid),
+ noData: NO_DATA,
+ purchasedDate: e.purchasedDate ? e.purchasedDate : undefined,
+ certificateOfTitle: e.certificateOfTitle?.document.fileName,
+ ownershipType: e.ownershipType?.label,
+ owners: e.owners.map((o) => ({
+ ...o,
+ noData: NO_DATA,
+ notApplicable: NOT_APPLICABLE,
+ name: `${o.firstName} ${o.lastName}`,
+ organizationName: o.organizationName,
+ corporateSummary: o.corporateSummary?.document.fileName,
+ })),
+ }));
+ }
+
+ private populateSoilFields(
+ pdfData: any,
+ submission: NoticeOfIntentSubmission,
+ documents: NoticeOfIntentDocument[],
+ ) {
+ const crossSections = documents.filter(
+ (document) => document.type?.code === DOCUMENT_TYPE.CROSS_SECTIONS,
+ );
+
+ const reclamationPlans = documents.filter(
+ (document) => document.type?.code === DOCUMENT_TYPE.RECLAMATION_PLAN,
+ );
+
+ const noticesOfWork = documents.filter(
+ (document) => document.type?.code === DOCUMENT_TYPE.NOTICE_OF_WORK,
+ );
+
+ const buildingPlans = documents.filter(
+ (document) => document.type?.code === DOCUMENT_TYPE.BUILDING_PLAN,
+ );
+
+ pdfData = {
+ ...pdfData,
+ fillProjectDuration: submission.fillProjectDuration,
+ soilIsFollowUp: formatBooleanToYesNoString(submission.soilIsFollowUp),
+ soilFollowUpIDs: submission.soilFollowUpIDs,
+ soilProjectDuration: submission.soilProjectDuration,
+ soilTypeRemoved: submission.soilTypeRemoved,
+ soilToRemoveVolume: submission.soilToRemoveVolume,
+ soilToRemoveArea: submission.soilToRemoveArea,
+ soilToRemoveMaximumDepth: submission.soilToRemoveMaximumDepth,
+ soilToRemoveAverageDepth: submission.soilToRemoveAverageDepth,
+ soilAlreadyRemovedVolume: submission.soilAlreadyRemovedVolume,
+ soilAlreadyRemovedArea: submission.soilAlreadyRemovedArea,
+ soilAlreadyRemovedMaximumDepth: submission.soilAlreadyRemovedMaximumDepth,
+ soilAlreadyRemovedAverageDepth: submission.soilAlreadyRemovedAverageDepth,
+
+ soilToPlaceVolume: submission.soilToPlaceVolume,
+ soilToPlaceArea: submission.soilToPlaceArea,
+ soilToPlaceMaximumDepth: submission.soilToPlaceMaximumDepth,
+ soilToPlaceAverageDepth: submission.soilToPlaceAverageDepth,
+ soilAlreadyPlacedVolume: submission.soilAlreadyPlacedVolume,
+ soilAlreadyPlacedArea: submission.soilAlreadyPlacedArea,
+ soilAlreadyPlacedMaximumDepth: submission.soilAlreadyPlacedMaximumDepth,
+ soilAlreadyPlacedAverageDepth: submission.soilAlreadyPlacedAverageDepth,
+ soilFillTypeToPlace: submission.soilFillTypeToPlace,
+
+ crossSections: crossSections.map((d) => d.document),
+ reclamationPlans: reclamationPlans.map((d) => d.document),
+ noticesOfWork: noticesOfWork.map((d) => d.document),
+ buildingPlans: buildingPlans.map((d) => d.document),
+ soilIsExtractionOrMining: formatBooleanToYesNoString(
+ submission.soilIsExtractionOrMining,
+ ),
+ soilIsAreaWideFilling: formatBooleanToYesNoString(
+ submission.soilIsAreaWideFilling,
+ ),
+ soilHasSubmittedNotice: formatBooleanToYesNoString(
+ submission.soilHasSubmittedNotice,
+ ),
+ soilIsRemovingSoilForNewStructure: formatBooleanToYesNoString(
+ submission.soilIsRemovingSoilForNewStructure,
+ ),
+ soilProposedStructures: submission.soilProposedStructures.map(
+ (structure, index) => ({ ...structure, index }),
+ ),
+ soilStructureFarmUseReason: submission.soilStructureFarmUseReason,
+ soilStructureResidentialUseReason:
+ submission.soilStructureResidentialUseReason,
+ soilAgriParcelActivity: submission.soilAgriParcelActivity,
+ soilStructureResidentialAccessoryUseReason:
+ submission.soilStructureResidentialAccessoryUseReason,
+ soilStructureOtherUseReason: submission.soilStructureOtherUseReason,
+ };
+
+ return pdfData;
+ }
+
+ private formatPid(pid?: string | null) {
+ const matches = pid?.match(/(.{1,3})/g);
+ if (matches) {
+ return matches.join('-');
+ }
+ return undefined;
+ }
+}
diff --git a/services/apps/alcs/src/portal/pdf-generation/generate-submission-document.service.ts b/services/apps/alcs/src/portal/pdf-generation/generate-submission-document.service.ts
index 767e5c715e..41d0c437ae 100644
--- a/services/apps/alcs/src/portal/pdf-generation/generate-submission-document.service.ts
+++ b/services/apps/alcs/src/portal/pdf-generation/generate-submission-document.service.ts
@@ -179,6 +179,9 @@ export class GenerateSubmissionDocumentService {
case APPLICATION_SUBMISSION_TYPES.NFUP:
payload = this.populateNfuData(payload, submission);
return { payload, templateName: 'nfu-submission-template.docx' };
+ case APPLICATION_SUBMISSION_TYPES.NARU:
+ payload = this.populateNaruData(payload, submission);
+ return { payload, templateName: 'naru-submission-template.docx' };
case APPLICATION_SUBMISSION_TYPES.TURP:
payload = this.populateTurData(payload, submission, documents);
return { payload, templateName: 'tur-submission-template.docx' };
@@ -365,6 +368,34 @@ export class GenerateSubmissionDocumentService {
};
}
+ private populateNaruData(pdfData: any, submission: ApplicationSubmission) {
+ return {
+ ...pdfData,
+ naruSubtypeLabel: submission.naruSubtype?.label,
+ naruSubtypeCode: submission.naruSubtypeCode,
+ naruFloorArea: submission.naruFloorArea,
+ naruResidenceNecessity: submission.naruResidenceNecessity,
+ naruLocationRationale: submission.naruLocationRationale,
+ naruInfrastructure: submission.naruInfrastructure,
+ naruExistingStructures: submission.naruExistingStructures,
+ naruSleepingUnits: submission.naruSleepingUnits,
+ naruAgriTourism: submission.naruAgriTourism,
+ showImportFill: submission.naruWillImportFill,
+ naruWillImportFill: formatBooleanToYesNoString(
+ submission.naruWillImportFill,
+ ),
+
+ // NFU Proposal => Soil and Fill
+ naruFillType: submission.naruFillType,
+ naruFillOrigin: submission.naruFillOrigin,
+ naruProjectDuration: submission.naruProjectDuration,
+ naruToPlaceMaximumDepth: submission.naruToPlaceMaximumDepth,
+ naruToPlaceAverageDepth: submission.naruToPlaceAverageDepth,
+ naruToPlaceVolume: submission.naruToPlaceVolume,
+ naruToPlaceArea: submission.naruToPlaceArea,
+ };
+ }
+
private populateTurData(
pdfData: any,
submission: ApplicationSubmission,
diff --git a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.spec.ts b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.spec.ts
index a5cb0e14cc..9503c28c23 100644
--- a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.spec.ts
+++ b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.spec.ts
@@ -4,6 +4,7 @@ import { FastifyReply } from 'fastify';
import { ClsService } from 'nestjs-cls';
import { mockKeyCloakProviders } from '../../../test/mocks/mockTypes';
import { User } from '../../user/user.entity';
+import { GenerateNoiSubmissionDocumentService } from './generate-noi-submission-document.service';
import { GenerateReviewDocumentService } from './generate-review-document.service';
import { GenerateSubmissionDocumentService } from './generate-submission-document.service';
import { PdfGenerationController } from './pdf-generation.controller';
@@ -11,10 +12,12 @@ import { PdfGenerationController } from './pdf-generation.controller';
describe('PdfGenerationController', () => {
let controller: PdfGenerationController;
let mockSubmissionDocumentService: DeepMocked;
+ let mockNoiSubmissionDocumentService: DeepMocked;
let mockReviewDocumentService: DeepMocked;
beforeEach(async () => {
mockSubmissionDocumentService = createMock();
+ mockNoiSubmissionDocumentService = createMock();
mockReviewDocumentService = createMock();
const module: TestingModule = await Test.createTestingModule({
@@ -24,6 +27,10 @@ describe('PdfGenerationController', () => {
provide: GenerateSubmissionDocumentService,
useValue: mockSubmissionDocumentService,
},
+ {
+ provide: GenerateNoiSubmissionDocumentService,
+ useValue: mockNoiSubmissionDocumentService,
+ },
{
provide: GenerateReviewDocumentService,
useValue: mockReviewDocumentService,
@@ -61,6 +68,24 @@ describe('PdfGenerationController', () => {
expect(mockResponse.send).toBeCalledWith('fake');
});
+ it('should successfully call service for generateNoiSubmission', async () => {
+ mockNoiSubmissionDocumentService.generate.mockResolvedValue({
+ data: 'fake',
+ } as any);
+
+ const mockResponse = createMock>();
+
+ await controller.generateNoiSubmission(mockResponse, 'fake-id', {
+ user: {
+ entity: new User(),
+ },
+ });
+
+ expect(mockNoiSubmissionDocumentService.generate).toBeCalledTimes(1);
+ expect(mockResponse.type).toBeCalledWith('application/pdf');
+ expect(mockResponse.send).toBeCalledWith('fake');
+ });
+
it('should successfully call service for generateReview', async () => {
mockReviewDocumentService.generate.mockResolvedValue({
data: 'fake',
diff --git a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.ts b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.ts
index d488a8ab44..d6a462a142 100644
--- a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.ts
+++ b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.controller.ts
@@ -2,6 +2,7 @@ import { Controller, Get, Param, Req, Res, UseGuards } from '@nestjs/common';
import { FastifyReply } from 'fastify';
import { PortalAuthGuard } from '../../common/authorization/portal-auth-guard.service';
import { User } from '../../user/user.entity';
+import { GenerateNoiSubmissionDocumentService } from './generate-noi-submission-document.service';
import { GenerateReviewDocumentService } from './generate-review-document.service';
import { GenerateSubmissionDocumentService } from './generate-submission-document.service';
@@ -10,6 +11,7 @@ import { GenerateSubmissionDocumentService } from './generate-submission-documen
export class PdfGenerationController {
constructor(
private submissionDocumentService: GenerateSubmissionDocumentService,
+ private noiSubmissionDocumentService: GenerateNoiSubmissionDocumentService,
private reviewDocumentService: GenerateReviewDocumentService,
) {}
@@ -31,6 +33,24 @@ export class PdfGenerationController {
}
}
+ @Get(':fileNumber/noi-submission')
+ async generateNoiSubmission(
+ @Res() resp: FastifyReply,
+ @Param('fileNumber') fileNumber: string,
+ @Req() req,
+ ) {
+ const user = req.user.entity as User;
+ const result = await this.noiSubmissionDocumentService.generate(
+ fileNumber,
+ user,
+ );
+
+ if (result) {
+ resp.type('application/pdf');
+ resp.send(result.data);
+ }
+ }
+
@Get(':fileNumber/review')
async generateReview(
@Res() resp: FastifyReply,
diff --git a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.module.ts b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.module.ts
index 7bc26faea1..0226a1e1cf 100644
--- a/services/apps/alcs/src/portal/pdf-generation/pdf-generation.module.ts
+++ b/services/apps/alcs/src/portal/pdf-generation/pdf-generation.module.ts
@@ -1,10 +1,13 @@
import { CdogsModule } from '@app/common/cdogs/cdogs.module';
import { forwardRef, Module } from '@nestjs/common';
import { ApplicationModule } from '../../alcs/application/application.module';
+import { NoticeOfIntentModule } from '../../alcs/notice-of-intent/notice-of-intent.module';
import { NotificationModule } from '../../alcs/notification/notification.module';
import { ApplicationSubmissionReviewModule } from '../application-submission-review/application-submission-review.module';
import { ApplicationSubmissionModule } from '../application-submission/application-submission.module';
+import { NoticeOfIntentSubmissionModule } from '../notice-of-intent-submission/notice-of-intent-submission.module';
import { NotificationSubmissionModule } from '../notification-submission/notification-submission.module';
+import { GenerateNoiSubmissionDocumentService } from './generate-noi-submission-document.service';
import { GenerateReviewDocumentService } from './generate-review-document.service';
import { GenerateSrwDocumentService } from './generate-srw-document.service';
import { GenerateSubmissionDocumentService } from './generate-submission-document.service';
@@ -18,14 +21,18 @@ import { PdfGenerationController } from './pdf-generation.controller';
forwardRef(() => ApplicationSubmissionReviewModule),
forwardRef(() => NotificationModule),
forwardRef(() => NotificationSubmissionModule),
+ forwardRef(() => NoticeOfIntentModule),
+ forwardRef(() => NoticeOfIntentSubmissionModule),
],
providers: [
GenerateSubmissionDocumentService,
GenerateReviewDocumentService,
GenerateSrwDocumentService,
+ GenerateNoiSubmissionDocumentService,
],
controllers: [PdfGenerationController],
exports: [
+ GenerateNoiSubmissionDocumentService,
GenerateSubmissionDocumentService,
GenerateReviewDocumentService,
GenerateSrwDocumentService,
diff --git a/services/apps/alcs/src/providers/typeorm/migrations/1706056918403-remove_dupe_govs.ts b/services/apps/alcs/src/providers/typeorm/migrations/1706056918403-remove_dupe_govs.ts
new file mode 100644
index 0000000000..67a6743a65
--- /dev/null
+++ b/services/apps/alcs/src/providers/typeorm/migrations/1706056918403-remove_dupe_govs.ts
@@ -0,0 +1,44 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class RemoveDupeGovs1706056918403 implements MigrationInterface {
+ public async up(queryRunner: QueryRunner): Promise {
+ //City of Port Alberni
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = 'b8a622d7-66da-4598-9687-2e8727fb2561';
+ `);
+
+ //District of Barriere
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = 'd3f74ba5-0dc3-43ea-a7e2-004882d65c33';
+ `);
+
+ //District of Fort St. James
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = 'e0684ddf-fa61-47c2-87b3-1c72bed5c365';
+ `);
+
+ //District of Hudson’s Hope
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = '79c3e16e-fdb9-4bb0-a427-f044a5d4bb95';
+ `);
+
+ //Northern Rockies (Historical)
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = '02afc23a-088c-4c09-aa69-b5c6c516cabc';
+ `);
+
+ //Village of Canal Flats
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = '6c0079df-2647-445f-9718-4433e9761eac';
+ `);
+
+ //Village of Port Clements
+ await queryRunner.query(`
+ DELETE FROM "alcs"."local_government" WHERE "uuid" = '18bd941a-7c0f-44fe-9d12-a084d2a5f372';
+ `);
+ }
+
+ public async down(): Promise {
+ //No
+ }
+}
diff --git a/services/templates/pdf/noi-submissions/noi-pfrs-submission-template.docx b/services/templates/pdf/noi-submissions/noi-pfrs-submission-template.docx
new file mode 100644
index 0000000000..e518388aa6
Binary files /dev/null and b/services/templates/pdf/noi-submissions/noi-pfrs-submission-template.docx differ
diff --git a/services/templates/pdf/noi-submissions/noi-pofo-submission-template.docx b/services/templates/pdf/noi-submissions/noi-pofo-submission-template.docx
new file mode 100644
index 0000000000..4f49ab16c4
Binary files /dev/null and b/services/templates/pdf/noi-submissions/noi-pofo-submission-template.docx differ
diff --git a/services/templates/pdf/noi-submissions/noi-roso-submission-template.docx b/services/templates/pdf/noi-submissions/noi-roso-submission-template.docx
new file mode 100644
index 0000000000..bd285fd098
Binary files /dev/null and b/services/templates/pdf/noi-submissions/noi-roso-submission-template.docx differ
diff --git a/services/templates/pdf/submissions/naru-submission-template.docx b/services/templates/pdf/submissions/naru-submission-template.docx
new file mode 100644
index 0000000000..9dca6d4062
Binary files /dev/null and b/services/templates/pdf/submissions/naru-submission-template.docx differ
diff --git a/services/templates/pdf/submissions/pfrs-submission-template.docx b/services/templates/pdf/submissions/pfrs-submission-template.docx
index d183d3adc7..55725f4462 100644
Binary files a/services/templates/pdf/submissions/pfrs-submission-template.docx and b/services/templates/pdf/submissions/pfrs-submission-template.docx differ
diff --git a/services/templates/pdf/submissions/subd-submission-template.docx b/services/templates/pdf/submissions/subd-submission-template.docx
index ba97237c73..e8cee18015 100644
Binary files a/services/templates/pdf/submissions/subd-submission-template.docx and b/services/templates/pdf/submissions/subd-submission-template.docx differ