Skip to content

Commit

Permalink
[MDS-6205] Permit condition specific reports (#3302)
Browse files Browse the repository at this point in the history
* addressed code quality concern from sonarcloud

update PermitConditions.spec.tsx.snap

update conditions to fix propogation issue

update RenderRadioButtons.spec.js.snap

Add report prop to permit condition form and disable button if report exists

This commit introduces a new optional `report` prop to the `AddReportToPermitConditionForm` component, allowing it to use an existing report if available. Additionally, it modifies the permit condition layer to disable the "Add Report" button when a report already exists for the sub-condition, and ensures permits are refetched after adding a new report.

Rename component and update references

Renamed `AddRequestToPermitConditionForm` to `AddReportToPermitConditionForm` to better reflect its purpose. Also updated all references and added a default export for consistency. Added a snapshot test for the form component.

Add report handling to permit conditions

Enhanced permit conditions to include associated reports. Updated the UI to display report-related information and modified relevant constants, styles, and selectors accordingly.

update interfaces to accomodate permit reports

added permit condition specific report form

updated action buttons on specific permit conditions and updated interface to expand and show action buttons when clicking into a condition.

update backend to accomodate new permit condition specific details for mine reports

migrate mine_report table with new permit specific details

* revert backend changes

* create new mine_report_permit_requirement.py table and updated permit return to include these.

* Rename form and update permit requirement logic

Renamed `AddReporttoPermitConditionForm` to `ReportPermitRequirementForm`. Updated the form to use new `mineReportPermitRequirement` structure. Enhanced `MineReportPermitRequirement` model with soft delete and additional fields like `initial_due_date` and `ministry_recipient`.

* added ts-ignore

* updated text for adding a report requirement

* make view mode RenderDate formatting conditional

* address PR comments and added backend resource test.

* renamed migration and added cols to buttons on permit conditions

* update snap

* update permitAmendment factory to create auth end date as date instead of datetime

* update mine_report_permit_requirement response model to not break swagger

* Enhance permit condition editing permissions

Added feature flag check to control permit condition editing. Updated PermitConditionLayer component to respect user permissions and feature availability. Simplified getMineReportPermitRequirements selector for clarity.

* Remove unused imports and add new test case for MineReportPermit

Removed `ValidationError` and `Raw` imports from `helpers.py` as they were not being used. Added a test case in `test_expected_auth.py` for `MineReportPermitRequirementResource` to ensure proper resource handling and permissions.
  • Loading branch information
matbusby-fw authored Nov 14, 2024
1 parent ad07a9c commit 7670a16
Show file tree
Hide file tree
Showing 33 changed files with 1,687 additions and 178 deletions.
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11.7
3.11.9
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
-- Create the cim_or_cpo_type ENUM type
CREATE TYPE cim_or_cpo_type AS ENUM ('CIM', 'CPO', 'BOTH');

-- Create the ministry_recipient_type ENUM type
CREATE TYPE ministry_recipient_type AS ENUM ('MMO', 'HS', 'RO', 'MOE');

-- Create the new permit_report_requirement table
CREATE TABLE mine_report_permit_requirement
(
mine_report_permit_requirement_id SERIAL PRIMARY KEY,
update_user VARCHAR(255) NOT NULL,
update_timestamp timestamp with time zone DEFAULT now() NOT NULL,
initial_due_date DATE,
due_date_period_months INTEGER NOT NULL,
create_user VARCHAR(255) NOT NULL,
create_timestamp timestamp with time zone DEFAULT now() NOT NULL,
active_ind BOOLEAN DEFAULT true NOT NULL,
deleted_ind BOOLEAN DEFAULT false NOT NULL,
cim_or_cpo cim_or_cpo_type,
ministry_recipient ministry_recipient_type[],
permit_condition_id INTEGER NOT NULL,
permit_amendment_id INTEGER NOT NULL,
CONSTRAINT fk_permit_amendment
FOREIGN KEY (permit_amendment_id)
REFERENCES permit_amendment (permit_amendment_id),
CONSTRAINT fk_permit_condition
FOREIGN KEY (permit_condition_id)
REFERENCES permit_conditions (permit_condition_id)
);

COMMENT ON TABLE mine_report_permit_requirement IS 'Captures the report requirements laid out in permit conditions which can have permit required reports submitted against';


-- Add a new column `mine_report_permit_requirement_id` to the `mine_report` table
ALTER TABLE mine_report
ADD COLUMN mine_report_permit_requirement_id INTEGER,
ADD CONSTRAINT fk_mine_report_permit_requirement
FOREIGN KEY (mine_report_permit_requirement_id)
REFERENCES mine_report_permit_requirement (mine_report_permit_requirement_id);
10 changes: 9 additions & 1 deletion services/common/src/components/forms/RenderDate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import moment from "moment-timezone";
import { DatePicker, Form } from "antd";
import { BaseInputProps, BaseViewInput, getFormItemLabel } from "./BaseInput";
import { FormConsumer } from "./FormWrapper";
import { formatDate } from "@mds/common/redux/utils/helpers";

/**
* @constant RenderDate - Ant Design `DatePicker` component for redux-form.
Expand All @@ -12,6 +13,7 @@ interface DateInputProps extends BaseInputProps {
showTime?: boolean;
yearMode?: boolean;
disabledDate?: (currentDate) => boolean;
formatViewDate?: boolean;
}

const RenderDate: FC<DateInputProps> = ({
Expand All @@ -26,12 +28,18 @@ const RenderDate: FC<DateInputProps> = ({
showTime = false,
yearMode = false,
disabledDate,
formatViewDate = false,
}) => {
return (
<FormConsumer>
{(value) => {
if (!value.isEditMode) {
return <BaseViewInput label={label} value={input?.value} />;
return (
<BaseViewInput
label={label}
value={formatViewDate ? formatDate(input?.value) : input?.value}
/>
);
}
// TS is very angry when showTime & picker are both passed as props
let extraProps: any = {};
Expand Down
3 changes: 3 additions & 0 deletions services/common/src/constants/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ export const MINE_REPORT_COMMENT = (mineGuid, reportGuid, commentGuid) =>
export const MINE_REPORT_STATUS = "/mines/reports/status-codes";
export const MINE_REPORT_CATEGORY = "/mines/reports/category-codes";

export const MINE_REPORT_PERMIT_REQUIREMENT = (mineGuid) =>
`/mines/${mineGuid}/reports/permit-requirements`;

// Compliance Codes
export const COMPLIANCE_CODE_LIST = (params = {}) =>
`/compliance/codes?${queryString.stringify(params)}`;
Expand Down
1 change: 1 addition & 0 deletions services/common/src/constants/forms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum FORM {
// permits
REVOKE_DIGITAL_CREDENTIAL = "REVOKE_DIGITAL_CREDENTIAL",
REQUEST_REPORT = "REQUEST_REPORT",
ADD_REPORT_TO_PERMIT_CONDITION = "ADD_REPORT_TO_PERMIT_CONDITION",
// tailings
ADD_TAILINGS = "ADD_TAILINGS",
// documents
Expand Down
1 change: 1 addition & 0 deletions services/common/src/constants/reducerTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export const GET_MINE_REPORT = "GET_MINE_REPORT";
export const GET_MINE_REPORT_DEFINITION_OPTIONS = "GET_MINE_REPORT_DEFINITION_OPTIONS";
export const GET_MINE_REPORT_STATUS_OPTIONS = "GET_MINE_REPORT_STATUS_OPTIONS";
export const GET_MINE_REPORT_CATEGORY_OPTIONS = "GET_MINE_REPORT_CATEGORY_OPTIONS";
export const CREATE_MINE_REPORT_PERMIT_REQUIREMENT = "CREATE_MINE_REPORT_PERMIT_REQUIREMENT";

// Report Comments
export const GET_MINE_REPORT_COMMENTS = "GET_MINE_REPORT_COMMENTS";
Expand Down
23 changes: 23 additions & 0 deletions services/common/src/constants/strings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -470,3 +470,26 @@ export const WASTE_DISCHARGE_AMENDMENT_AUTHORIZATIONS_URL =
"https://www2.gov.bc.ca/gov/content/environment/waste-management/waste-discharge-authorization/change";
export const WASTE_DISCHARGE_AUTHORIZATION_PROCESS =
"https://www2.gov.bc.ca/gov/content/environment/waste-management/waste-discharge-authorization/process";

export const REPORT_REGULATORY_AUTHORITY_CODES_HASH = {
CPO: "Chief Permitting Officer",
CIM: "Chief Inspector of Mines",
Both: "Both",
NONE: "Not Specified",
};

export const REPORT_MINISTRY_RECIPIENT_HASH = {
MMO: "Major Mines Office",
HS: "Health and Safety",
RO: "Regional Office",
MOE: "Ministry of Environment",
};

export const REPORT_FREQUENCY_HASH = {
"Monthly": 1,
"Quarterly": 4,
"Semi-Annually": 6,
"Annually": 12,
"Bi-Annually": 24,
"Not Specified": 0,
};
1 change: 1 addition & 0 deletions services/common/src/interfaces/permits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export * from "./patchPermitVCLocked.interface";
export * from "./exemptionFeeStatusOption.interface";
export * from "./permitStatusOption.interface";
export * from "./permitTypeOption.interface";
export * from "./mineReportPermitRequirements.interface";
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface IMineReportPermitRequirement {
mine_report_permit_requirement_id: number;
cim_or_cpo: string;
ministry_recipient: string[];
permit_condition_id: number;
due_date_period_months: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
IPermitAmendmentDocument,
INoWImportedApplicationDocument,
IPermitCondition,
VC_CRED_ISSUE_STATES,
VC_CRED_ISSUE_STATES, IMineReportPermitRequirement,
} from "@mds/common/index";

export interface IPermitAmendment {
Expand Down Expand Up @@ -33,4 +33,5 @@ export interface IPermitAmendment {
is_generated_in_core: boolean;
preamble_text: string;
vc_credential_exch_state: VC_CRED_ISSUE_STATES;
mine_report_permit_requirements?: IMineReportPermitRequirement[]
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { IMineReportPermitRequirement } from "@mds/common/interfaces";

export interface IPermitCondition {
permit_condition_id: number;
permit_amendment_id: number;
Expand All @@ -8,5 +10,7 @@ export interface IPermitCondition {
parent_permit_condition_id: number;
sub_conditions: IPermitCondition[];
step: string;
stepPath?: string;
display_order: number;
mineReportPermitRequirement?: IMineReportPermitRequirement;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export interface IMineReport {
submitter_name: string;
submitter_email: string;
mine_report_status_code: MINE_REPORT_SUBMISSION_CODES;
mine_report_permit_requirement_id?: number;
}
9 changes: 9 additions & 0 deletions services/common/src/redux/selectors/permitSelectors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createSelector } from "reselect";
import { getNoticeOfWork } from "@mds/common/redux/selectors/noticeOfWorkSelectors";
import * as permitReducer from "../reducers/permitReducer";
import { IMineReportPermitRequirement } from "@mds/common/interfaces";

const draft = "DFT";

Expand Down Expand Up @@ -84,3 +85,11 @@ export const getPermits = createSelector([getUnformattedPermits], (permits) => {
const formattedPermits = permits.map((permit) => formatPermit(permit));
return formattedPermits;
});

export const getMineReportPermitRequirements = (permitGuid) =>
createSelector(
[getLatestAmendmentByPermitGuid(permitGuid)],
(latestAmendment): IMineReportPermitRequirement[] => {
return (latestAmendment && latestAmendment.mine_report_permit_requirements) ?? [];
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { createAppSlice } from "@mds/common/redux/createAppSlice";
import { hideLoading, showLoading } from "react-redux-loading-bar";
import CustomAxios from "@mds/common/redux/customAxios";
import { ENVIRONMENT, MINE_REPORT_PERMIT_REQUIREMENT } from "@mds/common/constants";
import { IMineReportPermitRequirement } from "@mds/common/interfaces";

const createRequestHeader = REQUEST_HEADER.createRequestHeader;
export const mineReportPermitRequirementReducerType = "mineReportPermitRequirement";

interface IMineReportPermitRequirementState {}

const initialState: IMineReportPermitRequirementState = {};

const mineReportPermitRequirementSlice = createAppSlice({
name: mineReportPermitRequirementReducerType,
initialState,
reducers: (create) => ({
createMineReportPermitRequirement: create.asyncThunk(
async (
payload: {
mineGuid: string;
values: IMineReportPermitRequirement;
},
thunkApi
) => {
const headers = createRequestHeader();
thunkApi.dispatch(showLoading());

const response = await CustomAxios({
errorToastMessage: "default",
}).post(
`${ENVIRONMENT.apiUrl}${MINE_REPORT_PERMIT_REQUIREMENT(payload.mineGuid)}`,
payload.values,
headers
);

thunkApi.dispatch(hideLoading());
return response.data;
}
),
}),
});

export const { createMineReportPermitRequirement } = mineReportPermitRequirementSlice.actions;
export const mineReportPermitRequirementReducer = mineReportPermitRequirementSlice.reducer;
export default mineReportPermitRequirementReducer;
2 changes: 2 additions & 0 deletions services/core-api/app/api/mines/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
from app.api.mines.reports.resources.mine_report_document import (
MineReportDocumentListResource,
)
from app.api.mines.reports.resources.mine_report_permit_requirement import MineReportPermitRequirementResource
from app.api.mines.reports.resources.mine_report_submission_resource import (
ReportSubmissionResource,
)
Expand Down Expand Up @@ -253,6 +254,7 @@
MineReportDocumentListResource,
'/<string:mine_guid>/reports/documents',
)
api.add_resource(MineReportPermitRequirementResource, '/<string:mine_guid>/reports/permit-requirements')

api.add_resource(PermitResource, '/<string:mine_guid>/permits/<string:permit_guid>')
api.add_resource(PermitListResource, '/<string:mine_guid>/permits')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from app.api.mines.permits.permit_amendment.models.permit_amendment_document import PermitAmendmentDocument
from app.api.mines.permits.permit_conditions.models.standard_permit_conditions import StandardPermitConditions
from app.api.mines.permits.permit_conditions.models.permit_conditions import PermitConditions
from app.api.mines.reports.models.mine_report import MineReport
from app.api.mines.reports.models.mine_report_permit_requirement import MineReportPermitRequirement
from app.api.now_applications.models.now_application import NOWApplication
from app.api.now_applications.models.now_application_identity import NOWApplicationIdentity
from app.api.now_applications.models.application_type_code import ApplicationTypeCode
Expand Down Expand Up @@ -112,6 +114,10 @@ def get(self, mine_guid):
results = [permit] if permit else []
else:
results = Mine.find_by_mine_guid(mine_guid).mine_permit

for permit in results:
for permit_amendment in permit.permit_amendments:
permit_amendment.mine_report_permit_requirements = MineReportPermitRequirement.query.filter_by(permit_amendment_id=permit_amendment.permit_amendment_id).all()
return results

@api.doc(params={'permit_guid': 'Permit guid.'})
Expand Down
8 changes: 6 additions & 2 deletions services/core-api/app/api/mines/reports/models/mine_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from app.api.utils.helpers import format_email_datetime_to_string
from app.api.mines.exceptions.mine_exceptions import MineException


class MineReport(SoftDeleteMixin, AuditMixin, Base):
__tablename__ = "mine_report"
mine_report_id = db.Column(db.Integer, primary_key=True, server_default=FetchedValue())
Expand All @@ -40,6 +39,11 @@ class MineReport(SoftDeleteMixin, AuditMixin, Base):
mine_report_definition_guid = association_proxy('mine_report_definition',
'mine_report_definition_guid')
mine_report_definition_report_name = association_proxy('mine_report_definition', 'report_name')
mine_report_permit_requirement_id = db.Column(
db.Integer,
db.ForeignKey('mine_report_permit_requirement.mine_report_permit_requirement_id'),
)
mine_report_permit_requirement = db.relationship('MineReportPermitRequirement', lazy='joined')

submitter_name = db.Column(db.String, nullable=False)
submitter_email = db.Column(db.String, nullable=False)
Expand Down Expand Up @@ -398,4 +402,4 @@ def validate_permit_condition_category(self, key, permit_condition_category_code
if permit_condition_category_code and self.mine_report_definition_id:
raise AssertionError(
'Permit required reports must not specify Code required reports specific data.')
return permit_condition_category_code
return permit_condition_category_code
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from datetime import date
from enum import Enum
from typing import Optional

from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.schema import FetchedValue

from app.api.utils.models_mixins import Base, AuditMixin, SoftDeleteMixin
from app.extensions import db


class CimOrCpo(str, Enum):
CIM = "CIM"
CPO = "CPO"
BOTH = "BOTH"

def __str__(self):
return self.value


class OfficeDestination(str, Enum):
MMO = "MMO"
HS = "HS"
RO = "RO"
MOE = "MOE"

def __str__(self):
return self.value


class MineReportPermitRequirement(SoftDeleteMixin, Base, AuditMixin):
__tablename__ = "mine_report_permit_requirement"

mine_report_permit_requirement_id: int = db.Column(db.Integer, primary_key=True, server_default=FetchedValue())
due_date_period_months: int = db.Column(db.Integer, nullable=False)
initial_due_date: Optional[date] = db.Column(db.Date, nullable=True)
active_ind: bool = db.Column(db.Boolean, nullable=False, server_default=FetchedValue())
cim_or_cpo: Optional[CimOrCpo] = db.Column(db.Enum(CimOrCpo, name='cim_or_cpo_type'), nullable=True)
ministry_recipient: Optional[list[OfficeDestination]] = db.Column(
ARRAY(db.Enum(OfficeDestination, name='ministry_recipient_type')), nullable=True)
permit_condition_id: int = db.Column(db.Integer, db.ForeignKey('permit_conditions.permit_condition_id'))
permit_amendment_id: int = db.Column(db.Integer, db.ForeignKey('permit_amendment.permit_amendment_id'))

def __repr__(self):
return '<MineReportPermitRequirement %r>' % self.permit_report_requirement_id

@classmethod
def find_by_permit_report_requirement_id(cls, _id) -> "MineReportPermitRequirement":
try:
return cls.query.filter_by(permit_report_requirement_id=_id).first()
except ValueError:
return None

@classmethod
def find_by_report_name(cls, _report_name) -> "MineReportPermitRequirement":
try:
return cls.query.filter_by(report_name=_report_name).all()
except ValueError:
return None

@classmethod
def get_all(cls) -> list["MineReportPermitRequirement"]:
try:
return cls.query.all()
except ValueError:
return None

@classmethod
def create(cls,
due_date_period_months: int,
initial_due_date: date,
cim_or_cpo: Optional[CimOrCpo],
ministry_recipient: Optional[list[OfficeDestination]],
permit_condition_id: int,
permit_amendment_id: int) -> "MineReportPermitRequirement":

mine_report_permit_requirement = cls(
due_date_period_months=due_date_period_months,
initial_due_date=initial_due_date,
cim_or_cpo=cim_or_cpo,
ministry_recipient=ministry_recipient,
permit_condition_id=permit_condition_id,
permit_amendment_id=permit_amendment_id
)

mine_report_permit_requirement.save(commit=True)
return mine_report_permit_requirement
Loading

0 comments on commit 7670a16

Please sign in to comment.