Class: Article
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Article
- Includes:
- LocalizeInput, PriceCalculation
- Defined in:
- app/models/article.rb
Direct Known Subclasses
Instance Attribute Summary collapse
-
#article_category ⇒ ArticleCategory
Category this article is in.
-
#article_versions ⇒ Array<ArticleVersion>
Price history (current price first).
-
#availability ⇒ Boolean
Whether this article is available within the Foodcoop.
-
#deposit ⇒ Number
Deposit.
-
#manufacturer ⇒ String
Original manufacturer.
-
#name ⇒ String
Article name.
-
#note ⇒ String
Short line with optional extra article information.
-
#order ⇒ Array<Order>
Orders this article appears in.
-
#order_number ⇒ Object
Order number, this can be used by the supplier to identify articles.
-
#origin ⇒ String
Where the article was produced.
-
#price ⇒ Number
Net price.
-
#supplier ⇒ Supplier
Supplier this article belongs to.
-
#tax ⇒ Number
VAT percentage (10 is 10%).
-
#unit ⇒ String
Unit, e.g.
-
#unit_quantity ⇒ Number
Number of units in wholesale package (box).
Class Method Summary collapse
- .ransackable_associations(_auth_object = nil) ⇒ Object
- .ransackable_attributes(_auth_object = nil) ⇒ Object
Instance Method Summary collapse
-
#check_article_in_use ⇒ Object
protected
Checks if the article is in use before it will deleted.
-
#convert_units(new_article = shared_article) ⇒ Object
convert units in foodcoop-size uses unit factors in app_config.yml to calc the price/unit_quantity returns new price and unit_quantity in array, when calc is possible => [price, unit_quantity] returns false if units aren’t foodsoft-compatible returns nil if units are eqal.
- #current_article_units ⇒ Object
- #deleted? ⇒ Boolean
- #duplicate_including_latest_version_and_ratios ⇒ Object
-
#in_open_order ⇒ Object
If the article is used in an open Order, the Order will be returned.
- #mark_as_deleted ⇒ Object
-
#ordered_in_order?(order) ⇒ Boolean
Returns true if the article has been ordered in the given order at least once.
-
#recently_updated ⇒ Object
Returns true if article has been updated at least 2 days ago.
- #reload_article_on_version_change ⇒ Object protected
-
#shared_article(supplier = self.supplier) ⇒ Object
to get the correspondent shared article.
-
#unequal_attributes(new_article, options = {}) ⇒ Hash<Symbol, Object>
Return article attributes that were changed (incl. unit conversion).
-
#update_or_create_article_version ⇒ Object
protected
Create an ArticleVersion, when the price-attr are changed.
- #version_dup_required? ⇒ Boolean protected
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
Instance Attribute Details
#article_category ⇒ ArticleCategory
Returns Category this article is in.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#article_versions ⇒ Array<ArticleVersion>
Returns Price history (current price first).
44 |
# File 'app/models/article.rb', line 44 has_many :article_versions, -> { order('created_at DESC') } |
#availability ⇒ Boolean
Returns Whether this article is available within the Foodcoop.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#deposit ⇒ Number
Returns Deposit.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#manufacturer ⇒ String
Returns Original manufacturer.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#name ⇒ String
Returns Article name.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#note ⇒ String
Returns Short line with optional extra article information.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#order ⇒ Array<Order>
Returns Orders this article appears in.
48 |
# File 'app/models/article.rb', line 48 has_many :orders, through: :order_articles |
#order_number ⇒ Object
Order number, this can be used by the supplier to identify articles. This is required when using the shared database functionality.
@return [String] Order number.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#origin ⇒ String
Where the article was produced. ISO 3166-1 2-letter country code, optionally prefixed with region. E.g. NL or Sicily, IT or Berlin, DE.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#price ⇒ Number
Returns Net price.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#supplier ⇒ Supplier
Returns Supplier this article belongs to.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#tax ⇒ Number
Returns VAT percentage (10 is 10%).
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#unit ⇒ String
Returns Unit, e.g. kg, 2 L or 5 pieces.
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
#unit_quantity ⇒ Number
Returns Number of units in wholesale package (box).
41 |
# File 'app/models/article.rb', line 41 belongs_to :supplier |
Class Method Details
.ransackable_associations(_auth_object = nil) ⇒ Object
99 100 101 102 |
# File 'app/models/article.rb', line 99 def self.ransackable_associations(_auth_object = nil) # TODO: - see https://github.com/foodcoopsat/foodsoft_hackathon/issues/92 %w[article_category supplier order_articles orders] end |
.ransackable_attributes(_auth_object = nil) ⇒ Object
94 95 96 97 |
# File 'app/models/article.rb', line 94 def self.ransackable_attributes(_auth_object = nil) # TODO: - see https://github.com/foodcoopsat/foodsoft_hackathon/issues/92 %w[id name supplier_id article_category_id unit note manufacturer origin unit_quantity order_number] end |
Instance Method Details
#check_article_in_use ⇒ Object (protected)
Checks if the article is in use before it will deleted
258 259 260 |
# File 'app/models/article.rb', line 258 def check_article_in_use raise I18n.t('articles.model.error_in_use', article: name.to_s) if in_open_order end |
#convert_units(new_article = shared_article) ⇒ Object
convert units in foodcoop-size uses unit factors in app_config.yml to calc the price/unit_quantity returns new price and unit_quantity in array, when calc is possible => [price, unit_quantity] returns false if units aren’t foodsoft-compatible returns nil if units are eqal
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'app/models/article.rb', line 196 def convert_units(new_article = shared_article) return unless unit != new_article.unit return false if new_article.unit.include?(',') # legacy, used by foodcoops in Germany if new_article.unit == 'KI' && unit == 'ST' # 'KI' means a box, with a different amount of items in it # try to match the size out of its name, e.g. "banana 10-12 St" => 10 new_unit_quantity = /[0-9\-\s]+(St)/.match(new_article.name).to_s.to_i if new_unit_quantity && new_unit_quantity > 0 new_price = (new_article.price / new_unit_quantity.to_f).round(2) [new_price, new_unit_quantity] else false end else # use ruby-units to convert fc_unit = begin ::Unit.new(unit) rescue StandardError nil end supplier_unit = begin ::Unit.new(new_article.unit) rescue StandardError nil end if fc_unit != 0 && supplier_unit != 0 && fc_unit && supplier_unit && fc_unit =~ supplier_unit conversion_factor = (supplier_unit / fc_unit).to_base.to_r new_price = new_article.price / conversion_factor new_unit_quantity = new_article.unit_quantity * conversion_factor [new_price, new_unit_quantity] else false end end end |
#current_article_units ⇒ Object
242 243 244 245 246 247 |
# File 'app/models/article.rb', line 242 def current_article_units [supplier_order_unit, group_order_unit, billing_unit, price_unit, article_unit_ratios.map(&:unit)] .flatten .uniq .compact end |
#deleted? ⇒ Boolean
233 234 235 |
# File 'app/models/article.rb', line 233 def deleted? deleted_at.present? end |
#duplicate_including_latest_version_and_ratios ⇒ Object
249 250 251 252 253 |
# File 'app/models/article.rb', line 249 def duplicate_including_latest_version_and_ratios article = dup article.latest_article_version = latest_article_version.duplicate_including_article_unit_ratios article end |
#in_open_order ⇒ Object
If the article is used in an open Order, the Order will be returned.
110 111 112 113 114 115 116 |
# File 'app/models/article.rb', line 110 def in_open_order @in_open_order ||= begin order_articles = OrderArticle.where(order_id: Order.open.collect(&:id)) order_article = order_articles.detect { |oa| oa.article_version.article_id == id } order_article ? order_article.order : nil end end |
#mark_as_deleted ⇒ Object
237 238 239 240 |
# File 'app/models/article.rb', line 237 def mark_as_deleted check_article_in_use update_column :deleted_at, Time.now end |
#ordered_in_order?(order) ⇒ Boolean
Returns true if the article has been ordered in the given order at least once
119 120 121 |
# File 'app/models/article.rb', line 119 def ordered_in_order?(order) order.order_articles.includes(:article_version).where(article_version: { article_id: id }).where('quantity > 0').one? end |
#recently_updated ⇒ Object
Returns true if article has been updated at least 2 days ago
105 106 107 |
# File 'app/models/article.rb', line 105 def recently_updated latest_article_version.updated_at > 2.days.ago end |
#reload_article_on_version_change ⇒ Object (protected)
282 283 284 285 |
# File 'app/models/article.rb', line 282 def reload_article_on_version_change reload if @version_changed_before_save @version_changed_before_save = false end |
#shared_article(supplier = self.supplier) ⇒ Object
to get the correspondent shared article
124 125 126 127 128 129 130 131 |
# File 'app/models/article.rb', line 124 def shared_article(supplier = self.supplier) order_number.blank? and return nil @shared_article ||= begin supplier.shared_supplier.find_article_by_number(order_number) rescue StandardError nil end end |
#unequal_attributes(new_article, options = {}) ⇒ Hash<Symbol, Object>
Return article attributes that were changed (incl. unit conversion)
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'app/models/article.rb', line 137 def unequal_attributes(new_article, = {}) # try to convert different units when desired if [:convert_units] == false new_price = nil new_unit_quantity = nil else new_price, new_unit_quantity = convert_units(new_article) end if new_price && new_unit_quantity new_unit = unit else new_price = new_article.price new_unit_quantity = new_article.unit_quantity new_unit = new_article.unit end ret = ArticleVersion.compare_attributes( { name: [latest_article_version.name, new_article.name], manufacturer: [latest_article_version.manufacturer, new_article.manufacturer.to_s], origin: [latest_article_version.origin, new_article.origin], unit: [latest_article_version.unit, new_unit], supplier_order_unit: [latest_article_version.supplier_order_unit, new_article.supplier_order_unit], minimum_order_quantity: [latest_article_version.minimum_order_quantity, new_article.minimum_order_quantity], billing_unit: [latest_article_version.billing_unit || latest_article_version.supplier_order_unit, new_article.billing_unit || new_article.supplier_order_unit], group_order_granularity: [latest_article_version.group_order_granularity, new_article.group_order_granularity], group_order_unit: [latest_article_version.group_order_unit, new_article.group_order_unit], price: [latest_article_version.price.to_f.round(2), new_price.to_f.round(2)], tax: [latest_article_version.tax, new_article.tax], deposit: [latest_article_version.deposit.to_f.round(2), new_article.deposit.to_f.round(2)], note: [latest_article_version.note.to_s, new_article.note.to_s] } ) ratios_differ = latest_article_version.article_unit_ratios.length != new_article.article_unit_ratios.length || latest_article_version.article_unit_ratios.each_with_index.any? do |article_unit_ratio, index| new_article.article_unit_ratios[index].unit != article_unit_ratio.unit || new_article.article_unit_ratios[index].quantity != article_unit_ratio.quantity end if ratios_differ ratio_attribs = new_article.article_unit_ratios.map(&:attributes) ret[:article_unit_ratios_attributes] = ratio_attribs end if [:convert_units] && latest_article_version.article_unit_ratios.length < 2 && new_article.article_unit_ratios.length < 2 && !new_unit_quantity.nil? ret[:article_unit_ratios_attributes] = [new_article.article_unit_ratios.build(unit: 'XPP', quantity: new_unit_quantity, sort: 1).attributes] # TODO: Either remove this aspect of the :convert_units feature or extend it to also work for the new units system (see https://github.com/foodcoopsat/foodsoft_hackathon/issues/90) end ret end |
#update_or_create_article_version ⇒ Object (protected)
Create an ArticleVersion, when the price-attr are changed.
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'app/models/article.rb', line 263 def update_or_create_article_version @version_changed_before_save = false return unless version_dup_required? old_version = latest_article_version new_version = old_version.duplicate_including_article_unit_ratios article_versions << new_version OrderArticle.belonging_to_open_order .joins(:article_version) .where(article_versions: { article_id: id }) .update_all(article_version_id: new_version.id) # reload old version to avoid updating it too (would automatically happen after before_save): old_version.reload @version_changed_before_save = true end |
#version_dup_required? ⇒ Boolean (protected)
287 288 289 290 291 292 |
# File 'app/models/article.rb', line 287 def version_dup_required? return false if latest_article_version.nil? return false unless latest_article_version.self_or_ratios_changed? OrderArticle.belonging_to_finished_order.exists?(article_version_id: latest_article_version.id) end |