From a61f621e71b0c69338bfb7e4620985176179c2fd Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Thu, 17 Oct 2024 17:18:36 -0700 Subject: [PATCH 01/11] chore:Added Api to get report Type and modified fields --- bc_obps/reporting/api/__init__.py | 1 + bc_obps/reporting/api/reports.py | 16 +- .../0021_reportversion_report_type.py | 18 + bc_obps/reporting/models/report_version.py | 5 + bc_obps/reporting/schema/report_version.py | 13 + bc_obps/service/report_service.py | 4 + .../components/operations/OperationReview.tsx | 117 +++-- .../operations/OperationReviewFormData.tsx | 4 + .../reporting/src/app/utils/getReportType.ts | 9 + .../src/data/jsonSchema/operations.ts | 19 +- erd_diagrams/erd_reporting.md | 426 ++++++++++++++++++ 11 files changed, 600 insertions(+), 32 deletions(-) create mode 100644 bc_obps/reporting/migrations/0021_reportversion_report_type.py create mode 100644 bc_obps/reporting/schema/report_version.py create mode 100644 bciers/apps/reporting/src/app/utils/getReportType.ts create mode 100644 erd_diagrams/erd_reporting.md diff --git a/bc_obps/reporting/api/__init__.py b/bc_obps/reporting/api/__init__.py index c7bb2e989b..9603583f0f 100644 --- a/bc_obps/reporting/api/__init__.py +++ b/bc_obps/reporting/api/__init__.py @@ -10,6 +10,7 @@ from .report_person_responsible import get_report_person_responsible_by_version_id from .report_person_responsible import save_report_contact from .report_additional_data import get_registration_purpose_by_version_id +from .reports import get_report_type_by_version from .gas_type import get_gas_type from .emission_category import get_emission_category from .production_data import save_production_data diff --git a/bc_obps/reporting/api/reports.py b/bc_obps/reporting/api/reports.py index 89867db638..5a04e2a54b 100644 --- a/bc_obps/reporting/api/reports.py +++ b/bc_obps/reporting/api/reports.py @@ -12,7 +12,8 @@ from reporting.schema.report_operation import ReportOperationOut, ReportOperationIn from reporting.schema.reporting_year import ReportingYearOut from .router import router -from ..models import ReportingYear +from ..models import ReportingYear, ReportVersion +from ..schema.report_version import ReportingVersionOut @router.post( @@ -71,3 +72,16 @@ def save_report( @handle_http_errors() def get_reporting_year(request: HttpRequest) -> Tuple[Literal[200], ReportingYear]: return 200, ReportingYearService.get_current_reporting_year() + + +@router.get( + "/report-version/{version_id}/report-type", + response={200: ReportingVersionOut, custom_codes_4xx: Message}, + tags=EMISSIONS_REPORT_TAGS, + description="Retrieve the report type for a specific reporting version, including the reporting year and due date.", + # auth=authorize("all_roles"), +) +@handle_http_errors() +def get_report_type_by_version(request: HttpRequest, version_id: int) -> tuple[Literal[200], ReportVersion]: + report_type = ReportService.get_report_type_by_version_id(version_id) + return 200, report_type diff --git a/bc_obps/reporting/migrations/0021_reportversion_report_type.py b/bc_obps/reporting/migrations/0021_reportversion_report_type.py new file mode 100644 index 0000000000..b5757bdcf0 --- /dev/null +++ b/bc_obps/reporting/migrations/0021_reportversion_report_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.9 on 2024-10-17 21:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('reporting', '0020_report_person_responsible'), + ] + + operations = [ + migrations.AddField( + model_name='reportversion', + name='report_type', + field=models.CharField(db_comment='Report type', default='Annual Report', max_length=1000), + ), + ] diff --git a/bc_obps/reporting/models/report_version.py b/bc_obps/reporting/models/report_version.py index 7b9fda2b52..9f1d5a9348 100644 --- a/bc_obps/reporting/models/report_version.py +++ b/bc_obps/reporting/models/report_version.py @@ -15,6 +15,11 @@ class ReportVersion(TimeStampedModel): db_comment="True if this version is the latest submitted one", default=False, ) + report_type = models.CharField( + max_length=1000, + db_comment="Report type", + default="Annual Report", + ) class ReportVersionStatus(models.TextChoices): Draft = 'draft' diff --git a/bc_obps/reporting/schema/report_version.py b/bc_obps/reporting/schema/report_version.py new file mode 100644 index 0000000000..405f1dcc48 --- /dev/null +++ b/bc_obps/reporting/schema/report_version.py @@ -0,0 +1,13 @@ +from ninja import ModelSchema + +from reporting.models import ReportVersion + + +class ReportingVersionOut(ModelSchema): + """ + Schema for the get reporting year endpoint request output + """ + + class Meta: + model = ReportVersion + fields = ['report_type'] diff --git a/bc_obps/service/report_service.py b/bc_obps/service/report_service.py index c9e893a82e..e10f1eddfe 100644 --- a/bc_obps/service/report_service.py +++ b/bc_obps/service/report_service.py @@ -102,3 +102,7 @@ def save_report_operation(cls, report_version_id: int, data: ReportOperationIn) report_operation.save() return report_operation + + @staticmethod + def get_report_type_by_version_id(version_id: int) -> ReportVersion: + return ReportVersion.objects.get(id=version_id) diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index 98e2ad99cb..946b6c37f5 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -17,6 +17,9 @@ import serializeSearchParams from "@bciers/utils/src/serializeSearchParams"; interface Props { formData: any; version_id: number; + reportType: { + report_type: string; + }; reportingYear: { reporting_year: number; report_due_date: string; @@ -45,12 +48,14 @@ const taskListElements: TaskListElement[] = [ export default function OperationReview({ formData, version_id, + reportType, reportingYear, allActivities, allRegulatedProducts, }: Props) { const router = useRouter(); const [schema, setSchema] = useState(operationReviewSchema); + const [uiSchema, setUiSchema] = useState(operationReviewUiSchema); const [formDataState, setFormDataState] = useState(formData); const queryString = serializeSearchParams(useSearchParams()); const saveAndContinueUrl = `/reports/${version_id}/person-responsible${queryString}`; @@ -60,6 +65,7 @@ export default function OperationReview({ "MMM DD YYYY", ); + // Function to handle form data submission const submitHandler = async ( data: { formData?: any }, reportVersionId: number, @@ -69,22 +75,30 @@ export default function OperationReview({ const formDataObject = safeJsonParse(JSON.stringify(data.formData)); + // Prepare the data based on the operation report type const preparedData = { ...formDataObject, - activities: formDataObject.activities?.map((activityId: any) => { - const activity = allActivities.find((a) => a.id === activityId); - if (!activity) - throw new Error(`Activity with ID ${activityId} not found`); - return activity.name; - }), - regulated_products: formDataObject.regulated_products?.map( - (productId: any) => { - const product = allRegulatedProducts.find((p) => p.id === productId); - if (!product) - throw new Error(`Product with ID ${productId} not found`); - return product.name; - }, - ), + // Check the report type and set activities and regulated_products to empty arrays if it's a simple report + activities: + formDataState.operation_report_type === "Simple Report" + ? [] + : formDataObject.activities?.map((activityId: any) => { + const activity = allActivities.find((a) => a.id === activityId); + if (!activity) + throw new Error(`Activity with ID ${activityId} not found`); + return activity.name; + }), + regulated_products: + formDataState.operation_report_type === "Simple Report" + ? [] + : formDataObject.regulated_products?.map((productId: any) => { + const product = allRegulatedProducts.find( + (p) => p.id === productId, + ); + if (!product) + throw new Error(`Product with ID ${productId} not found`); + return product.name; + }), }; const response = await actionHandler(endpoint, method, endpoint, { @@ -96,18 +110,43 @@ export default function OperationReview({ } }; + // Function to handle changes in the form data + const onChangeHandler = (data: { formData: any }) => { + const updatedData = { + ...data.formData, + // Modify the structure of form data here as needed + }; + + setFormDataState(updatedData); // Update the state with modified data + }; + useEffect(() => { if (!formData || !allActivities || !allRegulatedProducts) { return; } - const activities = formData.activities || []; - const products = formData.regulated_products || []; + const updatedFormData = { + ...formData, + operation_report_type: reportType?.report_type || "Annual Report", + activities: formData.activities || [], + regulated_products: formData.regulated_products || [], + }; + + setFormDataState(updatedFormData); + }, [formData, reportType, allActivities, allRegulatedProducts]); + useEffect(() => { setSchema((prevSchema) => ({ ...prevSchema, properties: { ...prevSchema.properties, + operation_report_type: { + type: "string", + title: "Please select what type of report you are filling", + enum: ["Annual Report", "Simple Report"], + default: formDataState?.operation_report_type || "Annual Report", + }, + // Conditionally render fields based on report type activities: { type: "array", title: "Reporting activities", @@ -117,6 +156,10 @@ export default function OperationReview({ enumNames: allActivities.map((activity) => activity.name), }, uniqueItems: true, + // Only show this field if report type is not simple + "ui:options": { + hidden: formDataState.operation_report_type === "Simple Report", + }, }, regulated_products: { type: "array", @@ -127,16 +170,20 @@ export default function OperationReview({ enumNames: allRegulatedProducts.map((product) => product.name), }, uniqueItems: true, + // Only show this field if report type is not simple + "ui:options": { + hidden: formDataState.operation_report_type === "Simple Report", + }, }, operation_representative_name: { type: "string", title: "Operation representative", - enum: [formData.operation_representative_name || ""], + enum: [formDataState.operation_representative_name || ""], }, operation_type: { type: "string", title: "Operation type", - enum: [formData.operation_type || ""], + enum: [formDataState.operation_type || ""], }, date_info: { type: "object", @@ -145,18 +192,35 @@ export default function OperationReview({ }, }, })); + }, [allActivities, allRegulatedProducts, formDataState, reportingWindowEnd]); - const updatedFormData = { - ...formData, - activities, - regulated_products: products, + useEffect(() => { + const updateUiSchema = () => { + const helperText = + formDataState?.operation_report_type === "Simple Report" ? ( + + Regulated or Reporting Operations should file a Simple Report if + their emissions have dropped below 10,000 tCO2e. They will continue + to report using the Simple Report form until they stop Schedule A + activities or stay under 10ktCO2e for three years. This does not + apply to Opt-ins. + + ) : null; + + setUiSchema({ + ...operationReviewUiSchema, + operation_report_type: { + "ui:widget": "select", // Set the widget type + "ui:help": helperText, + }, + }); }; - setFormDataState(updatedFormData); - }, [allActivities, allRegulatedProducts, formData, reportingWindowEnd]); + // Call the function to update the UI schema + updateUiSchema(); + }, [formDataState]); // Ensure the effect runs when formDataState changes if (!formData) { - //we need to render another component which we would display if no version Id exist or we want to show an error return
No version ID found(TBD)
; } @@ -171,11 +235,12 @@ export default function OperationReview({ ]} taskListElements={taskListElements} schema={schema} - uiSchema={operationReviewUiSchema} + uiSchema={uiSchema} formData={formDataState} baseUrl={baseUrl} cancelUrl={cancelUrl} onSubmit={(data) => submitHandler(data, version_id)} + onChange={onChangeHandler} // Pass the onChange handler here /> ); } diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx index be0dee5f44..b464c8fc00 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx @@ -2,6 +2,8 @@ import OperationReview from "./OperationReview"; import { getReportingYear } from "@reporting/src/app/utils/getReportingYear"; import { getReportingOperation } from "@reporting/src/app/utils/getReportingOperation"; import { getAllActivities } from "@reporting/src/app/utils/getAllReportingActivities"; +import { getAllRegulatedProducts } from "@reporting/src/app/utils/getAllRegulatedProducts"; +import { getReportType } from "@reporting/src/app/utils/getReportType"; import { getRegulatedProducts } from "@bciers/actions/api"; export default async function OperationReviewFormData({ @@ -13,10 +15,12 @@ export default async function OperationReviewFormData({ const allActivities = await getAllActivities(); const allRegulatedProducts = await getRegulatedProducts(); const reportingYear = await getReportingYear(); + const reportType = await getReportType(version_id); return ( Date: Mon, 21 Oct 2024 11:49:18 -0700 Subject: [PATCH 02/11] chore:fixed helper text --- .../app/components/operations/OperationReview.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index 946b6c37f5..b81e09d122 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -199,11 +199,15 @@ export default function OperationReview({ const helperText = formDataState?.operation_report_type === "Simple Report" ? ( - Regulated or Reporting Operations should file a Simple Report if - their emissions have dropped below 10,000 tCO2e. They will continue - to report using the Simple Report form until they stop Schedule A - activities or stay under 10ktCO2e for three years. This does not - apply to Opt-ins. + Simple reports are submitted by operations that previously emitted + greater than or equal to 10 000 tCO2e of attributable emissions in a + reporting period, but now emit under 10 000 tCO2e of attributable + emissions in a reporting period and have an obligation to continue + reporting emissions for three consecutive reporting periods. This + report type is not applicable for opted-in operations. +

+ If you are uncertain about which report type your operation should + complete, please contact GHGRegulator@gov.bc.ca.
) : null; From 7818ca62d6d7ab4494772f6a04470878772407f0 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Mon, 21 Oct 2024 18:05:34 -0700 Subject: [PATCH 03/11] chore:Added tests and fixed form --- bc_obps/reporting/api/reports.py | 2 +- ...e.py => 0022_reportversion_report_type.py} | 2 +- bc_obps/reporting/schema/report_operation.py | 7 +- bc_obps/service/report_service.py | 5 +- .../components/operations/OperationReview.tsx | 180 +++++++----------- .../operations/OperationReviewFormData.tsx | 13 +- .../src/data/jsonSchema/operations.ts | 116 +++++++++-- .../operations/OperationReview.test.tsx | 97 ++++++++-- 8 files changed, 273 insertions(+), 149 deletions(-) rename bc_obps/reporting/migrations/{0021_reportversion_report_type.py => 0022_reportversion_report_type.py} (87%) diff --git a/bc_obps/reporting/api/reports.py b/bc_obps/reporting/api/reports.py index 5a04e2a54b..df7bda874e 100644 --- a/bc_obps/reporting/api/reports.py +++ b/bc_obps/reporting/api/reports.py @@ -51,7 +51,7 @@ def get_report_operation_by_version_id( response={201: ReportOperationOut, custom_codes_4xx: Message}, tags=EMISSIONS_REPORT_TAGS, description="""Updates given report operation with fields: Operator Legal Name, Operator Trade Name, Operation Name, Operation Type, - Operation BC GHG ID, BC OBPS Regulated Operation ID, Operation Representative Name, and Activities.""", + Operation BC GHG ID, BC OBPS Regulated Operation ID, Operation Representative Name, aznd Activities.""", auth=authorize("approved_industry_user"), ) @handle_http_errors() diff --git a/bc_obps/reporting/migrations/0021_reportversion_report_type.py b/bc_obps/reporting/migrations/0022_reportversion_report_type.py similarity index 87% rename from bc_obps/reporting/migrations/0021_reportversion_report_type.py rename to bc_obps/reporting/migrations/0022_reportversion_report_type.py index b5757bdcf0..35b0da0471 100644 --- a/bc_obps/reporting/migrations/0021_reportversion_report_type.py +++ b/bc_obps/reporting/migrations/0022_reportversion_report_type.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('reporting', '0020_report_person_responsible'), + ('reporting', '0021_report_additional_data'), ] operations = [ diff --git a/bc_obps/reporting/schema/report_operation.py b/bc_obps/reporting/schema/report_operation.py index c8898f005c..c650be3a01 100644 --- a/bc_obps/reporting/schema/report_operation.py +++ b/bc_obps/reporting/schema/report_operation.py @@ -1,4 +1,4 @@ -from ninja import ModelSchema +from ninja import ModelSchema, Schema from reporting.models.report_operation import ReportOperation from pydantic import alias_generators from typing import List @@ -33,7 +33,7 @@ class Meta: ] -class ReportOperationIn(ModelSchema): +class ReportOperationIn(Schema): """ Schema for the save report operation endpoint request input """ @@ -47,10 +47,10 @@ class ReportOperationIn(ModelSchema): activities: List[str] regulated_products: List[str] operation_representative_name: str + operation_report_type: str class Meta: alias_generator = to_snake - model = ReportOperation fields = [ 'operator_legal_name', 'operator_trade_name', @@ -61,4 +61,5 @@ class Meta: 'activities', 'regulated_products', 'operation_representative_name', + 'operation_report_type', ] diff --git a/bc_obps/service/report_service.py b/bc_obps/service/report_service.py index e10f1eddfe..fc48a2fc13 100644 --- a/bc_obps/service/report_service.py +++ b/bc_obps/service/report_service.py @@ -98,9 +98,12 @@ def save_report_operation(cls, report_version_id: int, data: ReportOperationIn) report_operation.activities.set(activities) report_operation.regulated_products.set(regulated_products) - # Save the updated report operation report_operation.save() + report_version = ReportVersion.objects.get(id=report_version_id) + report_version.report_type = data.operation_report_type + report_version.save() + return report_operation @staticmethod diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index b81e09d122..c1ffab26b3 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -7,6 +7,7 @@ import { useRouter, useSearchParams } from "next/navigation"; import { operationReviewSchema, operationReviewUiSchema, + updateSchema, } from "@reporting/src/data/jsonSchema/operations"; import { TaskListElement } from "@bciers/components/navigation/reportingTaskList/types"; import safeJsonParse from "@bciers/utils/src/safeJsonParse"; @@ -27,6 +28,7 @@ interface Props { }; allActivities: { id: number; name: string }[]; allRegulatedProducts: { id: number; name: string }[]; + registrationPurpose: string; } const baseUrl = "/reports"; @@ -52,6 +54,7 @@ export default function OperationReview({ reportingYear, allActivities, allRegulatedProducts, + registrationPurpose, }: Props) { const router = useRouter(); const [schema, setSchema] = useState(operationReviewSchema); @@ -65,20 +68,10 @@ export default function OperationReview({ "MMM DD YYYY", ); - // Function to handle form data submission - const submitHandler = async ( - data: { formData?: any }, - reportVersionId: number, - ) => { - const method = "POST"; - const endpoint = `reporting/report-version/${reportVersionId}/report-operation`; - - const formDataObject = safeJsonParse(JSON.stringify(data.formData)); - - // Prepare the data based on the operation report type - const preparedData = { + // Function to prepare the form data for submission + const prepareFormData = (formDataObject: any) => { + return { ...formDataObject, - // Check the report type and set activities and regulated_products to empty arrays if it's a simple report activities: formDataState.operation_report_type === "Simple Report" ? [] @@ -100,108 +93,36 @@ export default function OperationReview({ return product.name; }), }; - - const response = await actionHandler(endpoint, method, endpoint, { - body: JSON.stringify(preparedData), - }); - - if (response) { - router.push(saveAndContinueUrl); // Navigate on success - } - }; - - // Function to handle changes in the form data - const onChangeHandler = (data: { formData: any }) => { - const updatedData = { - ...data.formData, - // Modify the structure of form data here as needed - }; - - setFormDataState(updatedData); // Update the state with modified data }; + // Combined useEffect for initialization and schema updates useEffect(() => { - if (!formData || !allActivities || !allRegulatedProducts) { - return; - } - - const updatedFormData = { - ...formData, - operation_report_type: reportType?.report_type || "Annual Report", - activities: formData.activities || [], - regulated_products: formData.regulated_products || [], - }; - - setFormDataState(updatedFormData); - }, [formData, reportType, allActivities, allRegulatedProducts]); + if (formData && allActivities && allRegulatedProducts) { + const updatedFormData = { + ...formData, + operation_report_type: reportType?.report_type || "Annual Report", + activities: formData.activities || [], + regulated_products: formData.regulated_products || [], + }; + setFormDataState(updatedFormData); + + setSchema((prevSchema) => + updateSchema( + prevSchema, + updatedFormData, + registrationPurpose, + reportingWindowEnd, + allActivities, + allRegulatedProducts, + ), + ); - useEffect(() => { - setSchema((prevSchema) => ({ - ...prevSchema, - properties: { - ...prevSchema.properties, - operation_report_type: { - type: "string", - title: "Please select what type of report you are filling", - enum: ["Annual Report", "Simple Report"], - default: formDataState?.operation_report_type || "Annual Report", - }, - // Conditionally render fields based on report type - activities: { - type: "array", - title: "Reporting activities", - items: { - type: "number", - enum: allActivities.map((activity) => activity.id), - enumNames: allActivities.map((activity) => activity.name), - }, - uniqueItems: true, - // Only show this field if report type is not simple - "ui:options": { - hidden: formDataState.operation_report_type === "Simple Report", - }, - }, - regulated_products: { - type: "array", - title: "Regulated products", - items: { - type: "number", - enum: allRegulatedProducts.map((product) => product.id), - enumNames: allRegulatedProducts.map((product) => product.name), - }, - uniqueItems: true, - // Only show this field if report type is not simple - "ui:options": { - hidden: formDataState.operation_report_type === "Simple Report", - }, - }, - operation_representative_name: { - type: "string", - title: "Operation representative", - enum: [formDataState.operation_representative_name || ""], - }, - operation_type: { - type: "string", - title: "Operation type", - enum: [formDataState.operation_type || ""], - }, - date_info: { - type: "object", - readOnly: true, - title: `Please ensure this information was accurate for ${reportingWindowEnd}`, - }, - }, - })); - }, [allActivities, allRegulatedProducts, formDataState, reportingWindowEnd]); - - useEffect(() => { - const updateUiSchema = () => { const helperText = - formDataState?.operation_report_type === "Simple Report" ? ( + updatedFormData.operation_report_type === "Simple Report" ? ( Simple reports are submitted by operations that previously emitted - greater than or equal to 10 000 tCO2e of attributable emissions in a - reporting period, but now emit under 10 000 tCO2e of attributable + greater than or equal to 10,000 tCO2e of attributable emissions in a + reporting period, but now emit under 10,000 tCO2e of attributable emissions in a reporting period and have an obligation to continue reporting emissions for three consecutive reporting periods. This report type is not applicable for opted-in operations. @@ -214,18 +135,47 @@ export default function OperationReview({ setUiSchema({ ...operationReviewUiSchema, operation_report_type: { - "ui:widget": "select", // Set the widget type + "ui:widget": "select", "ui:help": helperText, }, }); - }; + } + }, [ + formData, + reportType, + allActivities, + allRegulatedProducts, + registrationPurpose, + reportingWindowEnd, + ]); + + // Handle form submission + const submitHandler = async ( + data: { formData?: any }, + reportVersionId: number, + ) => { + const method = "POST"; + const endpoint = `reporting/report-version/${reportVersionId}/report-operation`; + + const formDataObject = safeJsonParse(JSON.stringify(data.formData)); + const preparedData = prepareFormData(formDataObject); + + const response = await actionHandler(endpoint, method, endpoint, { + body: JSON.stringify(preparedData), + }); - // Call the function to update the UI schema - updateUiSchema(); - }, [formDataState]); // Ensure the effect runs when formDataState changes + if (response) { + router.push(saveAndContinueUrl); // Navigate on success + } + }; + + // Handle form data changes + const onChangeHandler = (data: { formData: any }) => { + setFormDataState(data.formData); + }; if (!formData) { - return
No version ID found(TBD)
; + return
No version ID found (TBD)
; } return ( @@ -244,7 +194,7 @@ export default function OperationReview({ baseUrl={baseUrl} cancelUrl={cancelUrl} onSubmit={(data) => submitHandler(data, version_id)} - onChange={onChangeHandler} // Pass the onChange handler here + onChange={onChangeHandler} /> ); } diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx index b464c8fc00..66a4f53877 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx @@ -4,7 +4,7 @@ import { getReportingOperation } from "@reporting/src/app/utils/getReportingOper import { getAllActivities } from "@reporting/src/app/utils/getAllReportingActivities"; import { getAllRegulatedProducts } from "@reporting/src/app/utils/getAllRegulatedProducts"; import { getReportType } from "@reporting/src/app/utils/getReportType"; -import { getRegulatedProducts } from "@bciers/actions/api"; +import { getRegistrationPurpose } from "@reporting/src/app/utils/getRegistrationPurpose"; export default async function OperationReviewFormData({ version_id, @@ -13,9 +13,17 @@ export default async function OperationReviewFormData({ }) { const reportOperation = await getReportingOperation(version_id); const allActivities = await getAllActivities(); - const allRegulatedProducts = await getRegulatedProducts(); + const allRegulatedProducts = await getAllRegulatedProducts(); const reportingYear = await getReportingYear(); const reportType = await getReportType(version_id); + const registrationPurpose = await getRegistrationPurpose(version_id); + + const registrationPurposeString = Array.isArray( + registrationPurpose.registration_purposes, + ) + ? registrationPurpose.registration_purposes.join(", ") + : registrationPurpose.registration_purposes || ""; + return ( ); } diff --git a/bciers/apps/reporting/src/data/jsonSchema/operations.ts b/bciers/apps/reporting/src/data/jsonSchema/operations.ts index 89a3717715..0a1c58576e 100644 --- a/bciers/apps/reporting/src/data/jsonSchema/operations.ts +++ b/bciers/apps/reporting/src/data/jsonSchema/operations.ts @@ -25,6 +25,7 @@ export const operationReviewSchema: RJSFSchema = { enum: ["Annual report", "Simple Report"], default: "Annual report", }, + operation_representative_name: { type: "string", title: "Operation representative", @@ -42,19 +43,30 @@ export const operationReviewSchema: RJSFSchema = { type: "string", title: "Operation type", }, - operation_bcghgid: { type: "string", title: "BCGHG ID" }, - bc_obps_regulated_operation_id: { type: "string", title: "BORO ID" }, - registration_pupose: { + registration_purpose: { type: "string", title: "Registration Purpose", }, - activities: { - type: "array", - title: "Reporting activities", - }, - regulated_products: { - type: "array", - title: "Regulated products", + operation_bcghgid: { type: "string", title: "BCGHG ID" }, + bc_obps_regulated_operation_id: { type: "string", title: "BORO ID" }, + }, + dependencies: { + operation_report_type: { + oneOf: [ + { + enum: ["Annual Report"], + properties: { + activities: { + type: "array", + title: "Reporting activities", + }, + regulated_products: { + type: "array", + title: "Regulated products", + }, + }, + }, + ], }, }, }; @@ -87,9 +99,9 @@ export const operationReviewUiSchema = { }, }, operation_type: { - "ui:widget": "select", "ui:options": commonUiOptions, "ui:placeholder": "Operation type", + "ui:disabled": true, }, operation_report_type: { "ui:widget": "select", @@ -97,6 +109,10 @@ export const operationReviewUiSchema = { "ui:placeholder": "Report type", "ui:disabled": true, }, + registration_purpose: { + "ui:placeholder": "Registration Purpose", + "ui:disabled": true, + }, operation_bcghgid: { "ui:options": commonUiOptions, "ui:placeholder": "BCGHG ID", @@ -108,9 +124,6 @@ export const operationReviewUiSchema = { "ui:disabled": true, }, - registration_pupose: { - "ui:placeholder": "Registration Purpose", - }, activities: { "ui:widget": "MultiSelectWidget", "ui:options": { @@ -153,3 +166,78 @@ export const operationReviewUiSchema = { submitText: "Save", }, }; + +export const updateSchema = ( + prevSchema: RJSFSchema, + formDataState: any, + registrationPurpose: string, + reportingWindowEnd: string, + allActivities: any[], + allRegulatedProducts: any[], +) => { + return { + ...prevSchema, + properties: { + ...prevSchema.properties, + operation_report_type: { + type: "string", + title: "Please select what type of report you are filling", + enum: ["Annual Report", "Simple Report"], + default: formDataState?.operation_report_type || "Annual Report", + }, + operation_representative_name: { + type: "string", + title: "Operation representative", + enum: [formDataState.operation_representative_name || ""], + }, + operation_type: { + type: "string", + title: "Operation type", + default: [formDataState.operation_type || ""], + }, + registration_purpose: { + type: "string", + title: "Registration Purpose", + default: registrationPurpose || "", + }, + date_info: { + type: "object", + readOnly: true, + title: `Please ensure this information was accurate for ${reportingWindowEnd}`, + }, + }, + dependencies: { + operation_report_type: { + oneOf: [ + { + properties: { + operation_report_type: { + enum: ["Annual Report"], + }, + activities: { + type: "array", + title: "Reporting activities", + items: { + type: "number", + enum: allActivities.map((activity) => activity.id), + enumNames: allActivities.map((activity) => activity.name), + }, + }, + regulated_products: { + type: "array", + title: "Regulated products", + items: { + type: "number", + enum: allRegulatedProducts.map((product) => product.id), + enumNames: allRegulatedProducts.map( + (product) => product.name, + ), + }, + }, + }, + }, + ], + }, + }, + }; +}; diff --git a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx index 7bffe49e1f..c1fef2e12d 100644 --- a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx +++ b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, waitFor } from "@testing-library/react"; +import { render, screen, waitFor, fireEvent } from "@testing-library/react"; import { describe, expect, it, beforeEach, vi } from "vitest"; import { useRouter } from "next/navigation"; import { actionHandler } from "@bciers/actions"; @@ -8,22 +8,19 @@ vi.mock("@bciers/actions", () => ({ actionHandler: vi.fn(), })); -vi.mock("next/navigation", async (importOriginal) => { - const actual = (await importOriginal()) as Record; - return { - ...actual, - useRouter: vi.fn(), - useSearchParams: vi.fn(() => new URLSearchParams()), - }; -}); +vi.mock("next/navigation", () => ({ + useRouter: vi.fn(), +})); const mockUseRouter = useRouter as vi.MockedFunction; -const mockActionHandler = vi.fn(); +const mockActionHandler = actionHandler as vi.MockedFunction< + typeof actionHandler +>; describe("OperationReview Component", () => { beforeEach(() => { mockUseRouter.mockReturnValue({ push: vi.fn() }); - (actionHandler as unknown as typeof mockActionHandler) = mockActionHandler; + mockActionHandler.mockResolvedValue(true); // Mock the action handler to always resolve successfully }); it("renders the form correctly after loading", async () => { @@ -36,6 +33,7 @@ describe("OperationReview Component", () => { operation_type: "Test Operation", }} version_id={1} + reportType={{ report_type: "Annual Report" }} reportingYear={{ reporting_year: 2024, report_due_date: "2024-12-31", @@ -43,12 +41,87 @@ describe("OperationReview Component", () => { }} allActivities={[{ id: 1, name: "Activity 1" }]} allRegulatedProducts={[{ id: 1, name: "Product 1" }]} + registrationPurpose="Test Purpose" />, ); + await waitFor(() => { - expect(screen.getByText("Operation Information")).toBeInTheDocument(); + expect( + screen.getByText("Review operation information"), + ).toBeInTheDocument(); }); + expect(screen.getByText(/Cancel/i)).toBeInTheDocument(); expect(screen.getByText(/Save And Continue/i)).toBeInTheDocument(); }); + + it("submits the form and navigates to the next page", async () => { + const { push } = useRouter(); + + render( + , + ); + + // Simulate form submission + fireEvent.click(screen.getByText(/Save And Continue/i)); + + await waitFor(() => { + expect(mockActionHandler).toHaveBeenCalledWith( + expect.stringContaining("reporting/report-version/1/report-operation"), + expect.any(String), + expect.any(String), + expect.objectContaining({ + body: expect.any(String), + }), + ); + expect(push).toHaveBeenCalledWith("/reports/1/person-responsible"); + }); + }); + + it("shows helper text for Simple Report", async () => { + render( + , + ); + + await waitFor(() => { + expect( + screen.getByText(/Simple reports are submitted by operations/i), + ).toBeInTheDocument(); + }); + }); }); From e14befadc3d140d655bcbda52de71ee27fa5097f Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Tue, 22 Oct 2024 16:30:53 -0700 Subject: [PATCH 04/11] chore:fixed label and tests --- .../tests/api/test_report_version_endpoint.py | 7 +++++++ bc_obps/reporting/tests/utils/bakers.py | 9 ++++++++- bc_obps/service/report_service.py | 1 + .../src/app/components/operations/OperationReview.tsx | 3 --- .../apps/reporting/src/data/jsonSchema/operations.ts | 10 ++++++---- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/bc_obps/reporting/tests/api/test_report_version_endpoint.py b/bc_obps/reporting/tests/api/test_report_version_endpoint.py index d3fd61f22e..48d7ea5563 100644 --- a/bc_obps/reporting/tests/api/test_report_version_endpoint.py +++ b/bc_obps/reporting/tests/api/test_report_version_endpoint.py @@ -35,8 +35,10 @@ def test_authorized_users_can_post_updates_to_report_version(self): "activities": [], "regulated_products": [], "operation_representative_name": "new operation representative name", + "operation_report_type": "LFO", # This belongs to ReportVersion, not ReportOperation } + # Assert changes for ReportOperation fields assert report_version.report_operation.operator_legal_name != data['operator_legal_name'] assert report_version.report_operation.operator_trade_name != data['operator_trade_name'] assert report_version.report_operation.operation_name != data['operation_name'] @@ -44,6 +46,9 @@ def test_authorized_users_can_post_updates_to_report_version(self): assert report_version.report_operation.bc_obps_regulated_operation_id != data['bc_obps_regulated_operation_id'] assert report_version.report_operation.operation_representative_name != data['operation_representative_name'] + # Assert change for ReportVersion field + assert report_version.report_type != data['operation_report_type'] # Correctly assert for ReportVersion + response = TestUtils.mock_post_with_auth_role( self, 'industry_user', self.content_type, data, endpoint_under_test ) @@ -51,9 +56,11 @@ def test_authorized_users_can_post_updates_to_report_version(self): assert response.status_code == 201 response_json = response.json() + # Assert response for ReportOperation fields assert response_json['operator_legal_name'] == data['operator_legal_name'] assert response_json['operator_trade_name'] == data['operator_trade_name'] assert response_json['operation_name'] == data['operation_name'] assert response_json['operation_bcghgid'] == data['operation_bcghgid'] assert response_json['bc_obps_regulated_operation_id'] == data['bc_obps_regulated_operation_id'] assert response_json['operation_representative_name'] == data['operation_representative_name'] + diff --git a/bc_obps/reporting/tests/utils/bakers.py b/bc_obps/reporting/tests/utils/bakers.py index 5d7e297673..5f98e7c0cc 100644 --- a/bc_obps/reporting/tests/utils/bakers.py +++ b/bc_obps/reporting/tests/utils/bakers.py @@ -21,13 +21,20 @@ def report_baker(**props) -> Report: return baker.make(Report, **props) -def report_version_baker(**props) -> ReportVersion: +def report_version_baker(report_type="Annual Report", **props) -> ReportVersion: + # Ensure that a report is created if not provided if "report" not in props and "report_id" not in props: props["report"] = report_baker() + # Explicitly set report_type in props if not already provided + props.setdefault("report_type", report_type) + + # Create the ReportVersion instance with given properties version = baker.make(ReportVersion, **props) + # Ensure that ReportOperation is created and linked to ReportVersion if "report_operation" not in props: + # You can set additional properties for ReportOperation here if needed baker.make(ReportOperation, report_version=version) return version diff --git a/bc_obps/service/report_service.py b/bc_obps/service/report_service.py index fc48a2fc13..7cd1e4e115 100644 --- a/bc_obps/service/report_service.py +++ b/bc_obps/service/report_service.py @@ -79,6 +79,7 @@ def get_report_operation_by_version_id(cls, report_version_id: int) -> ReportOpe return ReportOperation.objects.get(report_version__id=report_version_id) @classmethod + @transaction.atomic def save_report_operation(cls, report_version_id: int, data: ReportOperationIn) -> ReportOperation: # Fetch the existing report operation report_operation = ReportOperation.objects.get(report_version__id=report_version_id) diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index c1ffab26b3..ef545ae0e8 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -126,9 +126,6 @@ export default function OperationReview({ emissions in a reporting period and have an obligation to continue reporting emissions for three consecutive reporting periods. This report type is not applicable for opted-in operations. -

- If you are uncertain about which report type your operation should - complete, please contact GHGRegulator@gov.bc.ca.
) : null; diff --git a/bciers/apps/reporting/src/data/jsonSchema/operations.ts b/bciers/apps/reporting/src/data/jsonSchema/operations.ts index 0a1c58576e..c3fe5cf131 100644 --- a/bciers/apps/reporting/src/data/jsonSchema/operations.ts +++ b/bciers/apps/reporting/src/data/jsonSchema/operations.ts @@ -3,6 +3,7 @@ import FieldTemplate from "@bciers/components/form/fields/FieldTemplate"; import { TitleOnlyFieldTemplate } from "@bciers/components/form/fields"; import { purposeNote } from "./reviewOperationInformationText"; import { BC_GOV_BACKGROUND_COLOR_BLUE } from "@bciers/styles"; +import selectWidget from "@bciers/components/form/widgets/SelectWidget"; const commonUiOptions = { style: { width: "100%", textAlign: "left" } }; export const operationReviewSchema: RJSFSchema = { @@ -21,7 +22,8 @@ export const operationReviewSchema: RJSFSchema = { }, operation_report_type: { type: "string", - title: "Please select what type of report are you filling", + title: + "Select what type of report you are filling. If you are uncertain about which report type your operation should complete, please contact GHGRegulator@gov.bc.ca.", enum: ["Annual report", "Simple Report"], default: "Annual report", }, @@ -104,8 +106,7 @@ export const operationReviewUiSchema = { "ui:disabled": true, }, operation_report_type: { - "ui:widget": "select", - "ui:options": { style: { width: "100%", textAlign: "justify" } }, + "ui:widget": selectWidget, "ui:placeholder": "Report type", "ui:disabled": true, }, @@ -181,7 +182,8 @@ export const updateSchema = ( ...prevSchema.properties, operation_report_type: { type: "string", - title: "Please select what type of report you are filling", + title: + "Select what type of report you are filling. If you are uncertain about which report type your operation should complete, please contact GHGRegulator@gov.bc.ca.", enum: ["Annual Report", "Simple Report"], default: formDataState?.operation_report_type || "Annual Report", }, From b49e00486cb52c90220c6d095e29d6bfd2b70b44 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Tue, 22 Oct 2024 23:16:24 -0700 Subject: [PATCH 05/11] chore:fixed tests --- .../tests/api/test_report_version_endpoint.py | 14 ++++---------- .../reporting/tests/models/test_report_version.py | 1 + 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/bc_obps/reporting/tests/api/test_report_version_endpoint.py b/bc_obps/reporting/tests/api/test_report_version_endpoint.py index 48d7ea5563..3cc8566f2c 100644 --- a/bc_obps/reporting/tests/api/test_report_version_endpoint.py +++ b/bc_obps/reporting/tests/api/test_report_version_endpoint.py @@ -20,7 +20,8 @@ def test_authorized_users_can_get_report_version(self): # POST report-operation def test_authorized_users_can_post_updates_to_report_version(self): - report_version = report_version_baker() + report_version = report_version_baker(report_type="Initial Report") + TestUtils.authorize_current_user_as_operator_user(self, operator=report_version.report.operator) endpoint_under_test = f'/api/reporting/report-version/{report_version.id}/report-operation' @@ -35,19 +36,15 @@ def test_authorized_users_can_post_updates_to_report_version(self): "activities": [], "regulated_products": [], "operation_representative_name": "new operation representative name", - "operation_report_type": "LFO", # This belongs to ReportVersion, not ReportOperation + "operation_report_type": "Annual Report", } - - # Assert changes for ReportOperation fields assert report_version.report_operation.operator_legal_name != data['operator_legal_name'] assert report_version.report_operation.operator_trade_name != data['operator_trade_name'] assert report_version.report_operation.operation_name != data['operation_name'] assert report_version.report_operation.operation_bcghgid != data['operation_bcghgid'] assert report_version.report_operation.bc_obps_regulated_operation_id != data['bc_obps_regulated_operation_id'] assert report_version.report_operation.operation_representative_name != data['operation_representative_name'] - - # Assert change for ReportVersion field - assert report_version.report_type != data['operation_report_type'] # Correctly assert for ReportVersion + assert report_version.report_type != data['operation_report_type'] response = TestUtils.mock_post_with_auth_role( self, 'industry_user', self.content_type, data, endpoint_under_test @@ -55,12 +52,9 @@ def test_authorized_users_can_post_updates_to_report_version(self): assert response.status_code == 201 response_json = response.json() - - # Assert response for ReportOperation fields assert response_json['operator_legal_name'] == data['operator_legal_name'] assert response_json['operator_trade_name'] == data['operator_trade_name'] assert response_json['operation_name'] == data['operation_name'] assert response_json['operation_bcghgid'] == data['operation_bcghgid'] assert response_json['bc_obps_regulated_operation_id'] == data['bc_obps_regulated_operation_id'] assert response_json['operation_representative_name'] == data['operation_representative_name'] - diff --git a/bc_obps/reporting/tests/models/test_report_version.py b/bc_obps/reporting/tests/models/test_report_version.py index d46e14f0e2..7c66c73892 100644 --- a/bc_obps/reporting/tests/models/test_report_version.py +++ b/bc_obps/reporting/tests/models/test_report_version.py @@ -11,6 +11,7 @@ def setUpTestData(cls): *TIMESTAMP_COMMON_FIELDS, ("id", "ID", None, None), ("report", "report", None, None), + ("report_type", "report type", None, None), ("is_latest_submitted", "is latest submitted", None, None), ("status", "status", 1000, None), ("facility_reports", "facility report", None, 0), From a90dfd9e5d50302085b98633fadfd752b23d3126 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Wed, 23 Oct 2024 01:30:40 -0700 Subject: [PATCH 06/11] chore:fixed links --- .../components/operations/OperationReview.tsx | 70 +++++++++++++------ .../operations/OperationReviewFormData.tsx | 10 +-- .../operations/OperationReview.test.tsx | 12 ++++ 3 files changed, 64 insertions(+), 28 deletions(-) diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index ef545ae0e8..30d0afc75e 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -3,17 +3,16 @@ import React, { useEffect, useState } from "react"; import MultiStepFormWithTaskList from "@bciers/components/form/MultiStepFormWithTaskList"; import { RJSFSchema } from "@rjsf/utils"; -import { useRouter, useSearchParams } from "next/navigation"; +import { useRouter } from "next/navigation"; import { operationReviewSchema, operationReviewUiSchema, updateSchema, } from "@reporting/src/data/jsonSchema/operations"; import { TaskListElement } from "@bciers/components/navigation/reportingTaskList/types"; -import safeJsonParse from "@bciers/utils/src/safeJsonParse"; +import safeJsonParse from "@bciers/utils/safeJsonParse"; import { actionHandler } from "@bciers/actions"; import { formatDate } from "@reporting/src/app/utils/formatDate"; -import serializeSearchParams from "@bciers/utils/src/serializeSearchParams"; interface Props { formData: any; @@ -29,24 +28,15 @@ interface Props { allActivities: { id: number; name: string }[]; allRegulatedProducts: { id: number; name: string }[]; registrationPurpose: string; + facilityReport: { + facility_id: number; + operation_type: string; + }; } const baseUrl = "/reports"; const cancelUrl = "/reports"; -const taskListElements: TaskListElement[] = [ - { - type: "Section", - title: "Operation information", - isExpanded: true, - elements: [ - { type: "Page", title: "Review Operation information", isActive: true }, - { type: "Page", title: "Person responsible" }, - { type: "Page", title: "Review facilities" }, - ], - }, -]; - export default function OperationReview({ formData, version_id, @@ -55,20 +45,52 @@ export default function OperationReview({ allActivities, allRegulatedProducts, registrationPurpose, + facilityReport, }: Props) { const router = useRouter(); const [schema, setSchema] = useState(operationReviewSchema); const [uiSchema, setUiSchema] = useState(operationReviewUiSchema); const [formDataState, setFormDataState] = useState(formData); - const queryString = serializeSearchParams(useSearchParams()); - const saveAndContinueUrl = `/reports/${version_id}/person-responsible${queryString}`; + const saveAndContinueUrl = `/reports/${version_id}/person-responsible`; + const [facilityId, setFacilityId] = useState(null); + const [operationType, setOperationType] = useState(""); const reportingWindowEnd = formatDate( reportingYear.reporting_window_end, "MMM DD YYYY", ); - // Function to prepare the form data for submission + const facilityPageUrl = + operationType === "Linear Facility Operation" + ? `/reports/${version_id}/facilities/lfo-facilities` + : `/reports/${version_id}/facilities/${facilityId}/review`; + + const taskListElements: TaskListElement[] = [ + { + type: "Section", + title: "Operation information", + isExpanded: true, + elements: [ + { + type: "Page", + title: "Review Operation information", + isActive: true, + link: `/reports/${version_id}/review-operator-data`, + }, + { + type: "Page", + title: "Person responsible", + link: `/reports/${version_id}/person-responsible`, + }, + { + type: "Page", + title: "Review facilities", + link: `${facilityPageUrl}`, + }, + ], + }, + ]; + const prepareFormData = (formDataObject: any) => { return { ...formDataObject, @@ -95,7 +117,6 @@ export default function OperationReview({ }; }; - // Combined useEffect for initialization and schema updates useEffect(() => { if (formData && allActivities && allRegulatedProducts) { const updatedFormData = { @@ -137,16 +158,20 @@ export default function OperationReview({ }, }); } + if (facilityReport?.facility_id) { + setFacilityId(facilityReport.facility_id); + setOperationType(facilityReport.operation_type); + } }, [ formData, reportType, + facilityReport, allActivities, allRegulatedProducts, registrationPurpose, reportingWindowEnd, ]); - // Handle form submission const submitHandler = async ( data: { formData?: any }, reportVersionId: number, @@ -162,11 +187,10 @@ export default function OperationReview({ }); if (response) { - router.push(saveAndContinueUrl); // Navigate on success + router.push(saveAndContinueUrl); } }; - // Handle form data changes const onChangeHandler = (data: { formData: any }) => { setFormDataState(data.formData); }; diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx index 66a4f53877..ee674517d7 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx @@ -5,6 +5,7 @@ import { getAllActivities } from "@reporting/src/app/utils/getAllReportingActivi import { getAllRegulatedProducts } from "@reporting/src/app/utils/getAllRegulatedProducts"; import { getReportType } from "@reporting/src/app/utils/getReportType"; import { getRegistrationPurpose } from "@reporting/src/app/utils/getRegistrationPurpose"; +import { getFacilityReport } from "@reporting/src/app/utils/getFacilityReport"; export default async function OperationReviewFormData({ version_id, @@ -17,12 +18,10 @@ export default async function OperationReviewFormData({ const reportingYear = await getReportingYear(); const reportType = await getReportType(version_id); const registrationPurpose = await getRegistrationPurpose(version_id); + const facilityReport = await getFacilityReport(version_id); - const registrationPurposeString = Array.isArray( - registrationPurpose.registration_purposes, - ) - ? registrationPurpose.registration_purposes.join(", ") - : registrationPurpose.registration_purposes || ""; + const registrationPurposeString = + registrationPurpose?.registration_purposes?.join(", ") || ""; return ( ); } diff --git a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx index c1fef2e12d..10de37bc4f 100644 --- a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx +++ b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx @@ -42,6 +42,10 @@ describe("OperationReview Component", () => { allActivities={[{ id: 1, name: "Activity 1" }]} allRegulatedProducts={[{ id: 1, name: "Product 1" }]} registrationPurpose="Test Purpose" + facilityReport={{ + facility_id: 2344, + operation_type: "Single Facility Operation", + }} />, ); @@ -78,6 +82,10 @@ describe("OperationReview Component", () => { allActivities={[{ id: 1, name: "Activity 1" }]} allRegulatedProducts={[{ id: 1, name: "Product 1" }]} registrationPurpose="Test Purpose" + facilityReport={{ + facility_id: 2344, + operation_type: "Single Facility Operation", + }} />, ); @@ -115,6 +123,10 @@ describe("OperationReview Component", () => { allActivities={[{ id: 1, name: "Activity 1" }]} allRegulatedProducts={[{ id: 1, name: "Product 1" }]} registrationPurpose="Test Purpose" + facilityReport={{ + facility_id: 2344, + operation_type: "Single Facility Operation", + }} />, ); From 2382a70d96e3a9ab1a0619294bd0bfad2018eec8 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Thu, 7 Nov 2024 18:59:56 -0800 Subject: [PATCH 07/11] chore:fixed helper text --- .../components/operations/OperationReview.tsx | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index 30d0afc75e..3d36bafbea 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -137,26 +137,6 @@ export default function OperationReview({ allRegulatedProducts, ), ); - - const helperText = - updatedFormData.operation_report_type === "Simple Report" ? ( - - Simple reports are submitted by operations that previously emitted - greater than or equal to 10,000 tCO2e of attributable emissions in a - reporting period, but now emit under 10,000 tCO2e of attributable - emissions in a reporting period and have an obligation to continue - reporting emissions for three consecutive reporting periods. This - report type is not applicable for opted-in operations. - - ) : null; - - setUiSchema({ - ...operationReviewUiSchema, - operation_report_type: { - "ui:widget": "select", - "ui:help": helperText, - }, - }); } if (facilityReport?.facility_id) { setFacilityId(facilityReport.facility_id); @@ -192,7 +172,33 @@ export default function OperationReview({ }; const onChangeHandler = (data: { formData: any }) => { - setFormDataState(data.formData); + const updatedFormData = data.formData; + + const helperText = + updatedFormData.operation_report_type === "Simple Report" ? ( + + Simple Reports are submitted by reporting operations that previously + emitted greater than or equal to 10 000 tCO2e of attributable + emissions in a reporting period, but now emit under 10 000 tCO2e of + attributable emissions and have an obligation to continue reporting + emissions for three consecutive reporting periods. This report type is + not applicable for any operations that received third party + verification in the immediately preceding reporting period, and is not + applicable for opted-in operations. + + ) : ( + "" + ); + + setUiSchema({ + ...operationReviewUiSchema, + operation_report_type: { + "ui:widget": "select", + "ui:help": helperText, + }, + }); + + setFormDataState(updatedFormData); }; if (!formData) { From 806dfceb79d89fd768aef52be730127140519bd0 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Thu, 7 Nov 2024 19:12:58 -0800 Subject: [PATCH 08/11] chore:fixed migrations and other erros after rebase --- ...port_type.py => 0031_reportversion_report_type.py} | 2 +- .../src/app/components/operations/OperationReview.tsx | 4 ++-- .../components/operations/OperationReviewFormData.tsx | 11 +++++------ 3 files changed, 8 insertions(+), 9 deletions(-) rename bc_obps/reporting/migrations/{0022_reportversion_report_type.py => 0031_reportversion_report_type.py} (86%) diff --git a/bc_obps/reporting/migrations/0022_reportversion_report_type.py b/bc_obps/reporting/migrations/0031_reportversion_report_type.py similarity index 86% rename from bc_obps/reporting/migrations/0022_reportversion_report_type.py rename to bc_obps/reporting/migrations/0031_reportversion_report_type.py index 35b0da0471..0ffb5f9008 100644 --- a/bc_obps/reporting/migrations/0022_reportversion_report_type.py +++ b/bc_obps/reporting/migrations/0031_reportversion_report_type.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('reporting', '0021_report_additional_data'), + ('reporting', '0030_report_non_attributable_emissions'), ] operations = [ diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx index 3d36bafbea..cd2758c339 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReview.tsx @@ -10,9 +10,9 @@ import { updateSchema, } from "@reporting/src/data/jsonSchema/operations"; import { TaskListElement } from "@bciers/components/navigation/reportingTaskList/types"; -import safeJsonParse from "@bciers/utils/safeJsonParse"; import { actionHandler } from "@bciers/actions"; import { formatDate } from "@reporting/src/app/utils/formatDate"; +import safeJsonParse from "@bciers/utils/src/safeJsonParse"; interface Props { formData: any; @@ -220,7 +220,7 @@ export default function OperationReview({ formData={formDataState} baseUrl={baseUrl} cancelUrl={cancelUrl} - onSubmit={(data) => submitHandler(data, version_id)} + onSubmit={(data: { formData?: any }) => submitHandler(data, version_id)} onChange={onChangeHandler} /> ); diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx index ee674517d7..0c18da8b4a 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx @@ -1,12 +1,11 @@ -import OperationReview from "./OperationReview"; -import { getReportingYear } from "@reporting/src/app/utils/getReportingYear"; -import { getReportingOperation } from "@reporting/src/app/utils/getReportingOperation"; import { getAllActivities } from "@reporting/src/app/utils/getAllReportingActivities"; -import { getAllRegulatedProducts } from "@reporting/src/app/utils/getAllRegulatedProducts"; +import { getReportingOperation } from "@reporting/src/app/utils/getReportingOperation"; +import { getReportingYear } from "@reporting/src/app/utils/getReportingYear"; import { getReportType } from "@reporting/src/app/utils/getReportType"; +import { getRegulatedProducts } from "@bciers/actions/api"; import { getRegistrationPurpose } from "@reporting/src/app/utils/getRegistrationPurpose"; import { getFacilityReport } from "@reporting/src/app/utils/getFacilityReport"; - +import OperationReview from "./OperationReview"; export default async function OperationReviewFormData({ version_id, }: { @@ -14,7 +13,7 @@ export default async function OperationReviewFormData({ }) { const reportOperation = await getReportingOperation(version_id); const allActivities = await getAllActivities(); - const allRegulatedProducts = await getAllRegulatedProducts(); + const allRegulatedProducts = await getRegulatedProducts(); const reportingYear = await getReportingYear(); const reportType = await getReportType(version_id); const registrationPurpose = await getRegistrationPurpose(version_id); From 094fae729debb85ebddb5db59ea2b6c165f07123 Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Mon, 18 Nov 2024 11:28:14 -0800 Subject: [PATCH 09/11] chore: fixed registration purpose --- bc_obps/reporting/api/reports.py | 4 ++-- bc_obps/service/report_additional_data.py | 12 +++++++----- .../AdditionalReportingData.tsx | 9 ++++----- .../operations/OperationReviewFormData.tsx | 3 +-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bc_obps/reporting/api/reports.py b/bc_obps/reporting/api/reports.py index df7bda874e..ee28d6ae81 100644 --- a/bc_obps/reporting/api/reports.py +++ b/bc_obps/reporting/api/reports.py @@ -51,7 +51,7 @@ def get_report_operation_by_version_id( response={201: ReportOperationOut, custom_codes_4xx: Message}, tags=EMISSIONS_REPORT_TAGS, description="""Updates given report operation with fields: Operator Legal Name, Operator Trade Name, Operation Name, Operation Type, - Operation BC GHG ID, BC OBPS Regulated Operation ID, Operation Representative Name, aznd Activities.""", + Operation BC GHG ID, BC OBPS Regulated Operation ID, Operation Representative Name, and Activities.""", auth=authorize("approved_industry_user"), ) @handle_http_errors() @@ -79,7 +79,7 @@ def get_reporting_year(request: HttpRequest) -> Tuple[Literal[200], ReportingYea response={200: ReportingVersionOut, custom_codes_4xx: Message}, tags=EMISSIONS_REPORT_TAGS, description="Retrieve the report type for a specific reporting version, including the reporting year and due date.", - # auth=authorize("all_roles"), + auth=authorize("approved_industry_user"), ) @handle_http_errors() def get_report_type_by_version(request: HttpRequest, version_id: int) -> tuple[Literal[200], ReportVersion]: diff --git a/bc_obps/service/report_additional_data.py b/bc_obps/service/report_additional_data.py index a6f4812b24..4887fc33b0 100644 --- a/bc_obps/service/report_additional_data.py +++ b/bc_obps/service/report_additional_data.py @@ -17,13 +17,15 @@ def get_registration_purpose_by_version_id(version_id: int) -> dict: id=Report.objects.get(id=ReportVersion.objects.get(id=version_id).report_id).operation_id ) - # Use list comprehension to collect registration purposes directly - purposes_list = list( - RegistrationPurpose.objects.filter(operation=operation).values_list('registration_purpose', flat=True) + # Fetch the single registration purpose + registration_purpose = ( + RegistrationPurpose.objects.filter(operation=operation) + .values_list('registration_purpose', flat=True) + .first() ) - # Always return the result as a dictionary with an array (empty or populated) - return {"registration_purposes": purposes_list} + # Return the result as a dictionary with a single purpose or None if not found + return {"registration_purpose": registration_purpose} @staticmethod def save_report_additional_data(version_id: int, data: ReportAdditionalDataIn) -> ReportAdditionalData: diff --git a/bciers/apps/reporting/src/app/components/additionalInformation/additionalReportingData/AdditionalReportingData.tsx b/bciers/apps/reporting/src/app/components/additionalInformation/additionalReportingData/AdditionalReportingData.tsx index 2ee64ed629..42f88b4aa2 100644 --- a/bciers/apps/reporting/src/app/components/additionalInformation/additionalReportingData/AdditionalReportingData.tsx +++ b/bciers/apps/reporting/src/app/components/additionalInformation/additionalReportingData/AdditionalReportingData.tsx @@ -8,11 +8,10 @@ export default async function AdditionalReportingData({ }: { versionId: number; }) { - const registrationPurposes = - (await getRegistrationPurpose(versionId))?.registration_purposes || []; - const includeElectricityGenerated = registrationPurposes.includes( - "OBPS Regulated Operation", - ); + const registrationPurpose = (await getRegistrationPurpose(versionId)) + ?.registration_purpose; + const includeElectricityGenerated = + registrationPurpose === "OBPS Regulated Operation"; return ( }> diff --git a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx index 0c18da8b4a..b66ad3b54e 100644 --- a/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx +++ b/bciers/apps/reporting/src/app/components/operations/OperationReviewFormData.tsx @@ -19,8 +19,7 @@ export default async function OperationReviewFormData({ const registrationPurpose = await getRegistrationPurpose(version_id); const facilityReport = await getFacilityReport(version_id); - const registrationPurposeString = - registrationPurpose?.registration_purposes?.join(", ") || ""; + const registrationPurposeString = registrationPurpose?.registration_purposes; return ( Date: Mon, 18 Nov 2024 11:41:05 -0800 Subject: [PATCH 10/11] chore: fixed migration after rebases and test --- ...rsion_report_type.py => 0032_reportversion_report_type.py} | 2 +- .../src/tests/components/operations/OperationReview.test.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) rename bc_obps/reporting/migrations/{0031_reportversion_report_type.py => 0032_reportversion_report_type.py} (86%) diff --git a/bc_obps/reporting/migrations/0031_reportversion_report_type.py b/bc_obps/reporting/migrations/0032_reportversion_report_type.py similarity index 86% rename from bc_obps/reporting/migrations/0031_reportversion_report_type.py rename to bc_obps/reporting/migrations/0032_reportversion_report_type.py index 0ffb5f9008..b0d8b78250 100644 --- a/bc_obps/reporting/migrations/0031_reportversion_report_type.py +++ b/bc_obps/reporting/migrations/0032_reportversion_report_type.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('reporting', '0030_report_non_attributable_emissions'), + ('reporting', '0031_alter_reportemission_managers'), ] operations = [ diff --git a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx index 10de37bc4f..2088ce7653 100644 --- a/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx +++ b/bciers/apps/reporting/src/tests/components/operations/OperationReview.test.tsx @@ -132,7 +132,9 @@ describe("OperationReview Component", () => { await waitFor(() => { expect( - screen.getByText(/Simple reports are submitted by operations/i), + screen.getByText( + /Simple Reports are submitted by reporting operations/i, + ), ).toBeInTheDocument(); }); }); From 5c85955b6bdf5fab52f57b097d99528bca0ef4ea Mon Sep 17 00:00:00 2001 From: ayeshmcg Date: Mon, 18 Nov 2024 13:04:22 -0800 Subject: [PATCH 11/11] chore: fixed auth test --- .../common/tests/endpoints/auth/test_endpoint_permissions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bc_obps/common/tests/endpoints/auth/test_endpoint_permissions.py b/bc_obps/common/tests/endpoints/auth/test_endpoint_permissions.py index acb682b59a..5118f8b4f6 100644 --- a/bc_obps/common/tests/endpoints/auth/test_endpoint_permissions.py +++ b/bc_obps/common/tests/endpoints/auth/test_endpoint_permissions.py @@ -15,6 +15,7 @@ def suppress_django_request_logs(caplog): class TestEndpointPermissions(TestCase): client = Client() mock_uuid = "e1300fd7-2dee-47d1-b655-2ad3fd10f052" + mock_version = '1' mock_int = 1 endpoints_to_test = { "authorized_roles": [ @@ -146,6 +147,7 @@ class TestEndpointPermissions(TestCase): "endpoint_name": "remove_operation_representative", "kwargs": {"operation_id": mock_uuid}, }, + {"method": "get", "endpoint_name": "get_report_type_by_version", "kwargs": {'version_id': mock_version}}, ], "all_roles": [ {"method": "get", "endpoint_name": "get_reporting_year"},