Skip to content

Commit

Permalink
fix: add DB unique index to prevent duplicate ArticlesSales & show pr…
Browse files Browse the repository at this point in the history
…etty error in form
  • Loading branch information
nymous committed Nov 2, 2024
1 parent 97308cb commit 5a8745d
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 12 deletions.
15 changes: 4 additions & 11 deletions app/models/articles_sale.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,8 @@ class ArticlesSale < ApplicationRecord

validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }

before_create :consolidate_duplication

private

def consolidate_duplication
duplicate = ArticlesSale.where(article_id: article_id, sale_id: sale_id).where.not(id: id).first
return unless duplicate

errors.add(:base, 'Please merge the quantities of the same articles !')
throw :abort
end
validates :article_id, uniqueness: {
scope: :sale_id,
message: 'can only be added once to a sale, please merge the quantities of the same articles'
}
end
16 changes: 16 additions & 0 deletions app/models/sale.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Sale < ApplicationRecord
validates :duration, numericality: { greater_than_or_equal_to: 0 }
validate :not_empty_sale, unless: ->(sale) { sale.errors.include?(:duration) }
validate :exhaustive_subscription_offers, unless: ->(sale) { sale.errors.include?(:duration) }
validate :no_duplicate_article_sale

def verify
self.verified_at = Time.zone.now if verified_at.nil?
Expand Down Expand Up @@ -57,6 +58,21 @@ def exhaustive_subscription_offers
errors.add(:base, 'Subscription offers are not exhaustive!')
end

def no_duplicate_article_sale
# The unique constraint on ArticlesSales prevents us from duplicating articles in a single sale,
# but Rails doesn't rescue from hard DB integrity errors, so we need to validate the association
# in-memory before to show pretty errors.
# See https://github.com/rails/rails/issues/20676 that seems very related
duplicate_article_ids = articles_sales.map(&:article_id).tally.filter { |_, v| v > 1 }
return if duplicate_article_ids.empty?

articles_sales

This comment has been minimized.

Copy link
@nymous

nymous Nov 2, 2024

Author Member

With this we add errors to the specific ArticlesSale model field, so that it is marked in the form as invalid (with a red outline or whatever the CSS is)

.filter { |article_sale| article_sale.article_id.in? duplicate_article_ids }
.each { |article_sale| article_sale.errors.add(:article_id, :taken) }

errors.add(:base, 'Please merge the quantities of the same articles')
end

class << self
private

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddUniqueIndexToArticlesSales < ActiveRecord::Migration[7.1]
def change
add_index :articles_sales, [:article_id, :sale_id], unique: true
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5a8745d

Please sign in to comment.