Repository: bcgov/nr-compliance-enforcement
Pull request #1079: feat: Refer to external agency
Author: login: afwilcox
Created: 2025-05-20T17:21:45Z | Merged: 2025-05-22T02:30:42Z

<!-- Provide a general summary of your changes in the Title above -->

# Description

Please provide a summary of the change and the issue fixed. Please include relevant context. List dependency changes.

Fixes # (issue)

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration

- [ ] Test A
- [ ] Test B


## Checklist

- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes


## Further comments

<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->


---

Thanks for the PR!

Deployments, as required, will be available below:
- [Frontend](https://nr-compliance-enforcement-1079-frontend.apps.silver.devops.gov.bc.ca)
- [Backend](https://nr-compliance-enforcement-1079-frontend.apps.silver.devops.gov.bc.ca/api)


Please create PRs in draft mode.  Mark as ready to enable:
- [Analysis Workflow](https://github.com/bcgov/nr-compliance-enforcement/actions/workflows/analysis.yml)

After merge, new images are deployed in:
- [Merge Workflow](https://github.com/bcgov/nr-compliance-enforcement/actions/workflows/merge.yml)


Diff:
diff --git a/backend/package.json b/backend/package.json
index fb8a40a32e..007e1d7ada 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
 {
   "name": "nr-compliance-enforcement",
-  "version": "1.9.22",
+  "version": "1.9.23",
   "description": "BCGov devops quickstart. For reference, testing and new projects.",
   "main": "index.js",
   "scripts": {
diff --git a/backend/src/types/models/code-tables/agency.ts b/backend/src/types/models/code-tables/agency.ts
index d7e319c1a9..f1401ff5f3 100644
--- a/backend/src/types/models/code-tables/agency.ts
+++ b/backend/src/types/models/code-tables/agency.ts
@@ -2,4 +2,5 @@ import { BaseCodeTable } from "../../models/code-tables/code-table";
 
 export interface Agency extends BaseCodeTable {
   agency: string;
+  externalAgencyInd;
 }
diff --git a/backend/src/v1/agency_code/dto/agency_code.dto.ts b/backend/src/v1/agency_code/dto/agency_code.dto.ts
index 37778b5b9e..74a465c9fb 100644
--- a/backend/src/v1/agency_code/dto/agency_code.dto.ts
+++ b/backend/src/v1/agency_code/dto/agency_code.dto.ts
@@ -43,4 +43,10 @@ export class AgencyCodeDto {
     description: "The timestamp when the agency was last updated",
   })
   update_utc_timestamp: Date;
+
+  @ApiProperty({
+    example: "false",
+    description: "Flag to indicate if an agency has been onboarded to Natcom or is external.",
+  })
+  external_agency_ind: boolean;
 }
diff --git a/backend/src/v1/agency_code/entities/agency_code.entity.ts b/backend/src/v1/agency_code/entities/agency_code.entity.ts
index f35ff70b2f..7d43f61c52 100644
--- a/backend/src/v1/agency_code/entities/agency_code.entity.ts
+++ b/backend/src/v1/agency_code/entities/agency_code.entity.ts
@@ -55,6 +55,13 @@ export class AgencyCode {
   @Column()
   update_utc_timestamp: Date;
 
+  @ApiProperty({
+    example: "false",
+    description: "Flag to indicate if an agency has been onboarded to Natcom or is external.",
+  })
+  @Column()
+  external_agency_ind: boolean;
+
   @OneToMany(() => ViolationAgencyXref, (violationAgencyXref) => violationAgencyXref.agency_code)
   violationAgencyXrefs: ViolationAgencyXref[];
 
diff --git a/backend/src/v1/code-table/code-table.service.ts b/backend/src/v1/code-table/code-table.service.ts
index d34bcf838d..877450b247 100644
--- a/backend/src/v1/code-table/code-table.service.ts
+++ b/backend/src/v1/code-table/code-table.service.ts
@@ -102,16 +102,19 @@ export class CodeTableService {
     switch (table) {
       case "agency": {
         const data = await this._agencyRepository.find({ order: { display_order: "ASC" } });
-        let results = data.map(({ agency_code, short_description, long_description, display_order, active_ind }) => {
-          let table: Agency = {
-            agency: agency_code,
-            shortDescription: short_description,
-            longDescription: long_description,
-            displayOrder: display_order,
-            isActive: active_ind,
-          };
-          return table;
-        });
+        let results = data.map(
+          ({ agency_code, short_description, long_description, display_order, active_ind, external_agency_ind }) => {
+            let table: Agency = {
+              agency: agency_code,
+              shortDescription: short_description,
+              longDescription: long_description,
+              displayOrder: display_order,
+              isActive: active_ind,
+              externalAgencyInd: external_agency_ind,
+            };
+            return table;
+          },
+        );
 
         return results;
       }
@@ -776,6 +779,7 @@ export class CodeTableService {
               longDescription: longDescription,
               displayOrder: displayOrder,
               isActive: activeIndicator,
+              externalAgencyInd: false, // Included in the type but external agencies are only in Natcom, not CM
             };
             return table;
           },
diff --git a/backend/src/v1/complaint_referral/complaint_referral.service.ts b/backend/src/v1/complaint_referral/complaint_referral.service.ts
index c648b38a87..43eb408b4e 100644
--- a/backend/src/v1/complaint_referral/complaint_referral.service.ts
+++ b/backend/src/v1/complaint_referral/complaint_referral.service.ts
@@ -48,7 +48,7 @@ export class ComplaintReferralService {
     createComplaintReferralDto.create_user_id = idir;
     createComplaintReferralDto.update_user_id = idir;
 
-    const { complaint_identifier: id, referred_to_agency_code } = createComplaintReferralDto;
+    const { complaint_identifier: id, referred_to_agency_code, externalAgencyInd } = createComplaintReferralDto;
     const { type, fileName, tz, attachments } = createComplaintReferralDto.documentExportParams;
     // Generate the document export from the referring agency if sending the referral email
     let complaintExport;
@@ -59,14 +59,16 @@ export class ComplaintReferralService {
     const newComplaintReferral = this.complaintReferralRepository.create(createComplaintReferralDto);
     const result: any = await this.complaintReferralRepository.save(newComplaintReferral);
 
-    //Update owned_by_agency_code in complaint table
     if (result.complaint_referral_guid) {
-      await this.complaintRepository.update(
-        { complaint_identifier: id },
-        { owned_by_agency_code: referred_to_agency_code },
-      );
-    }
+      const updateData: any = {
+        owned_by_agency_code: referred_to_agency_code,
+      };
+      if (externalAgencyInd) {
+        updateData.complaint_status_code = "CLOSED";
+      }
 
+      await this.complaintRepository.update({ complaint_identifier: id }, updateData);
+    }
     // Clear the officer assigend to the complaint
     this._personService.clearAssignedOfficer(createComplaintReferralDto.complaint_identifier);
 
diff --git a/backend/src/v1/office/office.controller.v2.spec.ts b/backend/src/v1/office/office.controller.v2.spec.ts
index 8163916619..02fa7f5286 100644
--- a/backend/src/v1/office/office.controller.v2.spec.ts
+++ b/backend/src/v1/office/office.controller.v2.spec.ts
@@ -82,6 +82,7 @@ describe("Testing: Office Controller", () => {
       update_user_id: "",
       create_utc_timestamp: new Date(),
       update_utc_timestamp: new Date(),
+      external_agency_ind: false,
     };
 
     const payload: CreateOfficeDto = {
@@ -107,6 +108,7 @@ describe("Testing: Office Controller", () => {
       update_user_id: "",
       create_utc_timestamp: new Date(),
       update_utc_timestamp: new Date(),
+      external_agency_ind: false,
     };
 
     const payload: UpdateOfficeDto = {
diff --git a/backend/src/v1/office/office.service.v2.spec.ts b/backend/src/v1/office/office.service.v2.spec.ts
index d894b568b8..5498e4b730 100644
--- a/backend/src/v1/office/office.service.v2.spec.ts
+++ b/backend/src/v1/office/office.service.v2.spec.ts
@@ -109,6 +109,7 @@ describe("Testing: OfficeService", () => {
       update_user_id: "MSEARS",
       create_utc_timestamp: new Date(),
       update_utc_timestamp: new Date(),
+      external_agency_ind: false,
     };
 
     const payload: CreateOfficeDto = {
diff --git a/charts/app/Chart.yaml b/charts/app/Chart.yaml
index dd2b4bfedb..7062fd3298 100644
--- a/charts/app/Chart.yaml
+++ b/charts/app/Chart.yaml
@@ -16,13 +16,13 @@ type: application
 # This is the chart version. This version number should be incremented each time you make changes
 # to the chart and its templates, including the app version.
 # Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 1.9.22
+version: 1.9.23
 
 # This is the version number of the application being deployed. This version number should be
 # incremented each time you make changes to the application. Versions are not expected to
 # follow Semantic Versioning. They should reflect the version the application is using.
 # It is recommended to use it with quotes.
-appVersion: 1.9.22
+appVersion: 1.9.23
 
 dependencies:
   - name: postgresql
diff --git a/charts/app/values.yaml b/charts/app/values.yaml
index 348250c8bb..a7592453d3 100644
--- a/charts/app/values.yaml
+++ b/charts/app/values.yaml
@@ -241,7 +241,7 @@ crunchy: # enable it for TEST and PROD, for PR based pipelines simply use single
         memory: 64Mi
 
 bitnami-pg:
-  enabled: true
+  enabled: false
   image:
     registry: ghcr.io
     repository: bcgov/nr-containers/bitnami/postgresql
diff --git a/frontend/package.json b/frontend/package.json
index aae31d16d1..bcbe737e06 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
 {
   "name": "nr-sample-natcomplaints",
-  "version": "1.9.22",
+  "version": "1.9.23",
   "private": true,
   "dependencies": {
     "@craco/craco": "^7.1.0",
diff --git a/frontend/src/app/components/modal/instances/manage-collaborators-modal.tsx b/frontend/src/app/components/modal/instances/manage-collaborators-modal.tsx
index e5a19fc192..0a0a578284 100644
--- a/frontend/src/app/components/modal/instances/manage-collaborators-modal.tsx
+++ b/frontend/src/app/components/modal/instances/manage-collaborators-modal.tsx
@@ -65,7 +65,7 @@ export const ManageCollaboratorsModal: FC<ManageCollaboratorsModalProps> = ({ cl
   }, [selectedAgency, complaintType, allOfficers, collaborators]);
 
   const agencyOptions = agencies
-    .filter((agency) => agency.agency !== complaintData?.ownedBy)
+    .filter((agency) => !agency.externalAgencyInd && agency.agency !== complaintData?.ownedBy)
     .map((agency) => ({
       label: agency.longDescription,
       labelElement: <AgencyBanner agency={agency.agency} />,
diff --git a/frontend/src/app/components/modal/instances/refer-complaint-modal.tsx b/frontend/src/app/components/modal/instances/refer-complaint-modal.tsx
index 9c411fa44b..86038acf12 100644
--- a/frontend/src/app/components/modal/instances/refer-complaint-modal.tsx
+++ b/frontend/src/app/components/modal/instances/refer-complaint-modal.tsx
@@ -40,11 +40,12 @@ export const ReferComplaintModal: FC<ReferComplaintModalProps> = ({ close, submi
   const agencyOptions = agencies
     .filter(
       (agency) =>
-        agency.agency !== complaintData?.ownedBy &&
-        // agency's agency_code is in mapping for the complaint type
-        COMPLAINT_TYPE_AGENCY_MAPPING[complaint_type as keyof typeof COMPLAINT_TYPE_AGENCY_MAPPING].some(
-          (agency_code) => agency_code === agency.agency,
-        ),
+        agency.externalAgencyInd ||
+        (agency.agency !== complaintData?.ownedBy &&
+          // agency's agency_code is in mapping for the complaint type
+          COMPLAINT_TYPE_AGENCY_MAPPING[complaint_type as keyof typeof COMPLAINT_TYPE_AGENCY_MAPPING].some(
+            (agency_code) => agency_code === agency.agency,
+          )),
     )
     .map((agency) => ({
       label: agency.longDescription,
diff --git a/frontend/src/app/store/migrations.ts b/frontend/src/app/store/migrations.ts
index 78f1bca763..a7e0c280ce 100644
--- a/frontend/src/app/store/migrations.ts
+++ b/frontend/src/app/store/migrations.ts
@@ -31,6 +31,7 @@ import { AddParkAreaCode } from "@/app/store/migrations/migration-31";
 import { MultipleAssessments } from "@/app/store/migrations/migration-32";
 import { AddParkCache } from "@/app/store/migrations/migration-33";
 import { MultiplePreventions } from "@/app/store/migrations/migration-34";
+import { ExternalAgencies } from "@/app/store/migrations/migration-35";
 const BaseMigration = {
   0: (state: any) => {
     return {
@@ -75,6 +76,7 @@ migration = {
   ...MultipleAssessments,
   ...AddParkCache,
   ...MultiplePreventions,
+  ...ExternalAgencies,
 };
 
 export default migration;
diff --git a/frontend/src/app/store/migrations/migration-35.ts b/frontend/src/app/store/migrations/migration-35.ts
new file mode 100644
index 0000000000..be879b7bf1
--- /dev/null
+++ b/frontend/src/app/store/migrations/migration-35.ts
@@ -0,0 +1,11 @@
+export const ExternalAgencies = {
+  34: (state: any) => {
+    return {
+      ...state,
+      codeTables: {
+        ...state.codeTables,
+        agency: [],
+      },
+    };
+  },
+};
diff --git a/frontend/src/app/store/reducers/code-table.ts b/frontend/src/app/store/reducers/code-table.ts
index 7f123092f0..5aa075bb9a 100644
--- a/frontend/src/app/store/reducers/code-table.ts
+++ b/frontend/src/app/store/reducers/code-table.ts
@@ -766,11 +766,13 @@ export const selectCreatableComplaintTypeDropdown = createSelector(
 export const selectAgencyDropdown = createSelector(
   (state: RootState) => state.codeTables.agency,
   (agencyItems) =>
-    agencyItems.map(({ agency, longDescription, isActive }) => ({
-      label: longDescription,
-      value: agency,
-      isActive,
-    })),
+    agencyItems
+      .filter((agency) => !agency.externalAgencyInd) // filter out the pseudo-agencies
+      .map(({ agency, longDescription, isActive }) => ({
+        label: longDescription,
+        value: agency,
+        isActive,
+      })),
 );
 
 export const selectLeadAgencyDropdown = (state: RootState): Array<Option> => {
diff --git a/frontend/src/app/store/reducers/complaints.ts b/frontend/src/app/store/reducers/complaints.ts
index a12d21500d..a6636733d1 100644
--- a/frontend/src/app/store/reducers/complaints.ts
+++ b/frontend/src/app/store/reducers/complaints.ts
@@ -42,6 +42,7 @@ import { LinkedComplaint } from "@/app/types/app/complaints/linked-complaint";
 import { getUserAgency } from "@/app/service/user-service";
 import { Collaborator } from "@apptypes/app/complaints/collaborator";
 import { generateExportComplaintInputParams } from "@/app/store/reducers/documents-thunks";
+import { CodeTableState } from "@/app/types/state/code-table-state";
 
 type dtoAlias = WildlifeComplaintDto | AllegationComplaintDto | GeneralIncidentComplaintDto;
 
@@ -615,6 +616,10 @@ export const createComplaintReferral =
   async (dispatch, getState) => {
     try {
       const { attachments } = getState();
+      const agencyTable = getState()?.codeTables?.agency as CodeTableState['agency'] | undefined;
+      const agency = agencyTable?.find((item) => item.agency === referred_to_agency_code);
+      const externalAgencyInd = agency?.externalAgencyInd;
+
       const documentExportParams = generateExportComplaintInputParams(
         complaint_identifier,
         attachments,
@@ -631,6 +636,7 @@ export const createComplaintReferral =
         referral_reason,
         documentExportParams,
         complaint_url,
+        externalAgencyInd,
       });
 
       await post<any>(dispatch, parameters);
diff --git a/frontend/src/app/store/store.ts b/frontend/src/app/store/store.ts
index 1cfbc4029e..0ba66ff51c 100644
--- a/frontend/src/app/store/store.ts
+++ b/frontend/src/app/store/store.ts
@@ -19,7 +19,7 @@ const persistConfig = {
   storage,
   blacklist: ["app"],
   whitelist: ["codeTables", "officers"],
-  version: 33, // This needs to be incremented every time a new migration is added
+  version: 35, // This needs to be incremented every time a new migration is added
   debug: true,
   migrate: createMigrate(migration, { debug: false }),
 };
diff --git a/frontend/src/app/types/app/code-tables/agency.ts b/frontend/src/app/types/app/code-tables/agency.ts
index a624a22f30..18b10c3481 100644
--- a/frontend/src/app/types/app/code-tables/agency.ts
+++ b/frontend/src/app/types/app/code-tables/agency.ts
@@ -2,4 +2,5 @@ import { BaseCodeTable } from "./base-code-table";
 
 export interface Agency extends BaseCodeTable {
   agency: string;
+  externalAgencyInd: boolean;
 }
diff --git a/frontend/src/app/types/app/complaints/complaint-referral.ts b/frontend/src/app/types/app/complaints/complaint-referral.ts
index b9da472579..39062717c9 100644
--- a/frontend/src/app/types/app/complaints/complaint-referral.ts
+++ b/frontend/src/app/types/app/complaints/complaint-referral.ts
@@ -16,6 +16,7 @@ export interface ComplaintReferral {
   referred_to_agency_code: {
     agency_code: string;
     long_description: string;
+    external_agency_ind: string;
   };
   referral_reason: string;
   referral_date: string;
diff --git a/frontend/src/assets/images/icons/ce-external-icon.svg b/frontend/src/assets/images/icons/ce-external-icon.svg
new file mode 100644
index 0000000000..ab11c33923
--- /dev/null
+++ b/frontend/src/assets/images/icons/ce-external-icon.svg
@@ -0,0 +1,42 @@
+<svg width="39" height="39" viewBox="0 0 39 39" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_21597_494235)">
+<g clip-path="url(#clip1_21597_494235)">
+<g clip-path="url(#clip2_21597_494235)">
+<rect x="-1.5" y="-1.5" width="42" height="42" rx="5" fill="#595959"/>
+<path d="M-12.8055 61.8378C-15.5051 62.9684 -16.5304 65.7401 -14.766 67.3223C-11.1359 70.5782 3.80875 68.9698 4.5119 66.7629C5.2046 64.5885 -7.17247 59.4779 -12.8055 61.8378Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-6.11644 54.596C0.0498811 52.4354 10.4038 67.8183 6.12994 71.45C3.50226 73.6831 -2.8588 73.4041 -8.04619 72.6702C-12.916 71.9813 -19.8743 69.1646 -21.5911 64.4866C-22.5001 62.0032 -21.9544 58.4649 -20.0292 57.2258C-18.5393 56.2671 -16.5278 56.6984 -16.0841 56.539C-13.9688 57.4167 -13.6584 57.7578 -11.973 57.9637C-9.77009 58.2329 -7.81224 55.1901 -6.11644 54.596Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M0.140059 47.03C7.30164 48.1737 15.084 70.6422 7.31537 75.813C2.3678 79.1062 -6.25759 77.652 -12.2697 75.911C-17.7923 74.3118 -26.9882 68.9751 -28.8487 61.3268C-29.9302 56.8827 -28.6161 50.631 -24.8507 48.8227C-21.8492 47.3816 -17.9818 48.6207 -17.3009 48.7162C-13.2761 50.882 -12.9506 51.9838 -9.95372 52.7256C-5.98574 53.7065 -2.85292 46.5497 0.140059 47.03Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M5.94304 39.5144C13.8503 41.6227 19.3108 73.4982 8.04727 80.2257C0.777866 84.5671 -10.1145 81.9628 -16.9467 79.2016C-23.1274 76.7044 -34.5223 68.8463 -36.5599 58.2166C-37.7891 51.8061 -35.7378 42.8546 -30.1263 40.4692C-25.6172 38.5517 -19.8587 40.5195 -18.9725 40.943C-12.7774 43.9055 -12.699 46.2595 -8.38601 47.5372C-2.65687 49.239 1.80188 38.4125 5.94304 39.5144Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M11.3971 32.3406C20.1538 35.3821 23.1742 76.6639 8.43022 84.9808C-1.17084 90.3483 -14.3256 86.6225 -21.972 82.8352C-28.818 79.4453 -42.3975 69.0666 -44.6226 55.4482C-45.9904 47.0771 -43.2111 35.4226 -35.7528 32.4562C-29.7335 30.0643 -22.1295 32.8699 -20.9944 33.5097C-13.0088 38.0113 -12.8023 40.8868 -7.17444 42.6912C0.317158 45.1139 6.15543 30.5205 11.3971 32.3406Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-41.6244 24.5387C-34.0975 21.6729 -24.6526 25.3125 -23.2614 26.1725C-13.5067 32.2033 -13.146 35.588 -6.20531 37.9361C3.05005 41.0672 10.3025 22.6696 16.6014 25.2576C26.277 29.233 26.82 79.9741 8.56353 89.8267C-3.34891 96.2556 -18.7915 91.3776 -27.2477 86.5591C-34.7575 82.2804 -50.5119 69.3809 -52.9298 52.7713C-54.4328 42.4403 -50.9307 28.0815 -41.6244 24.5387Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M39.8238 19.3104C36.8589 19.2856 33.3033 14.1429 35.9624 13.2842C37.4981 12.7875 41.5641 13.573 43.1612 14.9709C45.4105 16.9381 41.864 19.328 39.8238 19.3104Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M31.7663 14.0043C32.0565 13.3142 33.4151 13.0077 34.6547 12.1673C37.1694 10.4615 37.5301 9.65116 39.3161 8.6225C40.0127 8.22057 40.9498 7.69252 41.8647 7.93432C43.2657 8.30488 43.6154 9.90865 45.3935 12.7332C47.699 16.4035 48.5721 17.3524 47.9134 19.2973C47.7598 19.7496 47.5611 21.2141 46.4508 22.1075C44.9132 23.3427 43.1109 22.3885 40.8576 23.2061C38.1515 24.1864 36.8099 25.9313 35.6356 25.1602C34.8083 24.6171 34.7776 23.1296 34.1581 20.5397C33.6863 18.5667 33.3896 17.272 32.8158 16.2159C32.1277 14.9585 31.4435 14.7716 31.7663 14.0043Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M27.2344 15.4428C27.2847 14.2194 29.2059 13.6273 30.7893 11.8595C34.0365 8.23299 32.5322 5.64173 35.135 2.9923C36.2394 1.86757 37.9725 0.778125 39.7095 0.989217C42.1744 1.28919 43.0024 3.83601 46.7972 8.99958C51.7585 15.7506 53.8719 17.0727 53.4085 20.6704C53.3138 21.4037 53.2217 24.2191 51.2828 25.9137C48.6969 28.1736 45.5974 26.1176 41.549 27.8161C37.0256 29.7114 35.335 33.7633 33.8098 32.9248C32.7505 32.3425 33.1563 29.9976 32.342 25.4242C31.7657 22.1735 31.4095 20.2737 30.2574 18.7176C28.8034 16.7551 27.1801 16.7714 27.2344 15.4428Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M22.6978 16.8655C22.5018 15.1121 24.9896 14.2305 26.9194 11.5353C30.8926 5.98679 27.467 1.64994 30.9494 -2.65424C32.4401 -4.4959 34.9965 -6.14346 37.5497 -5.97158C41.0883 -5.73369 42.396 -2.26343 48.2015 5.25026C55.8343 15.0676 59.1214 16.7962 58.9057 22.0278C58.8632 23.048 58.8828 27.207 56.1166 29.7062C52.4819 32.9889 48.0787 29.8271 42.243 32.4118C35.8977 35.222 33.864 41.5789 31.9833 40.6757C30.6926 40.051 31.5193 36.8512 30.5253 30.2943C29.8392 25.766 29.4255 23.2702 27.6977 21.205C25.4706 18.5438 22.9076 18.7542 22.6978 16.8655Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M18.1509 18.2843C17.7111 16.0029 20.7583 14.8285 23.039 11.2079C27.7297 3.73406 22.3757 -2.33139 26.7541 -8.30404C28.6256 -10.8594 32.012 -13.0657 35.3801 -12.9363C39.995 -12.7585 41.7614 -8.35306 49.5928 1.49766C59.8729 14.3984 64.3467 16.529 64.3865 23.3819C64.3944 24.689 64.5238 30.1878 60.9335 33.4947C56.2454 37.7963 50.5469 33.5306 42.9201 37.0042C34.7514 40.7254 32.3747 49.3932 30.1463 48.4234C28.6237 47.7581 29.8718 43.7009 28.6982 35.1612C27.9003 29.3546 27.4317 26.266 25.1282 23.6891C22.126 20.3325 18.624 20.7377 18.1509 18.2843Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M13.5954 19.7025C14.3319 22.7218 18.7717 22.1206 22.5482 26.1725C25.4281 29.2618 25.9516 32.9418 26.8612 40.0274C28.212 50.5493 26.5443 55.4646 28.2989 56.1697C30.8782 57.2069 33.5974 46.2275 43.5905 41.5959C53.0047 37.2329 60.0101 42.6115 65.7438 37.2826C70.164 33.1738 69.9183 26.3352 69.8608 24.7347C69.5563 16.2623 63.9029 13.7279 50.975 -2.25624C41.1171 -14.444 38.8939 -19.7834 33.2001 -19.9017C29.0177 -19.9886 24.7995 -17.2222 22.5482 -13.9545C17.2765 -6.30815 24.5694 1.48916 19.1501 10.8798C16.5237 15.4304 12.9099 16.8936 13.5954 19.7025Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M51.2723 68.2993C50.4986 67.9072 48.874 68.6627 48.7302 69.8397C48.5675 71.1749 50.4058 72.2793 51.2135 71.9362C52.1689 71.5297 52.2989 68.8182 51.2723 68.2993Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M53.4523 75.3934C54.7711 74.1602 55.3475 71.897 56.0068 68.0176C56.6603 64.1709 56.5577 61.4169 55.9833 61.217C55.6703 61.1091 54.7835 61.9528 54.0228 62.5711C52.5989 63.7252 50.1614 63.5971 49.5366 63.722C47.2494 64.1794 46.5776 65.029 45.6216 66.6563C45.1792 67.4079 44.1473 69.0744 43.6931 71.035C43.0004 74.004 39.8957 74.919 40.2741 76.109C40.5923 77.1024 43.6683 77.3436 45.6627 77.3717C48.5211 77.4122 51.4134 77.2978 53.4523 75.3934Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M55.7382 78.7088C57.9059 76.5051 58.7586 72.8067 60.0722 65.8904C61.4066 58.9407 61.3981 54.1738 60.7407 53.9961C60.2473 53.8601 58.6495 55.5783 57.2916 56.8063C54.7319 59.1309 50.1496 58.7486 49.2053 58.8976C44.8988 59.5629 43.8375 61.1568 42.1685 64.1696C41.4732 65.4244 39.5657 68.483 38.6966 72.095C37.3589 77.6285 31.0168 79.0774 31.5415 81.1288C31.98 82.8372 37.8614 83.0992 41.5621 82.9587C46.7874 82.7555 52.0754 82.4339 55.7382 78.7088Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M58.0117 82.0026C61.0302 78.8291 62.1496 73.6929 64.1245 63.7416C66.13 53.6869 66.2235 46.9183 65.485 46.7497C64.8087 46.5928 62.5012 49.1808 60.5499 51.0218C56.8609 54.5228 50.128 53.8791 48.8635 54.0477C42.5391 54.8921 41.0824 57.2533 38.705 61.6581C37.7562 63.4155 34.9743 67.8667 33.6889 73.1283C31.7081 81.2269 22.128 83.2084 22.7978 86.1219C23.3572 88.5452 32.0453 88.8386 37.451 88.5177C45.0419 88.0896 52.7335 87.5524 58.0117 82.0026Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M60.278 85.2945C64.1493 81.1511 65.534 74.5765 68.1702 61.5901C70.8495 48.4319 71.0456 39.6634 70.2235 39.5007C69.3635 39.3288 66.345 42.7762 63.8016 45.2315C58.9763 49.901 50.1006 49.0024 48.5159 49.1952C40.1734 50.2075 38.3103 53.3419 35.235 59.1433C34.0287 61.4019 30.3763 67.2471 28.676 74.1589C26.0502 84.822 13.2321 87.3368 14.0489 91.1122C14.7279 94.2492 26.2228 94.5622 33.334 94.0747C43.2873 93.4035 53.387 92.6709 60.278 85.2945Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M48.165 44.3407C37.8052 45.5171 35.5526 49.4383 31.7624 56.6272C30.3051 59.391 25.7751 66.6263 23.6591 75.1876C20.3917 88.4164 4.3335 91.4638 5.29609 96.1007C6.09596 99.9565 20.3969 100.283 29.2138 99.6297C41.5334 98.7148 54.0392 97.7881 62.5417 88.585C67.2658 83.4711 68.9152 75.4601 72.2133 59.4374C75.5612 43.1748 75.8598 32.4085 74.958 32.2504C73.9124 32.0674 70.1875 36.3676 67.0508 39.4392C61.0883 45.2766 50.0705 44.1244 48.165 44.3407Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-36.0698 12.9058C-34.8935 17.4151 -26.4504 15.297 -16.3997 25.4536C-12.7984 29.0931 -10.3837 32.9 -5.61717 33.4921C-5.1362 33.5516 -0.361158 34.0737 3.00888 31.0086C7.24872 27.1528 4.6609 21.8036 9.60912 14.3435C11.4317 11.5954 11.7349 12.3934 13.0726 10.2263C18.7815 0.975487 10.0744 -8.4158 15.0984 -17.8104C17.4111 -22.1348 20.8491 -23.1275 20.5224 -23.6922C19.1122 -26.1148 -39.2483 0.754593 -36.0698 12.9058Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-4.31019 27.3273C-4.02135 27.2881 -0.872843 27.5234 1.28694 25.3562C4.01787 22.6199 2.07112 18.8523 5.20003 13.7658C6.35148 11.8928 6.51158 12.4006 7.33106 10.919C10.8272 4.59672 4.55242 -1.76347 7.62513 -7.95244C9.04712 -10.8175 11.2918 -11.4743 10.9579 -11.7541C9.4477 -13.0297 -29.0735 5.81622 -26.0478 14.0168C-24.9134 17.0923 -18.7203 16.0009 -11.6338 22.6251C-9.05714 25.0321 -7.31232 27.7344 -4.31019 27.3273Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-3.00322 21.1619C-2.92088 20.9547 -1.38126 20.9593 -0.435009 19.7038C0.79355 18.0752 -0.514735 15.9055 0.790936 13.1887C1.26994 12.1914 1.28955 12.4045 1.59015 11.6118C2.88079 8.20227 -0.974791 4.89277 0.152477 1.90482C0.679842 0.498411 1.66008 0.258563 1.39868 0.183406C-0.512119 0.454623 -18.8595 10.8961 -16.0292 15.1278C-14.9242 16.7812 -10.9686 16.7021 -6.87122 19.7966C-5.31396 20.973 -4.29451 21.8343 -3.00322 21.1619Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+<path d="M-1.69624 14.9971C-2.71045 12.0333 -6.70327 11.0255 -8.16578 12.1215C-10.3223 13.7397 -8.48272 15.8467 -6.00926 16.2388C-4.16054 16.5296 -0.535644 18.3889 -1.69624 14.9971Z" stroke="white" stroke-width="0.75" stroke-miterlimit="10"/>
+</g>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_21597_494235">
+<rect width="39" height="39" rx="5" fill="white"/>
+</clipPath>
+<clipPath id="clip1_21597_494235">
+<rect width="39" height="39" rx="5" fill="white"/>
+</clipPath>
+<clipPath id="clip2_21597_494235">
+<rect x="-1.5" y="-1.5" width="42" height="42" rx="5" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/frontend/src/assets/images/icons/index.ts b/frontend/src/assets/images/icons/index.ts
index 8da1ba9bab..1f0a539fa1 100644
--- a/frontend/src/assets/images/icons/index.ts
+++ b/frontend/src/assets/images/icons/index.ts
@@ -1,11 +1,17 @@
 import COS from "./ce-cos-icon.svg";
 import PARKS from "./ce-parks-icon.svg";
 import EPO from "./ce-ceeb-icon.svg";
+import EXTERNAL from "./ce-external-icon.svg";
 
-const agencyIcons: { [key: string]: any; COS: any; PARKS: any; EPO: any } = {
+const agencyIcons: { [key: string]: any } = {
   COS: COS,
   PARKS: PARKS,
   EPO: EPO,
+  DFO: EXTERNAL,
+  ECCC: EXTERNAL,
+  POL: EXTERNAL,
+  NROS: EXTERNAL,
+  OTH: EXTERNAL,
 };
 
 export default agencyIcons;
diff --git a/migrations/migrations/R__Create-Test-Data.sql b/migrations/migrations/R__Create-Test-Data.sql
index 954cb08941..4ac203d11f 100644
--- a/migrations/migrations/R__Create-Test-Data.sql
+++ b/migrations/migrations/R__Create-Test-Data.sql
@@ -11433,6 +11433,35 @@ INSERT INTO
 UPDATE feature_agency_xref SET active_ind = false WHERE feature_code = 'ZONE_FLTR' AND agency_code = 'PARKS';
 UPDATE feature_agency_xref SET active_ind = false WHERE feature_code = 'REG_FLTR' AND agency_code = 'PARKS';
 
+---------------------------
+-- CE-225 Add external agencies
+---------------------------
+
+insert into agency_code (agency_code, short_description, long_description, display_order, active_ind, create_user_id, create_utc_timestamp, update_user_id, update_utc_timestamp, external_agency_ind)
+values ('DFO', 'Fisheries and Oceans Canada', 'Fisheries and Oceans Canada', 20, true, user, now(), user, now(), 'Y'),
+      ('ECCC', 'Environment and Climate Change Canada', 'Environment and Climate Change Canada', 10, true, user, now(), user, now(), 'Y'),
+      ('POL', 'Police', 'Police', 50, true, user, now(), user, now(), 'Y'),
+      ('NROS', 'Natural Resource Officer Service', 'Natural Resource Officer Service', 30, true, user, now(), user, now(), 'Y'),
+      ('OTH', 'Other', 'Other', 40, true, user, now(), user, now(), 'Y')
+on conflict do nothing;
+
+INSERT INTO
+  feature_agency_xref (
+    feature_code,
+    agency_code,
+    active_ind,
+    create_user_id,
+    create_utc_timestamp,
+    update_user_id,
+    update_utc_timestamp
+  )
+values ('REFEMAIL','DFO','N',user,now(),user,now()),
+('REFEMAIL','ECCC','N',user,now(),user,now()),
+('REFEMAIL','POL','N',user,now(),user,now()),
+('REFEMAIL','NROS','N',user,now(),user,now()),
+('REFEMAIL','OTH','N',user,now(),user,now())
+ON CONFLICT DO NOTHING;
+
 --------------------------
 -- New Changes above this line
 -------------------------
diff --git a/migrations/migrations/V0.44.0__CE-225-external-agency.sql b/migrations/migrations/V0.44.0__CE-225-external-agency.sql
new file mode 100644
index 0000000000..c33fe3c7fc
--- /dev/null
+++ b/migrations/migrations/V0.44.0__CE-225-external-agency.sql
@@ -0,0 +1,3 @@
+ALTER TABLE public.agency_code ADD column "external_agency_ind" boolean DEFAULT false;
+
+comment on column public.agency_code.external_agency_ind is 'Flag to indicate if an agency has been onboarded to Natcom or is external.';
diff --git a/webeoc/package.json b/webeoc/package.json
index b97b75fceb..7cb598c28b 100644
--- a/webeoc/package.json
+++ b/webeoc/package.json
@@ -1,6 +1,6 @@
 {
   "name": "webeoc",
-  "version": "1.9.22",
+  "version": "1.9.23",
   "description": "",
   "author": "",
   "private": true,


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

Adds a new `external_agency_ind` flag to agencies so that some agencies can be treated as external (pseudo-agencies) in both backend and frontend workflows.

- Database: introduces `external_agency_ind` column and seeds external agencies.
- Frontend: surfaces `externalAgencyInd` in icons, types, store migrations, filters, and referral logic.
- Backend: maps the flag in entity/DTO, includes it in code-table service, and adjusts referral service to close complaints for external agencies.

### Reviewed Changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.

<details>
<summary>Show a summary per file</summary>

| File                                                                                          | Description                                                 |
|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------|
| migrations/migrations/V0.44.0__CE-225-external-agency.sql                                     | Adds `external_agency_ind` column to `agency_code`.         |
| migrations/migrations/R__Create-Test-Data.sql                                                 | Seeds external agencies with the new flag.                  |
| frontend/src/assets/images/icons/index.ts                                                    | Registers an external agency icon and maps external codes.  |
| frontend/src/app/types/app/complaints/complaint-referral.ts                                  | Adds `external_agency_ind` field to referral type.          |
| frontend/src/app/types/app/code-tables/agency.ts                                             | Extends agency type with `externalAgencyInd`.               |
| frontend/src/app/store/store.ts                                                              | Bumps Redux Persist version for new migration.              |
| frontend/src/app/store/reducers/complaints.ts                                                | Passes `externalAgencyInd` when creating referrals.         |
| frontend/src/app/store/reducers/code-table.ts                                                | Filters out external agencies from dropdown.                |
| frontend/src/app/store/migrations/migration-35.ts                                            | Resets agency table state for migration.                   |
| frontend/src/app/store/migrations.ts                                                         | Registers the new migration.                                |
| frontend/src/app/components/modal/instances/refer-complaint-modal.tsx                        | Includes external agencies in referral modal options.       |
| frontend/src/app/components/modal/instances/manage-collaborators-modal.tsx                   | Excludes external agencies from collaborator options.       |
| charts/app/values.yaml                                                                       | Disables built-in Postgres chart for external config.       |
| backend/src/v1/office/office.service.v2.spec.ts                                              | Updates tests to include `external_agency_ind` in mocks.    |
| backend/src/v1/office/office.controller.v2.spec.ts                                           | Updates controller tests with the new flag.                 |
| backend/src/v1/complaint_referral/complaint_referral.service.ts                              | Closes complaints when referred to external agencies.      |
| backend/src/v1/code-table/code-table.service.ts                                              | Populates `externalAgencyInd` in agency code-table results. |
| backend/src/v1/agency_code/entities/agency_code.entity.ts                                    | Adds `external_agency_ind` column to the entity.            |
| backend/src/v1/agency_code/dto/agency_code.dto.ts                                            | Exposes the new flag in the DTO.                            |
| backend/src/types/models/code-tables/agency.ts                                               | Extends backend agency model with the new flag.             |
</details>



<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**frontend/src/app/types/app/complaints/complaint-referral.ts:19**
* The flag field is a boolean and should follow camelCase (`externalAgencyInd: boolean`) to match conventions and actual type.
```
external_agency_ind: string;
```
**backend/src/types/models/code-tables/agency.ts:5**
* Missing type annotation for `externalAgencyInd`; it should be declared as `externalAgencyInd: boolean`.
```
externalAgencyInd;
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T21:12:19Z
user: login: nayr974
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-21T21:19:42Z

Review comments:
diff_hunk: @@ -11433,6 +11433,35 @@ INSERT INTO
 UPDATE feature_agency_xref SET active_ind = false WHERE feature_code = 'ZONE_FLTR' AND agency_code = 'PARKS';
 UPDATE feature_agency_xref SET active_ind = false WHERE feature_code = 'REG_FLTR' AND agency_code = 'PARKS';
 
+---------------------------
+-- CE-225 Add external agencies
+---------------------------
+
+insert into agency_code (agency_code, short_description, long_description, display_order, active_ind, create_user_id, create_utc_timestamp, update_user_id, update_utc_timestamp, external_agency_ind)
+values ('DFO', 'Fisheries and Oceans Canada', 'Fisheries and Oceans Canada', 20, true, user, now(), user, now(), 'Y'),
+      ('ECCC', 'Environment and Climate Change Canada', 'Environment and Climate Change Canada', 10, true, user, now(), user, now(), 'Y'),
+      ('POL', 'Police', 'Police', 50, true, user, now(), user, now(), 'Y'),
+      ('NROS', 'Natural Resource Officer Service', 'Natural Resource Officer Service', 30, true, user, now(), user, now(), 'Y'),
+      ('OTH', 'Other', 'Other', 40, true, user, now(), user, now(), 'Y')
path: migrations/migrations/R__Create-Test-Data.sql
user: login: Copilot
body: Inserting 'Y' into a boolean column may not be portable; use the literal TRUE instead of string 'Y' for `external_agency_ind`.
```suggestion
values ('DFO', 'Fisheries and Oceans Canada', 'Fisheries and Oceans Canada', 20, true, user, now(), user, now(), TRUE),
      ('ECCC', 'Environment and Climate Change Canada', 'Environment and Climate Change Canada', 10, true, user, now(), user, now(), TRUE),
      ('POL', 'Police', 'Police', 50, true, user, now(), user, now(), TRUE),
      ('NROS', 'Natural Resource Officer Service', 'Natural Resource Officer Service', 30, true, user, now(), user, now(), TRUE),
      ('OTH', 'Other', 'Other', 40, true, user, now(), user, now(), TRUE)
```
created_at: 2025-05-21T21:12:18Z
author_association: NONE
diff_hunk: @@ -0,0 +1,11 @@
+export const ExternalAgencies = {
+  34: (state: any) => {
path: frontend/src/app/store/migrations/migration-35.ts
user: login: Copilot
body: The migration key should match the new persist version (35), not 34, otherwise this migration won't run correctly.
```suggestion
  35: (state: any) => {
```
created_at: 2025-05-21T21:12:19Z
author_association: NONE
diff_hunk: @@ -40,11 +40,12 @@ export const ReferComplaintModal: FC<ReferComplaintModalProps> = ({ close, submi
   const agencyOptions = agencies
     .filter(
       (agency) =>
-        agency.agency !== complaintData?.ownedBy &&
-        // agency's agency_code is in mapping for the complaint type
-        COMPLAINT_TYPE_AGENCY_MAPPING[complaint_type as keyof typeof COMPLAINT_TYPE_AGENCY_MAPPING].some(
-          (agency_code) => agency_code === agency.agency,
-        ),
+        agency.externalAgencyInd ||
+        (agency.agency !== complaintData?.ownedBy &&
+          // agency's agency_code is in mapping for the complaint type
+          COMPLAINT_TYPE_AGENCY_MAPPING[complaint_type as keyof typeof COMPLAINT_TYPE_AGENCY_MAPPING].some(
+            (agency_code) => agency_code === agency.agency,
+          )),
path: frontend/src/app/components/modal/instances/refer-complaint-modal.tsx
user: login: Copilot
body: [nitpick] The filter condition always includes external agencies and may bypass complaint-type mappings; consider adding explicit grouping or comments to clarify intent.
created_at: 2025-05-21T21:12:19Z
author_association: NONE

PR comments:
user: login: nayr974
created_at: 2025-05-21T23:47:16Z
body: /patch_version
author_association: COLLABORATOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-21T23:49:02Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=bcgov_nr-compliance-enforcement&pullRequest=1079) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [8.1% Duplication on New Code](https://sonarcloud.io/component_measures?id=bcgov_nr-compliance-enforcement&pullRequest=1079&metric=new_duplicated_lines_density&view=list) (required ≤ 3%)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=bcgov_nr-compliance-enforcement&pullRequest=1079)


author_association: NONE