Skip to content

Commit

Permalink
GL-364: Add model for exemption overrides, possibly should 'relate' t…
Browse files Browse the repository at this point in the history
…o certificates model (#1844)

* GL-364: Add model for exemption overrides, possibly should 'relate' to certificates model

* GL-366: Add admin APIs to list out a page of overrides

* GL-365: Use exempting_certificate_override table to control whether a certificate is treated as an exemption (#1864)
  • Loading branch information
rasikasri authored May 14, 2024
1 parent c55c4b3 commit 44f3680
Show file tree
Hide file tree
Showing 21 changed files with 405 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Admin
module GreenLanes
class CategoryAssessmentsController < AdminController
include Pageable
include XiOnly

before_action :check_service, :authenticate_user!

Expand Down Expand Up @@ -75,12 +76,6 @@ def serialize(*args)
def serialize_errors(category_assessment)
Api::Admin::ErrorSerializationService.new(category_assessment).call
end

def check_service
if TradeTariffBackend.uk?
raise ActionController::RoutingError, 'Invalid service'
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module Api
module Admin
module GreenLanes
class ExemptingCertificateOverridesController < AdminController
include Pageable
include XiOnly

before_action :check_service, :authenticate_user!

def index
render json: serialize(exempting_certificate_override.to_a, pagination_meta)
end

def show
eco = ::GreenLanes::ExemptingCertificateOverride.with_pk!(params[:id])
render json: serialize(eco)
end

def create
eco = ::GreenLanes::ExemptingCertificateOverride.new(eco_params)

if eco.valid? && eco.save
render json: serialize(eco),
location: api_admin_green_lanes_exempting_certificate_override_url(eco.id),
status: :created
else
render json: serialize_errors(eco),
status: :unprocessable_entity
end
end

def destroy
eco = ::GreenLanes::ExemptingCertificateOverride.with_pk!(params[:id])
eco.destroy

head :no_content
end

private

def eco_params
params.require(:data).require(:attributes).permit(
:certificate_type_code,
:certificate_code,
)
end

def record_count
@exempting_certificate_override.pagination_record_count
end

def exempting_certificate_override
@exempting_certificate_override ||= ::GreenLanes::ExemptingCertificateOverride.order(Sequel.asc(:certificate_type_code), Sequel.asc(:certificate_code)).paginate(current_page, per_page)
end

def serialize(*args)
Api::Admin::GreenLanes::ExemptingCertificateOverrideSerializer.new(*args).serializable_hash
end

def serialize_errors(exempting_certificate_override)
Api::Admin::ErrorSerializationService.new(exempting_certificate_override).call
end
end
end
end
end
8 changes: 2 additions & 6 deletions app/controllers/api/admin/green_lanes/themes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Api
module Admin
module GreenLanes
class ThemesController < AdminController
include XiOnly

before_action :check_service, :authenticate_user!

def index
Expand All @@ -17,12 +19,6 @@ def themes
def serialize(*args)
Api::Admin::GreenLanes::ThemeSerializer.new(*args).serializable_hash
end

def check_service
if TradeTariffBackend.uk?
raise ActionController::RoutingError, 'Invalid service'
end
end
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions app/controllers/concerns/xi_only.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module XiOnly
extend ActiveSupport::Concern

included do
def check_service
if TradeTariffBackend.uk?
raise ActionController::RoutingError, 'Invalid service'
end
end
end
end
6 changes: 6 additions & 0 deletions app/models/certificate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ class Certificate < Sequel::Model
ds.with_actual(CertificateType)
end

one_to_one :exempting_certificate_override,
class: 'ExemptingCertificateOverride',
class_namespace: 'GreenLanes',
primary_key: %i[certificate_type_code certificate_code],
key: %i[certificate_type_code certificate_code]

def special_nature?
certificate_type_code.in?(SPECIAL_NATURE_TYPE_CODE)
end
Expand Down
12 changes: 12 additions & 0 deletions app/models/green_lanes/exempting_certificate_override.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module GreenLanes
class ExemptingCertificateOverride < Sequel::Model(:green_lanes_exempting_certificate_overrides)
plugin :timestamps, update_on_create: true
plugin :auto_validations, not_null: :presence

one_to_one :certificate, class: :Certificate,
key: %i[certificate_type_code certificate_code],
primary_key: %i[certificate_type_code certificate_code] do |ds|
ds.with_actual(Certificate)
end
end
end
8 changes: 8 additions & 0 deletions app/models/measure_condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ def is_excluded_condition?
document_code.blank?
end

def is_exempting_with_certificate_overridden?
(exemption_class? && !is_exempting_certificate_overridden?) || (!exemption_class? && is_exempting_certificate_overridden?)
end

private

def is_threshold?
Expand Down Expand Up @@ -217,4 +221,8 @@ def is_percentage_abv_condition?
def is_eps_condition?
entry_price_system? && is_price_condition? && is_weight_condition?
end

def is_exempting_certificate_overridden?
certificate.present? && certificate.exempting_certificate_override.present?
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def exemptions
end

def certificates
measure_conditions.select(&:exemption_class?).map(&:certificate)
measure_conditions.select(&:is_exempting_with_certificate_overridden?).map(&:certificate)
end

def additional_codes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Api
module Admin
module GreenLanes
class ExemptingCertificateOverrideSerializer
include JSONAPI::Serializer

set_type :exempting_certificate_override

set_id :id

attributes :certificate_type_code,
:certificate_code,
:created_at,
:updated_at
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class FetchGoodsNomenclatureService
geographical_area: %i[geographical_area_descriptions contained_geographical_areas],
measure_excluded_geographical_areas: [],
excluded_geographical_areas: :geographical_area_descriptions,
measure_conditions: { certificate: :certificate_descriptions },
measure_conditions: { certificate: %i[certificate_descriptions exempting_certificate_override] },
category_assessment: (%i[theme base_regulation modification_regulation] +
[{ measure_type: :measure_type_description }]),
}.freeze
Expand Down
2 changes: 1 addition & 1 deletion app/services/green_lanes/permutation_calculator_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def permutation_key(measure)
measure.measure_excluded_geographical_areas.map(&:excluded_geographical_area).sort.uniq.join('|'),
measure.additional_code_type_id,
measure.additional_code_id,
measure.measure_conditions.select(&:exemption_class?).map(&:document_code).sort.uniq.join('|'),
measure.measure_conditions.select(&:is_exempting_with_certificate_overridden?).map(&:document_code).sort.uniq.join('|'),
]
end
end
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
namespace :green_lanes do
resources :category_assessments, only: %i[index show create update destroy]
resources :themes, only: %i[index]
resources :exempting_certificate_overrides, only: %i[index show create destroy]
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/factories/certificate_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,22 @@
factory :certificate do
transient do
description { Forgery(:basic).text }
exempting_certificate_override { false }
end

certificate_type_code { generate(:certificate_type_code) }
certificate_code { Forgery(:basic).text(exactly: 3) }
validity_start_date { 2.years.ago.beginning_of_day }
validity_end_date { nil }

after(:build) do |certificate, evaluator|
if evaluator.exempting_certificate_override
create(:exempting_certificate_override,
certificate_type_code: certificate.certificate_type_code,
certificate_code: certificate.certificate_code)
end
end

trait :with_description do
after(:create) do |certificate, _evaluator|
create(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FactoryBot.define do
factory :exempting_certificate_override, class: 'GreenLanes::ExemptingCertificateOverride' do
transient do
certificate {}
end

certificate_code { certificate.try(:certificate_code) || Forgery(:basic).text(exactly: 3) }
certificate_type_code { certificate.try(:certificate_type_code) || Forgery(:basic).text(exactly: 1) }
end
end
2 changes: 2 additions & 0 deletions spec/factories/measure_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@
condition_code { 'B' }
certificate_type_code { nil }
certificate_code { nil }
exempting_certificate_override { false }
end

after(:build) do |measure, evaluator|
Expand Down Expand Up @@ -415,6 +416,7 @@
:certificate,
certificate_type_code: evaluator.certificate_type_code,
certificate_code: evaluator.certificate_code,
exempting_certificate_override: evaluator.exempting_certificate_override,
)
end
end
Expand Down
21 changes: 21 additions & 0 deletions spec/models/certificate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,25 @@
it { is_expected.not_to be_authorised_use }
end
end

describe '#exempting_certificate_override' do
subject { certificate.reload.exempting_certificate_override }

let(:certificate) { create :certificate }

context 'with matching exempting_certificate_override' do
before { exempting_certificate_override }

let :exempting_certificate_override do
create :exempting_certificate_override, certificate_type_code: certificate.certificate_type_code,
certificate_code: certificate.certificate_code
end

it { is_expected.to eq_pk exempting_certificate_override }
end

context 'without matching exempting_certificate_override' do
it { is_expected.to be_nil }
end
end
end
47 changes: 47 additions & 0 deletions spec/models/green_lanes/exempting_certificate_override_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
RSpec.describe GreenLanes::ExemptingCertificateOverride do
describe 'attributes' do
it { is_expected.to respond_to :id }
it { is_expected.to respond_to :certificate_type_code }
it { is_expected.to respond_to :certificate_code }
it { is_expected.to respond_to :created_at }
it { is_expected.to respond_to :updated_at }
end

describe 'validations' do
subject(:errors) { instance.tap(&:valid?).errors }

let(:instance) { described_class.new }

it { is_expected.to include certificate_type_code: ['is not present'] }
it { is_expected.to include certificate_code: ['is not present'] }

context 'with duplicate certificate_type_code and certificate_code' do
let(:existing) { create :exempting_certificate_override }

let :instance do
described_class.new certificate_type_code: existing.certificate_type_code,
certificate_code: existing.certificate_code
end

it { is_expected.to include %i[certificate_code certificate_type_code] => ['is already taken'] }
end
end

describe 'date fields' do
subject { create(:exempting_certificate_override).reload }

it { is_expected.to have_attributes created_at: be_within(1.minute).of(Time.zone.now) }
it { is_expected.to have_attributes updated_at: be_within(1.minute).of(Time.zone.now) }
end

describe 'associations' do
describe '#certificate' do
subject { exempting_certificate_override.reload.certificate }

let(:exempting_certificate_override) { create :exempting_certificate_override, certificate: }
let(:certificate) { create :certificate }

it { is_expected.to eq certificate }
end
end
end
36 changes: 36 additions & 0 deletions spec/models/measure_condition_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,4 +295,40 @@
it { expect(measure_condition.threshold_unit_type).to eq nil }
end
end

describe '#is_exempting_with_certificate_overridden?' do
context 'when the condition is exemption class and certificate has not been overridden' do
subject(:measure_condition) { create :measure_condition, certificate: }

let(:certificate) { create(:certificate, certificate_code: '005', certificate_type_code: 'Y') }

it { is_expected.to be_is_exempting_with_certificate_overridden }
end

context 'when the condition is exemption class and certificate has been overridden' do
subject(:measure_condition) { create :measure_condition, certificate: }

let(:certificate) { create(:certificate, certificate_code: '005', certificate_type_code: 'Y', exempting_certificate_override:) }
let(:exempting_certificate_override) { create :exempting_certificate_override }

it { is_expected.not_to be_is_exempting_with_certificate_overridden }
end

context 'when the condition is not exemption class and certificate has not been overridden' do
subject(:measure_condition) { create :measure_condition, certificate: }

let(:certificate) { create(:certificate, certificate_code: '005', certificate_type_code: 'C') }

it { is_expected.not_to be_is_exempting_with_certificate_overridden }
end

context 'when the condition is not exemption class and certificate has been overridden' do
subject(:measure_condition) { create :measure_condition, certificate: }

let(:certificate) { create(:certificate, certificate_code: '005', certificate_type_code: 'C', exempting_certificate_override:) }
let(:exempting_certificate_override) { create :exempting_certificate_override }

it { is_expected.to be_is_exempting_with_certificate_overridden }
end
end
end
Loading

0 comments on commit 44f3680

Please sign in to comment.