natdeanlewissoftwire
2 years ago
191 changed files with 3887 additions and 1319 deletions
@ -1,18 +1,18 @@ |
|||||||
class CheckAnswersSummaryListCardComponent < ViewComponent::Base |
class CheckAnswersSummaryListCardComponent < ViewComponent::Base |
||||||
attr_reader :questions, :lettings_log, :user |
attr_reader :questions, :log, :user |
||||||
|
|
||||||
def initialize(questions:, lettings_log:, user:) |
def initialize(questions:, log:, user:) |
||||||
@questions = questions |
@questions = questions |
||||||
@lettings_log = lettings_log |
@log = log |
||||||
@user = user |
@user = user |
||||||
super |
super |
||||||
end |
end |
||||||
|
|
||||||
def applicable_questions |
def applicable_questions |
||||||
questions.reject { |q| q.hidden_in_check_answers?(lettings_log, user) } |
questions.reject { |q| q.hidden_in_check_answers?(log, user) } |
||||||
end |
end |
||||||
|
|
||||||
def get_answer_label(question) |
def get_answer_label(question) |
||||||
question.answer_label(lettings_log).presence || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe |
question.answer_label(log).presence || "<span class=\"app-!-colour-muted\">You didn’t answer this question</span>".html_safe |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -0,0 +1,73 @@ |
|||||||
|
class LogsController < ApplicationController |
||||||
|
include Pagy::Backend |
||||||
|
include Modules::LogsFilter |
||||||
|
include Modules::SearchFilter |
||||||
|
|
||||||
|
skip_before_action :verify_authenticity_token, if: :json_api_request? |
||||||
|
before_action :authenticate, if: :json_api_request? |
||||||
|
before_action :authenticate_user!, unless: :json_api_request? |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def create |
||||||
|
log = yield |
||||||
|
raise "Caller must pass a block that implements model creation" if log.blank? |
||||||
|
|
||||||
|
respond_to do |format| |
||||||
|
format.html do |
||||||
|
log.save! |
||||||
|
redirect_to post_create_redirect_url(log) |
||||||
|
end |
||||||
|
format.json do |
||||||
|
if log.save |
||||||
|
render json: log, status: :created |
||||||
|
else |
||||||
|
render json: { errors: log.errors.messages }, status: :unprocessable_entity |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def post_create_redirect_url |
||||||
|
raise "implement in sub class" |
||||||
|
end |
||||||
|
|
||||||
|
API_ACTIONS = %w[create show update destroy].freeze |
||||||
|
|
||||||
|
def json_api_request? |
||||||
|
API_ACTIONS.include?(request["action"]) && request.format.json? |
||||||
|
end |
||||||
|
|
||||||
|
def authenticate |
||||||
|
http_basic_authenticate_or_request_with name: ENV["API_USER"], password: ENV["API_KEY"] |
||||||
|
end |
||||||
|
|
||||||
|
def log_params |
||||||
|
if current_user && !current_user.support? |
||||||
|
org_params.merge(api_log_params) |
||||||
|
else |
||||||
|
api_log_params |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def api_log_params |
||||||
|
return {} unless params[:lettings_log] || params[:sales_log] |
||||||
|
|
||||||
|
permitted = permitted_log_params |
||||||
|
owning_id = permitted["owning_organisation_id"] |
||||||
|
permitted["owning_organisation"] = Organisation.find(owning_id) if owning_id |
||||||
|
permitted |
||||||
|
end |
||||||
|
|
||||||
|
def org_params |
||||||
|
{ |
||||||
|
"owning_organisation_id" => current_user.organisation.id, |
||||||
|
"managing_organisation_id" => current_user.organisation.id, |
||||||
|
"created_by_id" => current_user.id, |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
def search_term |
||||||
|
params["search"] |
||||||
|
end |
||||||
|
end |
@ -1,23 +0,0 @@ |
|||||||
module Modules::LettingsLogsFilter |
|
||||||
def filtered_lettings_logs(logs) |
|
||||||
if session[:lettings_logs_filters].present? |
|
||||||
filters = JSON.parse(session[:lettings_logs_filters]) |
|
||||||
filters.each do |category, values| |
|
||||||
next if Array(values).reject(&:empty?).blank? |
|
||||||
next if category == "organisation" && params["organisation_select"] == "all" |
|
||||||
|
|
||||||
logs = logs.public_send("filter_by_#{category}", values, current_user) |
|
||||||
end |
|
||||||
end |
|
||||||
logs = logs.order(created_at: :desc) |
|
||||||
current_user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs |
|
||||||
end |
|
||||||
|
|
||||||
def set_session_filters(specific_org: false) |
|
||||||
new_filters = session[:lettings_logs_filters].present? ? JSON.parse(session[:lettings_logs_filters]) : {} |
|
||||||
current_user.lettings_logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? } |
|
||||||
new_filters = new_filters.except("organisation") if params["organisation_select"] == "all" |
|
||||||
|
|
||||||
session[:lettings_logs_filters] = new_filters.to_json |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,21 @@ |
|||||||
|
module Modules::LogsFilter |
||||||
|
def filtered_logs(logs, search_term, filters) |
||||||
|
all_orgs = params["organisation_select"] == "all" |
||||||
|
FilterService.filter_logs(logs, search_term, filters, all_orgs, current_user) |
||||||
|
end |
||||||
|
|
||||||
|
def load_session_filters(specific_org: false) |
||||||
|
current_filters = session[:logs_filters] |
||||||
|
new_filters = current_filters.present? ? JSON.parse(current_filters) : {} |
||||||
|
current_user.logs_filters(specific_org:).each { |filter| new_filters[filter] = params[filter] if params[filter].present? } |
||||||
|
params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters |
||||||
|
end |
||||||
|
|
||||||
|
def session_filters(specific_org: false) |
||||||
|
@session_filters ||= load_session_filters(specific_org:) |
||||||
|
end |
||||||
|
|
||||||
|
def set_session_filters |
||||||
|
session[:logs_filters] = @session_filters.to_json |
||||||
|
end |
||||||
|
end |
@ -1,13 +1,9 @@ |
|||||||
module Modules::SearchFilter |
module Modules::SearchFilter |
||||||
def filtered_collection(base_collection, search_term = nil) |
def filtered_collection(base_collection, search_term = nil) |
||||||
if search_term.present? |
FilterService.filter_by_search(base_collection, search_term) |
||||||
base_collection.search_by(search_term) |
|
||||||
else |
|
||||||
base_collection |
|
||||||
end |
|
||||||
end |
end |
||||||
|
|
||||||
def filtered_users(base_collection, search_term = nil) |
def filtered_users(base_collection, search_term = nil) |
||||||
filtered_collection(base_collection, search_term).includes(:organisation) |
FilterService.filter_by_search(base_collection, search_term).includes(:organisation) |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -0,0 +1,46 @@ |
|||||||
|
class SalesLogsController < LogsController |
||||||
|
before_action :session_filters, if: :current_user |
||||||
|
before_action :set_session_filters, if: :current_user |
||||||
|
|
||||||
|
def create |
||||||
|
super { SalesLog.new(log_params) } |
||||||
|
end |
||||||
|
|
||||||
|
def index |
||||||
|
respond_to do |format| |
||||||
|
format.html do |
||||||
|
all_logs = current_user.sales_logs |
||||||
|
unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters) |
||||||
|
|
||||||
|
@search_term = search_term |
||||||
|
@pagy, @logs = pagy(unpaginated_filtered_logs) |
||||||
|
@searched = search_term.presence |
||||||
|
@total_count = all_logs.size |
||||||
|
render "logs/index" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def show |
||||||
|
respond_to do |format| |
||||||
|
format.html { edit } |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def edit |
||||||
|
@log = current_user.sales_logs.find_by(id: params[:id]) |
||||||
|
if @log |
||||||
|
render "logs/edit", locals: { current_user: } |
||||||
|
else |
||||||
|
render_not_found |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def post_create_redirect_url(log) |
||||||
|
sales_log_url(log) |
||||||
|
end |
||||||
|
|
||||||
|
def permitted_log_params |
||||||
|
params.require(:sales_log).permit(SalesLog.editable_fields) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,21 @@ |
|||||||
|
class EmailCsvJob < ApplicationJob |
||||||
|
queue_as :default |
||||||
|
|
||||||
|
BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 |
||||||
|
|
||||||
|
EXPIRATION_TIME = 3.hours.to_i |
||||||
|
|
||||||
|
def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil) # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params |
||||||
|
unfiltered_logs = organisation.present? && user.support? ? LettingsLog.where(owning_organisation_id: organisation.id) : user.lettings_logs |
||||||
|
filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user) |
||||||
|
|
||||||
|
filename = organisation.present? ? "logs-#{organisation.name}-#{Time.zone.now}.csv" : "logs-#{Time.zone.now}.csv" |
||||||
|
|
||||||
|
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) |
||||||
|
storage_service.write_file(filename, BYTE_ORDER_MARK + filtered_logs.to_csv(user)) |
||||||
|
|
||||||
|
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME) |
||||||
|
|
||||||
|
CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,11 @@ |
|||||||
|
class CsvDownloadMailer < NotifyMailer |
||||||
|
CSV_DOWNLOAD_TEMPLATE_ID = "7890e3b9-8c0d-4d08-bafe-427fd7cd95bf".freeze |
||||||
|
|
||||||
|
def send_csv_download_mail(user, link, duration) |
||||||
|
send_email( |
||||||
|
user.email, |
||||||
|
CSV_DOWNLOAD_TEMPLATE_ID, |
||||||
|
{ name: user.name, link:, duration: ActiveSupport::Duration.build(duration).inspect }, |
||||||
|
) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,37 @@ |
|||||||
|
class NotifyMailer |
||||||
|
require "notifications/client" |
||||||
|
|
||||||
|
def notify_client |
||||||
|
@notify_client ||= ::Notifications::Client.new(ENV["GOVUK_NOTIFY_API_KEY"]) |
||||||
|
end |
||||||
|
|
||||||
|
def send_email(email, template_id, personalisation) |
||||||
|
return true if intercept_send?(email) |
||||||
|
|
||||||
|
notify_client.send_email( |
||||||
|
email_address: email, |
||||||
|
template_id:, |
||||||
|
personalisation:, |
||||||
|
) |
||||||
|
end |
||||||
|
|
||||||
|
def personalisation(record, token, url, username: false) |
||||||
|
{ |
||||||
|
name: record.name || record.email, |
||||||
|
email: username || record.email, |
||||||
|
organisation: record.respond_to?(:organisation) ? record.organisation.name : "", |
||||||
|
link: "#{url}#{token}", |
||||||
|
} |
||||||
|
end |
||||||
|
|
||||||
|
def intercept_send?(email) |
||||||
|
return false unless email_allowlist |
||||||
|
|
||||||
|
email_domain = email.split("@").last.downcase |
||||||
|
!(Rails.env.production? || Rails.env.test?) && email_allowlist.exclude?(email_domain) |
||||||
|
end |
||||||
|
|
||||||
|
def email_allowlist |
||||||
|
Rails.application.credentials[:email_allowlist] |
||||||
|
end |
||||||
|
end |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::IrproductOther < ::Form::Question |
class Form::Lettings::Questions::IrproductOther < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "irproduct_other" |
@id = "irproduct_other" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::LocationId < ::Form::Question |
class Form::Lettings::Questions::LocationId < ::Form::Question |
||||||
def initialize(_id, hsh, page) |
def initialize(_id, hsh, page) |
||||||
super("location_id", hsh, page) |
super("location_id", hsh, page) |
||||||
@check_answer_label = "Location" |
@check_answer_label = "Location" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::NeedsType < ::Form::Question |
class Form::Lettings::Questions::NeedsType < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "needstype" |
@id = "needstype" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::PropertyReference < ::Form::Question |
class Form::Lettings::Questions::PropertyReference < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "propcode" |
@id = "propcode" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::Renewal < ::Form::Question |
class Form::Lettings::Questions::Renewal < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "renewal" |
@id = "renewal" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::RentType < ::Form::Question |
class Form::Lettings::Questions::RentType < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "rent_type" |
@id = "rent_type" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::SchemeId < ::Form::Question |
class Form::Lettings::Questions::SchemeId < ::Form::Question |
||||||
def initialize(_id, hsh, page) |
def initialize(_id, hsh, page) |
||||||
super("scheme_id", hsh, page) |
super("scheme_id", hsh, page) |
||||||
@check_answer_label = "Scheme name" |
@check_answer_label = "Scheme name" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::TenancyStartDate < ::Form::Question |
class Form::Lettings::Questions::TenancyStartDate < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "startdate" |
@id = "startdate" |
@ -1,4 +1,4 @@ |
|||||||
class Form::Setup::Questions::TenantCode < ::Form::Question |
class Form::Lettings::Questions::TenantCode < ::Form::Question |
||||||
def initialize(id, hsh, page) |
def initialize(id, hsh, page) |
||||||
super |
super |
||||||
@id = "tenancycode" |
@id = "tenancycode" |
@ -1,10 +1,10 @@ |
|||||||
class Form::Sections::Setup < ::Form::Section |
class Form::Lettings::Sections::Setup < ::Form::Section |
||||||
def initialize(id, hsh, form) |
def initialize(id, hsh, form) |
||||||
super |
super |
||||||
@id = "setup" |
@id = "setup" |
||||||
@label = "Before you start" |
@label = "Before you start" |
||||||
@description = "" |
@description = "" |
||||||
@form = form |
@form = form |
||||||
@subsections = [Form::Setup::Subsections::Setup.new(nil, nil, self)] |
@subsections = [Form::Lettings::Subsections::Setup.new(nil, nil, self)] |
||||||
end |
end |
||||||
end |
end |
@ -0,0 +1,37 @@ |
|||||||
|
class Form::Lettings::Subsections::Setup < ::Form::Subsection |
||||||
|
def initialize(id, hsh, section) |
||||||
|
super |
||||||
|
@id = "setup" |
||||||
|
@label = "Set up this lettings log" |
||||||
|
@section = section |
||||||
|
end |
||||||
|
|
||||||
|
def pages |
||||||
|
@pages ||= [ |
||||||
|
Form::Common::Pages::Organisation.new(nil, nil, self), |
||||||
|
Form::Common::Pages::CreatedBy.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::NeedsType.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::Scheme.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::Location.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::Renewal.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::TenancyStartDate.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::RentType.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::TenantCode.new(nil, nil, self), |
||||||
|
Form::Lettings::Pages::PropertyReference.new(nil, nil, self), |
||||||
|
] |
||||||
|
end |
||||||
|
|
||||||
|
def applicable_questions(lettings_log) |
||||||
|
questions.select { |q| support_only_questions.include?(q.id) } + super |
||||||
|
end |
||||||
|
|
||||||
|
def enabled?(_lettings_log) |
||||||
|
true |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def support_only_questions |
||||||
|
%w[owning_organisation_id created_by_id].freeze |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,15 @@ |
|||||||
|
class Form::Sales::Pages::PurchaserCode < ::Form::Page |
||||||
|
def initialize(id, hsh, subsection) |
||||||
|
super |
||||||
|
@id = "purchaser_code" |
||||||
|
@header = "" |
||||||
|
@description = "" |
||||||
|
@subsection = subsection |
||||||
|
end |
||||||
|
|
||||||
|
def questions |
||||||
|
@questions ||= [ |
||||||
|
Form::Sales::Questions::PurchaserCode.new(nil, nil, self), |
||||||
|
] |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,15 @@ |
|||||||
|
class Form::Sales::Pages::SaleDate < ::Form::Page |
||||||
|
def initialize(id, hsh, subsection) |
||||||
|
super |
||||||
|
@id = "completion_date" |
||||||
|
@header = "" |
||||||
|
@description = "" |
||||||
|
@subsection = subsection |
||||||
|
end |
||||||
|
|
||||||
|
def questions |
||||||
|
@questions ||= [ |
||||||
|
Form::Sales::Questions::SaleDate.new(nil, nil, self), |
||||||
|
] |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,18 @@ |
|||||||
|
class Form::Sales::Pages::SharedOwnershipType < ::Form::Page |
||||||
|
def initialize(id, hsh, subsection) |
||||||
|
super |
||||||
|
@id = "shared_ownership_type" |
||||||
|
@header = "" |
||||||
|
@description = "" |
||||||
|
@subsection = subsection |
||||||
|
@depends_on = [{ |
||||||
|
"ownershipsch" => 1, |
||||||
|
}] |
||||||
|
end |
||||||
|
|
||||||
|
def questions |
||||||
|
@questions ||= [ |
||||||
|
Form::Sales::Questions::SharedOwnershipType.new(nil, nil, self), |
||||||
|
] |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,12 @@ |
|||||||
|
class Form::Sales::Questions::PurchaserCode < ::Form::Question |
||||||
|
def initialize(id, hsh, page) |
||||||
|
super |
||||||
|
@id = "purchid" |
||||||
|
@check_answer_label = "Purchaser code" |
||||||
|
@header = "What is the purchaser code?" |
||||||
|
@hint_text = "This is how you usually refer to the purchaser on your own systems." |
||||||
|
@type = "text" |
||||||
|
@width = 10 |
||||||
|
@page = page |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,10 @@ |
|||||||
|
class Form::Sales::Questions::SaleDate < ::Form::Question |
||||||
|
def initialize(id, hsh, page) |
||||||
|
super |
||||||
|
@id = "saledate" |
||||||
|
@check_answer_label = "Sale completion date" |
||||||
|
@header = "What is the sale completion date?" |
||||||
|
@type = "date" |
||||||
|
@page = page |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,22 @@ |
|||||||
|
class Form::Sales::Questions::SharedOwnershipType < ::Form::Question |
||||||
|
def initialize(id, hsh, page) |
||||||
|
super |
||||||
|
@id = "type" |
||||||
|
@check_answer_label = "Type of shared ownership sale" |
||||||
|
@header = "What is the type of shared ownership sale?" |
||||||
|
@hint_text = "A shared ownership sale is when the purchaser buys up to 75% of the property value and pays rent to the Private Registered Provider (PRP) on the remaining portion" |
||||||
|
@type = "radio" |
||||||
|
@answer_options = ANSWER_OPTIONS |
||||||
|
@page = page |
||||||
|
end |
||||||
|
|
||||||
|
ANSWER_OPTIONS = { |
||||||
|
"2" => { "value" => "Shared Ownership" }, |
||||||
|
"24" => { "value" => "Old Persons Shared Ownership" }, |
||||||
|
"18" => { "value" => "Social HomeBuy (shared ownership purchase)" }, |
||||||
|
"16" => { "value" => "Home Ownership for people with Long Term Disabilities (HOLD)" }, |
||||||
|
"28" => { "value" => "Rent to Buy - Shared Ownership" }, |
||||||
|
"31" => { "value" => "Right to Shared Ownership" }, |
||||||
|
"30" => { "value" => "Shared Ownership - 2021 model lease" }, |
||||||
|
}.freeze |
||||||
|
end |
@ -0,0 +1,10 @@ |
|||||||
|
class Form::Sales::Sections::Setup < ::Form::Section |
||||||
|
def initialize(id, hsh, form) |
||||||
|
super |
||||||
|
@id = "setup" |
||||||
|
@label = "Before you start" |
||||||
|
@description = "" |
||||||
|
@form = form |
||||||
|
@subsections = [Form::Sales::Subsections::Setup.new(nil, nil, self)] || [] |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,18 @@ |
|||||||
|
class Form::Sales::Subsections::Setup < ::Form::Subsection |
||||||
|
def initialize(id, hsh, section) |
||||||
|
super |
||||||
|
@id = "setup" |
||||||
|
@label = "Set up this sales log" |
||||||
|
@section = section |
||||||
|
end |
||||||
|
|
||||||
|
def pages |
||||||
|
@pages ||= [ |
||||||
|
Form::Common::Pages::Organisation.new(nil, nil, self), |
||||||
|
Form::Common::Pages::CreatedBy.new(nil, nil, self), |
||||||
|
Form::Sales::Pages::SaleDate.new(nil, nil, self), |
||||||
|
Form::Sales::Pages::PurchaserCode.new(nil, nil, self), |
||||||
|
Form::Sales::Pages::SharedOwnershipType.new(nil, nil, self), |
||||||
|
] |
||||||
|
end |
||||||
|
end |
@ -1,37 +0,0 @@ |
|||||||
class Form::Subsections::Setup < ::Form::Subsection |
|
||||||
def initialize(id, hsh, section) |
|
||||||
super |
|
||||||
@id = "setup" |
|
||||||
@label = "Set up this lettings log" |
|
||||||
@section = section |
|
||||||
end |
|
||||||
|
|
||||||
def pages |
|
||||||
@pages ||= [ |
|
||||||
Form::Setup::Pages::Organisation.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::CreatedBy.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::NeedsType.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::Scheme.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::Location.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::Renewal.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::TenancyStartDate.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::RentType.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::TenantCode.new(nil, nil, self), |
|
||||||
Form::Setup::Pages::PropertyReference.new(nil, nil, self), |
|
||||||
] |
|
||||||
end |
|
||||||
|
|
||||||
def applicable_questions(lettings_log) |
|
||||||
questions.select { |q| support_only_questions.include?(q.id) } + super |
|
||||||
end |
|
||||||
|
|
||||||
def enabled?(_lettings_log) |
|
||||||
true |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def support_only_questions |
|
||||||
%w[owning_organisation_id created_by_id].freeze |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,73 @@ |
|||||||
|
class Log < ApplicationRecord |
||||||
|
self.abstract_class = true |
||||||
|
|
||||||
|
belongs_to :owning_organisation, class_name: "Organisation", optional: true |
||||||
|
belongs_to :managing_organisation, class_name: "Organisation", optional: true |
||||||
|
belongs_to :created_by, class_name: "User", optional: true |
||||||
|
before_save :update_status! |
||||||
|
|
||||||
|
STATUS = { "not_started" => 0, "in_progress" => 1, "completed" => 2 }.freeze |
||||||
|
enum status: STATUS |
||||||
|
|
||||||
|
scope :filter_by_organisation, ->(org, _user = nil) { where(owning_organisation: org).or(where(managing_organisation: org)) } |
||||||
|
scope :filter_by_status, ->(status, _user = nil) { where status: } |
||||||
|
scope :filter_by_years, lambda { |years, _user = nil| |
||||||
|
first_year = years.shift |
||||||
|
query = filter_by_year(first_year) |
||||||
|
years.each { |year| query = query.or(filter_by_year(year)) } |
||||||
|
query.all |
||||||
|
} |
||||||
|
scope :filter_by_id, ->(id) { where(id:) } |
||||||
|
scope :filter_by_user, lambda { |selected_user, user| |
||||||
|
if !selected_user.include?("all") && user.present? |
||||||
|
where(created_by: user) |
||||||
|
end |
||||||
|
} |
||||||
|
|
||||||
|
def collection_start_year |
||||||
|
return @start_year if @start_year |
||||||
|
return unless startdate |
||||||
|
|
||||||
|
window_end_date = Time.zone.local(startdate.year, 4, 1) |
||||||
|
@start_year = startdate < window_end_date ? startdate.year - 1 : startdate.year |
||||||
|
end |
||||||
|
|
||||||
|
def lettings? |
||||||
|
false |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
def update_status! |
||||||
|
self.status = if all_fields_completed? && errors.empty? |
||||||
|
"completed" |
||||||
|
elsif all_fields_nil? |
||||||
|
"not_started" |
||||||
|
else |
||||||
|
"in_progress" |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def all_fields_completed? |
||||||
|
subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq |
||||||
|
subsection_statuses == [:completed] |
||||||
|
end |
||||||
|
|
||||||
|
def all_fields_nil? |
||||||
|
not_started_statuses = %i[not_started cannot_start_yet] |
||||||
|
subsection_statuses = form.subsections.map { |subsection| subsection.status(self) }.uniq |
||||||
|
subsection_statuses.all? { |status| not_started_statuses.include?(status) } |
||||||
|
end |
||||||
|
|
||||||
|
def reset_created_by |
||||||
|
return unless created_by && owning_organisation |
||||||
|
|
||||||
|
self.created_by = nil if created_by.organisation != owning_organisation |
||||||
|
end |
||||||
|
|
||||||
|
def reset_invalidated_dependent_fields! |
||||||
|
return unless form |
||||||
|
|
||||||
|
form.reset_not_routed_questions(self) |
||||||
|
end |
||||||
|
end |
@ -1,5 +1,5 @@ |
|||||||
class RentPeriod |
class RentPeriod |
||||||
def self.rent_period_mappings |
def self.rent_period_mappings |
||||||
FormHandler.instance.current_form.get_question("period", nil).answer_options |
FormHandler.instance.current_lettings_form.get_question("period", nil).answer_options |
||||||
end |
end |
||||||
end |
end |
||||||
|
@ -0,0 +1,45 @@ |
|||||||
|
class SalesLogValidator < ActiveModel::Validator |
||||||
|
def validate(record); end |
||||||
|
end |
||||||
|
|
||||||
|
class SalesLog < Log |
||||||
|
self.inheritance_column = :_type_disabled |
||||||
|
has_paper_trail |
||||||
|
|
||||||
|
validates_with SalesLogValidator |
||||||
|
|
||||||
|
scope :filter_by_year, ->(year) { where(saledate: Time.zone.local(year.to_i, 4, 1)...Time.zone.local(year.to_i + 1, 4, 1)) } |
||||||
|
scope :search_by, ->(param) { filter_by_id(param) } |
||||||
|
|
||||||
|
OPTIONAL_FIELDS = [].freeze |
||||||
|
|
||||||
|
def startdate |
||||||
|
saledate |
||||||
|
end |
||||||
|
|
||||||
|
def self.editable_fields |
||||||
|
attribute_names |
||||||
|
end |
||||||
|
|
||||||
|
def form_name |
||||||
|
return unless saledate |
||||||
|
|
||||||
|
FormHandler.instance.form_name_from_start_year(collection_start_year, "sales") |
||||||
|
end |
||||||
|
|
||||||
|
def form |
||||||
|
FormHandler.instance.get_form(form_name) || FormHandler.instance.current_sales_form |
||||||
|
end |
||||||
|
|
||||||
|
def optional_fields |
||||||
|
[] |
||||||
|
end |
||||||
|
|
||||||
|
def not_started? |
||||||
|
status == "not_started" |
||||||
|
end |
||||||
|
|
||||||
|
def completed? |
||||||
|
status == "completed" |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,22 @@ |
|||||||
|
class FilterService |
||||||
|
def self.filter_by_search(base_collection, search_term = nil) |
||||||
|
if search_term.present? |
||||||
|
base_collection.search_by(search_term) |
||||||
|
else |
||||||
|
base_collection |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def self.filter_logs(logs, search_term, filters, all_orgs, user) |
||||||
|
logs = filter_by_search(logs, search_term) |
||||||
|
|
||||||
|
filters.each do |category, values| |
||||||
|
next if Array(values).reject(&:empty?).blank? |
||||||
|
next if category == "organisation" && all_orgs |
||||||
|
|
||||||
|
logs = logs.public_send("filter_by_#{category}", values, user) |
||||||
|
end |
||||||
|
logs = logs.order(created_at: :desc) |
||||||
|
user.support? ? logs.all.includes(:owning_organisation, :managing_organisation) : logs |
||||||
|
end |
||||||
|
end |
@ -1,8 +1,7 @@ |
|||||||
<h2 class="govuk-body"> |
<h2 class="govuk-body"> |
||||||
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> |
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "logs", path: request.path)) %> |
||||||
<%= govuk_link_to "Download (CSV)", "#{request.path}.csv", type: "text/csv" %> |
<%= govuk_link_to "Download (CSV)", csv_download_url, type: "text/csv" %> |
||||||
</h2> |
</h2> |
||||||
|
<% logs.map do |log| %> |
||||||
<% lettings_logs.map do |log| %> |
|
||||||
<%= render(LogSummaryComponent.new(current_user:, log:)) %> |
<%= render(LogSummaryComponent.new(current_user:, log:)) %> |
||||||
<% end %> |
<% end %> |
@ -0,0 +1,15 @@ |
|||||||
|
<% content_for :title, "We’re sending you an email" %> |
||||||
|
<div class="govuk-grid-row"> |
||||||
|
<div class="govuk-grid-column-two-thirds"> |
||||||
|
<%= govuk_panel(title_text: "We’re sending you an email") %> |
||||||
|
|
||||||
|
<p class="govuk-body">It should arrive in a few minutes, but it could take longer.</p> |
||||||
|
|
||||||
|
<h2 class="govuk-heading-m">What happens next</h2> |
||||||
|
<p class="govuk-body">Open your email inbox and click the link to download your CSV file.</p> |
||||||
|
|
||||||
|
<p class="govuk-body"> |
||||||
|
<%= govuk_link_to "Return to logs", lettings_logs_path %> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,16 @@ |
|||||||
|
<% content_for :title, "Download CSV" %> |
||||||
|
|
||||||
|
<% content_for :before_content do %> |
||||||
|
<%= govuk_back_link(href: :back) %> |
||||||
|
<% end %> |
||||||
|
|
||||||
|
<div class="govuk-grid-row"> |
||||||
|
<div class="govuk-grid-column-two-thirds"> |
||||||
|
<h1 class="govuk-heading-l">Download CSV</h2> |
||||||
|
|
||||||
|
<p class="govuk-body">We'll send a secure download link to your email address <strong><%= @current_user.email %></strong>.</p> |
||||||
|
<p class="govuk-body">You've selected <%= count %> logs.</p> |
||||||
|
|
||||||
|
<%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term } %> |
||||||
|
</div> |
||||||
|
</div> |
@ -0,0 +1,11 @@ |
|||||||
|
if Rails.env.staging? || Rails.env.production? |
||||||
|
redis_url = Configuration::PaasConfigurationService.new.redis_uris[:"dluhc-core-#{Rails.env}-redis"] |
||||||
|
|
||||||
|
Sidekiq.configure_server do |config| |
||||||
|
config.redis = { url: redis_url } |
||||||
|
end |
||||||
|
|
||||||
|
Sidekiq.configure_client do |config| |
||||||
|
config.redis = { url: redis_url } |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,12 @@ |
|||||||
|
class AddSalesLog < ActiveRecord::Migration[7.0] |
||||||
|
def change |
||||||
|
create_table :sales_logs do |t| |
||||||
|
t.integer :status, default: 0 |
||||||
|
t.datetime :saledate |
||||||
|
t.timestamps |
||||||
|
t.references :owning_organisation, class_name: "Organisation", foreign_key: { to_table: :organisations, on_delete: :cascade } |
||||||
|
t.references :managing_organisation, class_name: "Organisation" |
||||||
|
t.references :created_by, class_name: "User" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,5 @@ |
|||||||
|
class AddPurchid < ActiveRecord::Migration[7.0] |
||||||
|
def change |
||||||
|
add_column :sales_logs, :purchid, :string |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,5 @@ |
|||||||
|
class AddSharedOwnershipType < ActiveRecord::Migration[7.0] |
||||||
|
def change |
||||||
|
add_column :sales_logs, :type, :integer |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,5 @@ |
|||||||
|
class AddOwnershipSchemeToSalesLog < ActiveRecord::Migration[7.0] |
||||||
|
def change |
||||||
|
add_column :sales_logs, :ownershipsch, :integer |
||||||
|
end |
||||||
|
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue