Class: ArticleVersion

Inherits:
ApplicationRecord show all
Includes:
LocalizeInput, PriceCalculation
Defined in:
app/models/article_version.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from PriceCalculation

#convert_quantity, #fc_group_order_price, #fc_price, #get_unit_ratio_quantity, #gross_group_order_price, #gross_price, #group_order_price, #price_unit_price

Methods included from LocalizeInput

parse

Instance Attribute Details

#articleArticle

Returns Article this price is about.

Returns:

  • (Article)

    Article this price is about.



20
# File 'app/models/article_version.rb', line 20

belongs_to :article

#depositNumber

Returns Deposit.

Returns:

  • (Number)

    Deposit

See Also:



20
# File 'app/models/article_version.rb', line 20

belongs_to :article

#order_articlesArray<OrderArticle>

Returns Order articles this price is associated with.

Returns:

  • (Array<OrderArticle>)

    Order articles this price is associated with.



24
# File 'app/models/article_version.rb', line 24

has_many :order_articles

#priceNumber

Returns Net price.

Returns:

  • (Number)

    Net price

See Also:



20
# File 'app/models/article_version.rb', line 20

belongs_to :article

#taxNumber

Returns VAT percentage.

Returns:

  • (Number)

    VAT percentage

See Also:



20
# File 'app/models/article_version.rb', line 20

belongs_to :article

#unit_quantityNumber

Returns Number of units in wholesale package (box).

Returns:

  • (Number)

    Number of units in wholesale package (box).

See Also:



20
# File 'app/models/article_version.rb', line 20

belongs_to :article

Class Method Details

.compare_attributes(attributes) ⇒ Hash<Symbol, Object>

Compare attributes from two different articles.

This is used for auto-synchronization

Parameters:

  • attributes (Hash<Symbol, Array>)

    Attributes with old and new values

Returns:

  • (Hash<Symbol, Object>)

    Changed attributes with new values



143
144
145
146
147
148
# File 'app/models/article_version.rb', line 143

def self.compare_attributes(attributes)
  unequal_attributes = attributes.select do |_name, values|
    values[0] != values[1] && !(values[0].blank? && values[1].blank?)
  end
  unequal_attributes.to_a.map { |a| [a[0], a[1].last] }.to_h
end

.latest_outer_join_sql(article_field_name) ⇒ Object



62
63
64
65
66
67
68
# File 'app/models/article_version.rb', line 62

def self.latest_outer_join_sql(article_field_name)
  %(
    LEFT OUTER JOIN #{table_name} later_article_versions
    ON later_article_versions.article_id = #{article_field_name}
      AND later_article_versions.created_at > #{table_name}.created_at
  )
end

Instance Method Details

#billing_unit=(value) ⇒ Object



107
108
109
110
111
112
113
# File 'app/models/article_version.rb', line 107

def billing_unit=(value)
  if value.blank?
    self[:billing_unit] = nil
  else
    super
  end
end

#duplicate_including_article_unit_ratiosObject



127
128
129
130
131
132
133
134
135
136
# File 'app/models/article_version.rb', line 127

def duplicate_including_article_unit_ratios
  new_version = dup
  article_unit_ratios.each do |ratio|
    ratio = ratio.dup
    ratio.article_version_id = nil
    new_version.article_unit_ratios << ratio
  end

  new_version
end

#group_order_unit=(value) ⇒ Object



91
92
93
94
95
96
97
# File 'app/models/article_version.rb', line 91

def group_order_unit=(value)
  if value.blank?
    self[:group_order_unit] = nil
  else
    super
  end
end

#minimum_order_quantity=(value) ⇒ Object



115
116
117
118
119
120
121
# File 'app/models/article_version.rb', line 115

def minimum_order_quantity=(value)
  if value.blank?
    self[:minimum_order_quantity] = nil
  else
    super
  end
end

#minimum_order_quantity_as_integerObject (protected)



186
187
188
189
190
191
192
193
194
195
# File 'app/models/article_version.rb', line 186

def minimum_order_quantity_as_integer
  begin
    return if Float(minimum_order_quantity) % 1 == 0
  rescue ArgumentError, TypeError
    # not any number -> let numericality validation handle this
    return
  end

  errors.add(:minimum_order_quantity, :only_integer)
end

#on_article_unit_ratios_change(_some_change) ⇒ Object (protected)



203
204
205
# File 'app/models/article_version.rb', line 203

def on_article_unit_ratios_change(_some_change)
  @article_unit_ratios_changed = true
end

#only_one_unit_typeObject (protected)



197
198
199
200
201
# File 'app/models/article_version.rb', line 197

def only_one_unit_type
  return if unit.blank? || supplier_order_unit.blank?

  errors.add :unit # not specifying a specific error message as this should be prevented by js
end

#price_unit=(value) ⇒ Object



99
100
101
102
103
104
105
# File 'app/models/article_version.rb', line 99

def price_unit=(value)
  if value.blank?
    self[:price_unit] = nil
  else
    super
  end
end

#self_or_ratios_changed?Boolean

Returns:

  • (Boolean)


123
124
125
# File 'app/models/article_version.rb', line 123

def self_or_ratios_changed?
  changed? || @article_unit_ratios_changed || article_unit_ratios.any?(&:changed?)
end

#supplier_order_unit=(value) ⇒ Object



83
84
85
86
87
88
89
# File 'app/models/article_version.rb', line 83

def supplier_order_unit=(value)
  if value.blank?
    self[:supplier_order_unit] = nil
  else
    super
  end
end

#supplier_order_unit_is_si_convertibleObject



70
71
72
# File 'app/models/article_version.rb', line 70

def supplier_order_unit_is_si_convertible
  ArticleUnitsLib.unit_is_si_convertible(supplier_order_unit)
end

#uniqueness_of_nameObject (protected)

We used have the name unique per supplier+deleted_at+type. With the addition of shared_sync_method all, this came in the way, and we now allow duplicate names for the ‘all’ methods - expecting foodcoops to make their own choice among products with different units by making articles available/unavailable.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'app/models/article_version.rb', line 162

def uniqueness_of_name
  matches = Article.with_latest_versions.where(article_versions: { name: name },
                                               supplier_id: article.supplier_id,
                                               deleted_at: article.deleted_at,
                                               type: article.type)
  matches = matches.where.not(id: article.id) unless article.new_record?
  # supplier should always be there - except, perhaps, on initialization (on seeding)
  if article.supplier && (article.supplier.shared_sync_method.blank? || article.supplier.shared_sync_method == 'import')
    errors.add :name, :taken if matches.any?
  else
    article_unit_ratios.each_with_index do |article_unit_ratio, index|
      matches = matches.joins(%(
        INNER JOIN #{ArticleUnitRatio.table_name} #{ArticleUnitRatio.table_name}_#{index}
        ON #{ArticleUnitRatio.table_name}_#{index}.article_version_id = #{ArticleVersion.table_name}.id
          AND #{ArticleUnitRatio.table_name}_#{index}.sort = #{ArticleUnitRatio.connection.quote(article_unit_ratio.sort)}
          AND #{ArticleUnitRatio.table_name}_#{index}.unit = #{ArticleUnitRatio.connection.quote(article_unit_ratio.unit)}
          AND #{ArticleUnitRatio.table_name}_#{index}.quantity = #{ArticleUnitRatio.connection.quote(article_unit_ratio.quantity)}
      ))
    end

    errors.add :name, :taken_with_unit if matches.where(article_versions: { supplier_order_unit: supplier_order_unit, unit: unit }).any?
  end
end

#unit=(value) ⇒ Object

TODO: Maybe use the ‘nilify_blanks` gem instead of the following six methods? (see github.com/foodcoopsat/foodsoft_hackathon/issues/93):



75
76
77
78
79
80
81
# File 'app/models/article_version.rb', line 75

def unit=(value)
  if value.blank?
    self[:unit] = nil
  else
    super
  end
end

#uses_tolerance?Boolean

Returns:

  • (Boolean)


150
151
152
153
154
155
# File 'app/models/article_version.rb', line 150

def uses_tolerance?
  (
    !supplier_order_unit_is_si_convertible &&
    convert_quantity(1, supplier_order_unit, group_order_unit) != group_order_granularity
  ) || (minimum_order_quantity.presence || 0) > group_order_granularity
end