mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-11 18:26:50 +00:00
Fix unique validator for vouche code
Paranoia doesn't support unique validation including deleted records: https://github.com/rubysherpas/paranoia/pull/333 We use a custom validator, ScopedUniquenessValidator to avoid the issue
This commit is contained in:
committed by
Rachel Arnould
parent
9ab2a3ae3d
commit
73819a4638
@@ -4,6 +4,6 @@ module Vouchers
|
||||
class FlatRate < Voucher
|
||||
include FlatRatable
|
||||
|
||||
validates :code, uniqueness: { scope: :enterprise_id }
|
||||
validates_with ScopedUniquenessValidator
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ module Vouchers
|
||||
validates :amount,
|
||||
presence: true,
|
||||
numericality: { greater_than: 0, less_than_or_equal_to: 100 }
|
||||
validates :code, uniqueness: { scope: :enterprise_id }
|
||||
validates_with ScopedUniquenessValidator
|
||||
|
||||
def display_value
|
||||
ActionController::Base.helpers.number_to_percentage(amount, precision: 2)
|
||||
|
||||
25
app/validators/vouchers/scoped_uniqueness_validator.rb
Normal file
25
app/validators/vouchers/scoped_uniqueness_validator.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
# paranoia doesn't support unique validation including deleted records:
|
||||
# https://github.com/rubysherpas/paranoia/pull/333
|
||||
# We use a custom validator to fix the issue, so we don't need to fork/patch the gem
|
||||
module Vouchers
|
||||
class ScopedUniquenessValidator < ActiveModel::Validator
|
||||
def validate(record)
|
||||
@record = record
|
||||
|
||||
return unless unique_voucher_code_per_enterprise?
|
||||
|
||||
record.errors.add :code, :taken, value: @record.code
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unique_voucher_code_per_enterprise?
|
||||
query = Voucher.with_deleted.where(code: @record.code, enterprise_id: @record.enterprise_id)
|
||||
query = query.where.not(id: @record.id) unless @record.id.nil?
|
||||
|
||||
query.present?
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,7 +8,7 @@ RSpec.describe Vouchers::FlatRate do
|
||||
|
||||
it { is_expected.to validate_presence_of(:amount) }
|
||||
it { is_expected.to validate_numericality_of(:amount).is_greater_than(0) }
|
||||
it { is_expected.to validate_uniqueness_of(:code).scoped_to(:enterprise_id) }
|
||||
it_behaves_like 'has a unique code per enterprise', "voucher_flat_rate"
|
||||
end
|
||||
|
||||
describe '#compute_amount' do
|
||||
|
||||
@@ -12,7 +12,7 @@ RSpec.describe Vouchers::PercentageRate do
|
||||
.is_greater_than(0)
|
||||
.is_less_than_or_equal_to(100)
|
||||
end
|
||||
it { is_expected.to validate_uniqueness_of(:code).scoped_to(:enterprise_id) }
|
||||
it_behaves_like 'has a unique code per enterprise', "voucher_percentage_rate"
|
||||
end
|
||||
|
||||
describe '#compute_amount' do
|
||||
|
||||
39
spec/support/voucher_uniqueness_helper.rb
Normal file
39
spec/support/voucher_uniqueness_helper.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
shared_examples_for 'has a unique code per enterprise' do |voucher_type|
|
||||
describe "code" do
|
||||
let(:code) { "super_code" }
|
||||
let(:enterprise) { create(:enterprise) }
|
||||
|
||||
it "is unique per enterprise" do
|
||||
voucher = create(voucher_type, code:, enterprise:)
|
||||
expect(voucher).to be_valid
|
||||
|
||||
expect_voucher_with_same_enterprise_to_be_invalid(voucher_type)
|
||||
|
||||
expect_voucher_with_other_enterprise_to_be_valid(voucher_type)
|
||||
end
|
||||
|
||||
context "with deleted voucher" do
|
||||
it "is unique per enterprise" do
|
||||
create(voucher_type, code:, enterprise:).destroy!
|
||||
|
||||
expect_voucher_with_same_enterprise_to_be_invalid(voucher_type)
|
||||
|
||||
expect_voucher_with_other_enterprise_to_be_valid(voucher_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def expect_voucher_with_same_enterprise_to_be_invalid(voucher_type)
|
||||
new_voucher = build(voucher_type, code:, enterprise: )
|
||||
|
||||
expect(new_voucher).not_to be_valid
|
||||
expect(new_voucher.errors.full_messages).to include("Code has already been taken")
|
||||
end
|
||||
|
||||
def expect_voucher_with_other_enterprise_to_be_valid(voucher_type)
|
||||
other_voucher = build(voucher_type, code:, enterprise: create(:enterprise) )
|
||||
expect(other_voucher).to be_valid
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user