From 3b7b8094522de683ce074820197c370d9c1b3e3a Mon Sep 17 00:00:00 2001 From: Andrew White Date: Mon, 17 Feb 2025 20:58:44 +0000 Subject: [PATCH] Add assessment of GPDO policies to assessment report --- app/assets/stylesheets/_main.scss | 1 + .../components/_assessment_list.scss | 32 +++ .../assessment_report_component.html.erb | 39 ++++ .../assessment/assessment_report_component.rb | 7 + app/components/status_tags/base_component.rb | 6 +- app/models/comment.rb | 6 + app/models/planning_application.rb | 3 +- .../planning_application_policy_class.rb | 9 + .../planning_application_policy_section.rb | 5 + app/models/policy_class.rb | 4 + config/locales/en.yml | 4 + .../viewing_assessment_report_spec.rb | 189 ++++++++++++++++++ 12 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 app/assets/stylesheets/components/_assessment_list.scss diff --git a/app/assets/stylesheets/_main.scss b/app/assets/stylesheets/_main.scss index 39451bee5d..aee6adf17d 100644 --- a/app/assets/stylesheets/_main.scss +++ b/app/assets/stylesheets/_main.scss @@ -14,6 +14,7 @@ $govuk-page-width: 1100px; @import "bops_core/components/ticket_panel"; // Imports from the application +@import "components/assessment_list"; @import "components/buttons"; @import "components/documents"; @import "components/flashes"; diff --git a/app/assets/stylesheets/components/_assessment_list.scss b/app/assets/stylesheets/components/_assessment_list.scss new file mode 100644 index 0000000000..dc0be11071 --- /dev/null +++ b/app/assets/stylesheets/components/_assessment_list.scss @@ -0,0 +1,32 @@ +.bops-assessment-list { + border-top: 1px solid $govuk-border-colour; + + &__row { + display: flex; + padding-top: govuk-spacing(2); + padding-bottom: govuk-spacing(2); + border-bottom: 1px solid $govuk-border-colour; + } + + &__section { + @include govuk-font($size: 19); + font-weight: bold; + width: 10%; + } + + &__description { + margin-left: 0; + width: 62%; + + p { + margin-bottom: govuk-spacing(2); + } + } + + &__status { + margin-left: 0; + text-align: right; + white-space: nowrap; + width: 28%; + } +} diff --git a/app/components/planning_applications/assessment/assessment_report_component.html.erb b/app/components/planning_applications/assessment/assessment_report_component.html.erb index 7a85c5ccca..bd0912a7f0 100644 --- a/app/components/planning_applications/assessment/assessment_report_component.html.erb +++ b/app/components/planning_applications/assessment/assessment_report_component.html.erb @@ -179,4 +179,43 @@ <% end %> +<% if @planning_application.assess_against_policies? %> +
+

<%= t(".assessment_against_legislation") %>

+ + <% policy_classes.each do |policy_class| %> +

<%= govuk_link_to policy_class.description, policy_class.url %>

+ +
+ <% policy_class.policy_sections.each do |policy_section| %> +
+
+ <%= policy_section.section %> +
+
+

+ <%= policy_section.description %> +

+ <% if comment = policy_section.last_comment %> + <%= bops_ticket_panel(colour: "yellow") do |ticket| %> + <% ticket.with_body { comment.text } %> + <% ticket.with_footer { comment.information } %> + <% end %> + <% end %> +
+
+ <%= render(StatusTags::BaseComponent.new(status: policy_section.status)) %> +
+
+ <% end %> +
+ <% if show_edit_links && current_user.assessor? %> +

+ <%= govuk_link_to("Edit assessment", edit_planning_application_assessment_policy_areas_policy_class_path(@planning_application, policy_class)) %> +

+ <% end %> + <% end %> +
+<% end %> + <%= render "planning_applications/assessment/recommendations/documents", planning_application: planning_application, documents: documents %> diff --git a/app/components/planning_applications/assessment/assessment_report_component.rb b/app/components/planning_applications/assessment/assessment_report_component.rb index 4d39f7f934..ec1621f020 100644 --- a/app/components/planning_applications/assessment/assessment_report_component.rb +++ b/app/components/planning_applications/assessment/assessment_report_component.rb @@ -27,12 +27,15 @@ def initialize(planning_application:, show_additional_evidence: false, show_edit :consultation_summary, :consultation, :permitted_development_right, + :planning_application_policy_classes, :additional_evidence, :immunity_detail, :neighbour_summary, to: :planning_application ) + delegate :bops_ticket_panel, to: :helpers + def considerations planning_application.consideration_set.considerations end @@ -41,6 +44,10 @@ def documents planning_application.documents_for_decision_notice end + def policy_classes + planning_application_policy_classes + end + def current_user Current.user end diff --git a/app/components/status_tags/base_component.rb b/app/components/status_tags/base_component.rb index 7b6e99ab0a..5953dd57f1 100644 --- a/app/components/status_tags/base_component.rb +++ b/app/components/status_tags/base_component.rb @@ -25,15 +25,15 @@ def link_text def colour case status.to_sym - when :approved, :auto_approved, :supportive + when :approved, :auto_approved, :supportive, :complies "green" when :not_started, :new, :review_not_started, :not_consulted, :none, :no_response "blue" - when :in_progress, :awaiting_response + when :in_progress, :awaiting_response, :to_be_determined "light-blue" when :updated, :to_be_reviewed, :submitted, :neutral, :amendments_needed, :awaiting_changes "yellow" - when :refused, :removed, :invalid, :rejected, :objection, :failed, :refused_legal_agreement, :cancelled + when :refused, :removed, :invalid, :rejected, :objection, :failed, :refused_legal_agreement, :cancelled, :does_not_comply "red" end end diff --git a/app/models/comment.rb b/app/models/comment.rb index a8caf05e33..e554564091 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -18,6 +18,12 @@ def deleted? deleted_at.present? end + def information + if user.present? + "By #{user.name} on #{created_at.to_fs(:day_month_year_slashes)}" + end + end + private def previous diff --git a/app/models/planning_application.rb b/app/models/planning_application.rb index 9303aa018f..34f9bd297e 100644 --- a/app/models/planning_application.rb +++ b/app/models/planning_application.rb @@ -58,7 +58,7 @@ class WithdrawOrCancelError < RuntimeError; end has_many :press_notices, -> { by_created_at_desc } has_many :planning_application_policy_classes has_many :policy_classes, through: :planning_application_policy_classes - has_many :planning_application_policy_sections + has_many :planning_application_policy_sections, -> { by_id } has_many :policy_sections, through: :planning_application_policy_sections has_many :additional_services @@ -85,6 +85,7 @@ class WithdrawOrCancelError < RuntimeError; end with_options to: :application_type do delegate :appeals? + delegate :assess_against_policies? delegate :consultation? delegate :neighbour_consultation_feature? delegate :consultee_consultation_feature? diff --git a/app/models/planning_application_policy_class.rb b/app/models/planning_application_policy_class.rb index 872933d3fc..912db50b29 100644 --- a/app/models/planning_application_policy_class.rb +++ b/app/models/planning_application_policy_class.rb @@ -12,6 +12,15 @@ class PlanningApplicationPolicyClass < ApplicationRecord validates :reporting_types, presence: true end + delegate :section, :name, :url, :description, to: :policy_class + delegate :planning_application_policy_sections, to: :planning_application + + def policy_sections + planning_application_policy_sections + .includes(:comments, :policy_section) + .where(policy_section: {policy_class_id: policy_class_id}) + end + def current_review reviews.load.first || reviews.create! end diff --git a/app/models/planning_application_policy_section.rb b/app/models/planning_application_policy_section.rb index 13a540183d..dd2ced8f57 100644 --- a/app/models/planning_application_policy_section.rb +++ b/app/models/planning_application_policy_section.rb @@ -12,6 +12,8 @@ class PlanningApplicationPolicySection < ApplicationRecord dependent: :destroy ) + scope :by_id, -> { order(:id) } + accepts_nested_attributes_for( :comments, reject_if: :reject_comment? @@ -25,6 +27,9 @@ class PlanningApplicationPolicySection < ApplicationRecord %i[complies does_not_comply to_be_determined].index_with(&:to_s), default: :to_be_determined + delegate :section, :title, to: :policy_section + delegate :policy_class, to: :policy_section + def set_description self.description ||= policy_section.description end diff --git a/app/models/policy_class.rb b/app/models/policy_class.rb index a9a2b31f2a..0fe388d7dd 100644 --- a/app/models/policy_class.rb +++ b/app/models/policy_class.rb @@ -23,4 +23,8 @@ def menu pluck(:id, :section, :name) end end + + def description + "Part #{policy_part_number}, Class #{section} - #{name}" + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index ac12183791..6f1462e24a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1201,6 +1201,7 @@ en: assessment_report_component: additional_evidence: Summary of additional evidence application_details: Application details + assessment_against_legislation: Assessment against legislation assessment_against_policies_and_guidance: Policies and guidance constraints_including_article: Constraints including Article 4 direction(s) consultation_documents: Consultation documents @@ -2182,6 +2183,8 @@ en: awaiting_responses: Awaiting responses cancelled: Cancelled complete: Completed + complies: Complies + does_not_comply: Does not comply emailed: Emailed failed: Failed granted: To grant @@ -2222,6 +2225,7 @@ en: sending: Sending submitted: Submitted supportive: Supportive + to_be_determined: To be determined to_be_reviewed: To be reviewed updated: Updated valid: Valid diff --git a/spec/system/planning_applications/assessing/viewing_assessment_report_spec.rb b/spec/system/planning_applications/assessing/viewing_assessment_report_spec.rb index e566eed9af..e5e167b818 100644 --- a/spec/system/planning_applications/assessing/viewing_assessment_report_spec.rb +++ b/spec/system/planning_applications/assessing/viewing_assessment_report_spec.rb @@ -37,7 +37,95 @@ ) end + let!(:policy_schedule) do + create( + :policy_schedule, + number: 2, + name: "Permitted development rights" + ) + end + + let!(:policy_part) do + create( + :policy_part, + name: "Development within the curtilage of a dwellinghouse", + number: 1, + policy_schedule: policy_schedule + ) + end + + let!(:policy_class) do + create( + :policy_class, + section: "A", + name: "enlargement, improvement or other alteration of a dwellinghouse", + url: "https://www.legislation.gov.uk/uksi/2015/596/schedule/2/part/1/crossheading/class-a-enlargement-improvement-or-other-alteration-of-a-dwellinghouse", + policy_part: policy_part + ) + end + + let!(:policy_section_A) do + create( + :policy_section, + section: "A", + policy_class: policy_class, + title: "Other", + description: <<~TEXT.squish + The enlargement, improvement or other alteration of a dwellinghouse + TEXT + ) + end + + let!(:policy_section_1a) do + create( + :policy_section, + section: "1a", + policy_class: policy_class, + title: "Other", + description: <<~TEXT.squish + Development is not permitted by Class A if permission to + use the dwellinghouse as a dwellinghouse has been granted + only by virtue of Class M, MA, N, P, PA or Q of Part 3 of + this Schedule (changes of use) + TEXT + ) + end + + let!(:policy_section_1b) do + create( + :policy_section, + section: "1b", + policy_class: policy_class, + title: "Other", + description: <<~TEXT.squish + Development is not permitted by Class A if as a result + of the works, the total area of ground covered by + buildings within the curtilage of the dwellinghouse + (other than the original dwellinghouse) would exceed + 50% of the total area of the curtilage (excluding the + ground area of the original dwellinghouse) + TEXT + ) + end + + let!(:policy_section_1c) do + create( + :policy_section, + section: "1c", + policy_class: policy_class, + title: "Other", + description: <<~TEXT.squish + Development is not permitted by Class A if the height + of the part of the dwellinghouse enlarged, improved or + altered would exceed the height of the highest part of + the roof of the existing dwellinghouse + TEXT + ) + end + before do + Current.user = assessor + create( :recommendation, planning_application: @@ -84,6 +172,40 @@ application_number: "22-00999-LDCP", description: "This is the past application history summary." ) + + create( + :planning_application_policy_class, + planning_application:, + policy_class: + ) + + create( + :planning_application_policy_section, + planning_application:, + policy_section: policy_section_A + ) + + create( + :planning_application_policy_section, + :with_comments, + :complies, + planning_application:, + policy_section: policy_section_1a + ) + + create( + :planning_application_policy_section, + :with_comments, + :does_not_comply, + planning_application:, + policy_section: policy_section_1b + ) + + create( + :planning_application_policy_section, + planning_application:, + policy_section: policy_section_1c + ) end it "lets the user view and download the report" do @@ -100,6 +222,73 @@ expect(page).to have_content(planning_application.user.name) end + within("#policy-classes-section") do + expect(page).to have_selector("h3", text: "Assessment against legislation") + expect(page).to have_link(policy_class.description, href: policy_class.url) + + within ".bops-assessment-list" do + within ".bops-assessment-list__row:nth-of-type(1)" do + expect(page).to have_selector(".bops-assessment-list__section", text: "A") + + within ".bops-assessment-list__description" do + expect(page).to have_selector("p.govuk-body:first-child", text: policy_section_A.description) + expect(page).not_to have_selector(".bops-ticket-panel") + end + + within ".bops-assessment-list__status" do + expect(page).to have_selector(".govuk-tag.govuk-tag--light-blue", text: "To be determined") + end + end + + within ".bops-assessment-list__row:nth-of-type(2)" do + expect(page).to have_selector(".bops-assessment-list__section", text: "1a") + + within ".bops-assessment-list__description" do + expect(page).to have_selector("p.govuk-body:first-child", text: policy_section_1a.description) + + within ".bops-ticket-panel" do + expect(page).to have_selector(".bops-ticket-panel__body", text: "A comment") + expect(page).to have_selector(".bops-ticket-panel__footer", text: "By #{assessor.name} on #{Date.current.to_fs(:day_month_year_slashes)}") + end + end + + within ".bops-assessment-list__status" do + expect(page).to have_selector(".govuk-tag.govuk-tag--green", text: "Complies") + end + end + + within ".bops-assessment-list__row:nth-of-type(3)" do + expect(page).to have_selector(".bops-assessment-list__section", text: "1b") + + within ".bops-assessment-list__description" do + expect(page).to have_selector("p.govuk-body:first-child", text: policy_section_1b.description) + + within ".bops-ticket-panel" do + expect(page).to have_selector(".bops-ticket-panel__body", text: "A comment") + expect(page).to have_selector(".bops-ticket-panel__footer", text: "By #{assessor.name} on #{Date.current.to_fs(:day_month_year_slashes)}") + end + end + + within ".bops-assessment-list__status" do + expect(page).to have_selector(".govuk-tag.govuk-tag--red", text: "Does not comply") + end + end + + within ".bops-assessment-list__row:nth-of-type(4)" do + expect(page).to have_selector(".bops-assessment-list__section", text: "1c") + + within ".bops-assessment-list__description" do + expect(page).to have_selector("p.govuk-body:first-child", text: policy_section_1c.description) + expect(page).not_to have_selector(".bops-ticket-panel") + end + + within ".bops-assessment-list__status" do + expect(page).to have_selector(".govuk-tag.govuk-tag--light-blue", text: "To be determined") + end + end + end + end + expect(page).to have_content("Conservation area") expect(page).to have_content("22-00999-LDCP") expect(page).to have_content("This is the past application history summary.")