diff --git a/plugins/metadata/install.rb b/plugins/metadata/install.rb new file mode 100644 index 0000000..84357d1 --- /dev/null +++ b/plugins/metadata/install.rb @@ -0,0 +1,5 @@ +if ENV['CI'] + system 'script/noosfero-plugins -q enable products' + exit $?.exitstatus +end + diff --git a/plugins/metadata/lib/ext/product.rb b/plugins/metadata/lib/ext/product.rb deleted file mode 100644 index b2edcb2..0000000 --- a/plugins/metadata/lib/ext/product.rb +++ /dev/null @@ -1,34 +0,0 @@ -require_dependency 'product' - -class Product - - metadata_spec namespace: :og, tags: { - type: proc{ |p, plugin| plugin.context.params[:og_type] || MetadataPlugin.og_types[:product] || :product }, - url: proc do |p, plugin| - url = p.url.merge! profile: p.profile.identifier, og_type: plugin.context.params[:og_type] - plugin.og_url_for url - end, - gr_hascurrencyvalue: proc{ |p, plugin| p.price.to_f }, - gr_hascurrency: proc{ |p, plugin| p.environment.currency_unit }, - title: proc{ |p, plugin| "#{p.name} - #{p.profile.name}" if p }, - description: proc{ |p, plugin| ActionView::Base.full_sanitizer.sanitize p.description }, - - image: proc do |p, plugin| - img = "#{p.environment.top_url}#{p.image.public_filename}".html_safe if p.image - img = "#{p.environment.top_url}#{p.profile.image.public_filename}".html_safe if img.blank? and p.profile.image - img ||= MetadataPlugin.config[:open_graph][:environment_logo] rescue nil if img.blank? - img - end, - 'image:type' => proc{ |p, plugin| p.image.content_type if p.image }, - 'image:height' => proc{ |p, plugin| p.image.height if p.image }, - 'image:width' => proc{ |p, plugin| p.image.width if p.image }, - - see_also: [], - site_name: proc{ |p, plugin| plugin.og_url_for p.profile.url }, - updated_time: proc{ |p, plugin| p.updated_at.iso8601 if p.updated_at }, - - 'locale:locale' => proc{ |p, plugin| p.environment.default_language }, - 'locale:alternate' => proc{ |p, plugin| p.environment.languages - [p.environment.default_language] if p.environment.languages }, - } - -end diff --git a/plugins/metadata/lib/ext/products_plugin/product.rb b/plugins/metadata/lib/ext/products_plugin/product.rb new file mode 100644 index 0000000..0b01abe --- /dev/null +++ b/plugins/metadata/lib/ext/products_plugin/product.rb @@ -0,0 +1,38 @@ +if defined? ProductsPlugin + require_dependency 'products_plugin/product' + + module ProductsPlugin + class Product + + metadata_spec namespace: :og, tags: { + type: proc{ |p, plugin| plugin.context.params[:og_type] || MetadataPlugin.og_types[:product] || :product }, + url: proc do |p, plugin| + url = p.url.merge! profile: p.profile.identifier, og_type: plugin.context.params[:og_type] + plugin.og_url_for url + end, + gr_hascurrencyvalue: proc{ |p, plugin| p.price.to_f }, + gr_hascurrency: proc{ |p, plugin| p.environment.currency_unit }, + title: proc{ |p, plugin| "#{p.name} - #{p.profile.name}" if p }, + description: proc{ |p, plugin| ActionView::Base.full_sanitizer.sanitize p.description }, + + image: proc do |p, plugin| + img = "#{p.environment.top_url}#{p.image.public_filename}".html_safe if p.image + img = "#{p.environment.top_url}#{p.profile.image.public_filename}".html_safe if img.blank? and p.profile.image + img ||= MetadataPlugin.config[:open_graph][:environment_logo] rescue nil if img.blank? + img + end, + 'image:type' => proc{ |p, plugin| p.image.content_type if p.image }, + 'image:height' => proc{ |p, plugin| p.image.height if p.image }, + 'image:width' => proc{ |p, plugin| p.image.width if p.image }, + + see_also: [], + site_name: proc{ |p, plugin| plugin.og_url_for p.profile.url }, + updated_time: proc{ |p, plugin| p.updated_at.iso8601 if p.updated_at }, + + 'locale:locale' => proc{ |p, plugin| p.environment.default_language }, + 'locale:alternate' => proc{ |p, plugin| p.environment.languages - [p.environment.default_language] if p.environment.languages }, + } + + end + end +end diff --git a/plugins/metadata/test/functional/manage_products_controller_test.rb b/plugins/metadata/test/functional/manage_products_controller_test.rb deleted file mode 100644 index b75c8a6..0000000 --- a/plugins/metadata/test/functional/manage_products_controller_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'test_helper' -require 'home_controller' - -class ManageProductsControllerTest < ActionController::TestCase - - def setup - @controller = ManageProductsController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - @enterprise = fast_create(Enterprise, name: 'test', identifier: 'test_ent') - @user = create_user_with_permission('test_user', 'manage_products', @enterprise) - @environment = @enterprise.environment - @environment.enable('products_for_enterprises') - login_as :test_user - - @environment.enabled_plugins += ['MetadataPlugin'] - @environment.save! - end - - should "not crash on new products" do - get :new, profile: @enterprise.identifier - end - -end diff --git a/plugins/metadata/test/functional/products_plugin/page_controller_test.rb b/plugins/metadata/test/functional/products_plugin/page_controller_test.rb new file mode 100644 index 0000000..4dc0a70 --- /dev/null +++ b/plugins/metadata/test/functional/products_plugin/page_controller_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +if defined? ProductsPlugin + module ProductsPlugin + class ManageProductsControllerTest < ActionController::TestCase + + def setup + @controller = PageController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @enterprise = fast_create(Enterprise, name: 'test', identifier: 'test_ent') + @user = create_user_with_permission('test_user', 'manage_products', @enterprise) + login_as :test_user + + @enterprise.environment.enable_plugin 'MetadataPlugin' + end + + should "not crash on new products" do + get :new, profile: @enterprise.identifier + end + + end + end +end diff --git a/plugins/orders/install.rb b/plugins/orders/install.rb index 4e92bcd..909fb0e 100644 --- a/plugins/orders/install.rb +++ b/plugins/orders/install.rb @@ -1,3 +1,3 @@ -system "script/noosfero-plugins -q enable delivery" +system 'script/noosfero-plugins -q enable products delivery' exit $?.exitstatus diff --git a/plugins/orders/lib/ext/product.rb b/plugins/orders/lib/ext/product.rb deleted file mode 100644 index 2b66243..0000000 --- a/plugins/orders/lib/ext/product.rb +++ /dev/null @@ -1,16 +0,0 @@ -require_dependency 'product' - -class Product - - has_many :items, class_name: 'OrdersPlugin::Item', foreign_key: :product_id, dependent: :destroy - - has_many :orders, through: :items - has_many :sales, through: :items - has_many :purchases, through: :items - - attr_accessor :quantity_ordered - - extend CurrencyHelper::ClassMethods - instance_exec &OrdersPlugin::Item::DefineTotals - -end diff --git a/plugins/orders/lib/ext/products_plugin/product.rb b/plugins/orders/lib/ext/products_plugin/product.rb new file mode 100644 index 0000000..138e4cf --- /dev/null +++ b/plugins/orders/lib/ext/products_plugin/product.rb @@ -0,0 +1,18 @@ +require_dependency 'products_plugin/product' + +module ProductsPlugin + class Product + + has_many :items, class_name: 'OrdersPlugin::Item', foreign_key: :product_id, dependent: :destroy + + has_many :orders, through: :items + has_many :sales, through: :items + has_many :purchases, through: :items + + attr_accessor :quantity_ordered + + extend CurrencyHelper::ClassMethods + instance_exec &OrdersPlugin::Item::DefineTotals + + end +end diff --git a/plugins/orders/models/orders_plugin/item.rb b/plugins/orders/models/orders_plugin/item.rb index af91aa7..0e107f9 100644 --- a/plugins/orders/models/orders_plugin/item.rb +++ b/plugins/orders/models/orders_plugin/item.rb @@ -39,7 +39,7 @@ class OrdersPlugin::Item < ApplicationRecord belongs_to :sale, class_name: '::OrdersPlugin::Sale', foreign_key: :order_id, touch: true belongs_to :purchase, class_name: '::OrdersPlugin::Purchase', foreign_key: :order_id, touch: true - belongs_to :product + belongs_to :product, class_name: '::ProductsPlugin::Product' has_one :supplier, through: :product has_one :profile, through: :order diff --git a/plugins/orders_cycle/lib/ext/product.rb b/plugins/orders_cycle/lib/ext/product.rb deleted file mode 100644 index 67c1a4e..0000000 --- a/plugins/orders_cycle/lib/ext/product.rb +++ /dev/null @@ -1,12 +0,0 @@ -require_dependency 'product' - -# based on orders/lib/ext/product.rb -class Product - - has_many :orders_cycles_items, class_name: 'OrdersCyclePlugin::Item', foreign_key: :product_id - - has_many :orders_cycles_orders, through: :orders_cycles_items, source: :order - has_many :orders_cycles_sales, through: :orders_cycles_items, source: :sale - has_many :orders_cycles_purchases, through: :orders_cycles_items, source: :purchase - -end diff --git a/plugins/orders_cycle/lib/ext/products_plugin/product.rb b/plugins/orders_cycle/lib/ext/products_plugin/product.rb new file mode 100644 index 0000000..304e18f --- /dev/null +++ b/plugins/orders_cycle/lib/ext/products_plugin/product.rb @@ -0,0 +1,14 @@ +require_dependency 'products_plugin/product' + +# based on orders/lib/ext/product.rb +module ProductsPlugin + class Product + + has_many :orders_cycles_items, class_name: 'OrdersCyclePlugin::Item', foreign_key: :product_id + + has_many :orders_cycles_orders, through: :orders_cycles_items, source: :order + has_many :orders_cycles_sales, through: :orders_cycles_items, source: :sale + has_many :orders_cycles_purchases, through: :orders_cycles_items, source: :purchase + + end +end diff --git a/plugins/pg_search/db/migrate/20130320010063_create_indexes_for_search.rb b/plugins/pg_search/db/migrate/20130320010063_create_indexes_for_search.rb index 4dd2f20..6c84d95 100644 --- a/plugins/pg_search/db/migrate/20130320010063_create_indexes_for_search.rb +++ b/plugins/pg_search/db/migrate/20130320010063_create_indexes_for_search.rb @@ -1,5 +1,5 @@ class CreateIndexesForSearch < ActiveRecord::Migration - SEARCHABLES = %w[ article comment qualifier national_region certifier profile license scrap category ] + SEARCHABLES = %w[ article comment national_region profile license scrap category ] KLASSES = SEARCHABLES.map {|searchable| searchable.camelize.constantize } def self.up KLASSES.each do |klass| diff --git a/plugins/shopping_cart/views/shopping_cart_plugin/send_request.js.erb b/plugins/shopping_cart/views/shopping_cart_plugin/send_request.js.erb index e9a2e4a..69b763a 100644 --- a/plugins/shopping_cart/views/shopping_cart_plugin/send_request.js.erb +++ b/plugins/shopping_cart/views/shopping_cart_plugin/send_request.js.erb @@ -1,5 +1,5 @@ <% if @success %> - window.location.href = <%= url_for(controller: :catalog, profile: @profile.identifier).to_json %> + window.location.href = <%= url_for(controller: 'products_plugin/catalog', profile: @profile.identifier).to_json %> <% else %> display_notice(<%= "#{_ 'Error'}: #{@error}".to_json %>) <% end %> diff --git a/plugins/sniffer/install.rb b/plugins/sniffer/install.rb new file mode 100644 index 0000000..fd1ea3d --- /dev/null +++ b/plugins/sniffer/install.rb @@ -0,0 +1,3 @@ +system 'script/noosfero-plugins -q enable products' +exit $?.exitstatus + diff --git a/plugins/statistics/install.rb b/plugins/statistics/install.rb new file mode 100644 index 0000000..84357d1 --- /dev/null +++ b/plugins/statistics/install.rb @@ -0,0 +1,5 @@ +if ENV['CI'] + system 'script/noosfero-plugins -q enable products' + exit $?.exitstatus +end + diff --git a/plugins/suppliers/install.rb b/plugins/suppliers/install.rb new file mode 100644 index 0000000..fd1ea3d --- /dev/null +++ b/plugins/suppliers/install.rb @@ -0,0 +1,3 @@ +system 'script/noosfero-plugins -q enable products' +exit $?.exitstatus + diff --git a/plugins/suppliers/lib/ext/price_detail.rb b/plugins/suppliers/lib/ext/price_detail.rb deleted file mode 100644 index 8983a8e..0000000 --- a/plugins/suppliers/lib/ext/price_detail.rb +++ /dev/null @@ -1,8 +0,0 @@ -require_dependency 'price_detail' - -class PriceDetail - - # should be on core, used by SuppliersPlugin::Import - attr_accessible :production_cost - -end diff --git a/plugins/suppliers/lib/ext/product.rb b/plugins/suppliers/lib/ext/product.rb deleted file mode 100644 index 75ad780..0000000 --- a/plugins/suppliers/lib/ext/product.rb +++ /dev/null @@ -1,178 +0,0 @@ -require_dependency 'product' - -# FIXME: The lines bellow should be on the core -class Product - - extend CurrencyHelper::ClassMethods - has_currency :price - has_currency :discount - - scope :alphabetically, -> { order 'products.name ASC' } - - scope :available, -> { where available: true } - scope :unavailable, -> { where 'products.available <> true' } - scope :archived, -> { where archived: true } - scope :unarchived, -> { where 'products.archived <> true' } - - scope :with_available, -> (available) { where available: available } - scope :with_price, -> { where 'products.price > 0' } - - # FIXME: transliterate input and name column - scope :name_like, -> (name) { where "name ILIKE ?", "%#{name}%" } - scope :with_product_category_id, -> (id) { where product_category_id: id } - - scope :by_profile, -> (profile) { where profile_id: profile.id } - scope :by_profile_id, -> (profile_id) { where profile_id: profile_id } - - def self.product_categories_of products - ProductCategory.find products.collect(&:product_category_id).compact.select{ |id| not id.zero? } - end - - attr_accessible :external_id - settings_items :external_id, type: String, default: nil - - # should be on core, used by SuppliersPlugin::Import - attr_accessible :price_details - -end - -class Product - - attr_accessible :from_products, :from_product, :supplier_id, :supplier - - has_many :sources_from_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy - has_one :sources_from_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' - has_many :sources_to_products, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy - has_one :sources_to_product, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct' - has_many :to_products, -> { order 'id ASC' }, through: :sources_to_products - has_one :to_product, -> { order 'id ASC' }, through: :sources_to_product, autosave: true - has_many :from_products, -> { order 'id ASC' }, through: :sources_from_products - has_one :from_product, -> { order 'id ASC' }, through: :sources_from_product, autosave: true - - has_many :sources_from_2x_products, through: :from_products, source: :sources_from_products - has_one :sources_from_2x_product, through: :from_product, source: :sources_from_product - has_many :sources_to_2x_products, through: :to_products, source: :sources_to_products - has_one :sources_to_2x_product, through: :to_product, source: :sources_to_product - has_many :from_2x_products, through: :sources_from_2x_products, source: :from_product - has_one :from_2x_product, through: :sources_from_2x_product, source: :from_product - has_many :to_2x_products, through: :sources_to_2x_products, source: :to_product - has_one :to_2x_product, through: :sources_to_2x_product, source: :to_product - - # semantic alias for supplier_from_product(s) - has_many :sources_supplier_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' - has_one :sources_supplier_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' - has_many :supplier_products, -> { order 'id ASC' }, through: :sources_supplier_products, source: :from_product - has_one :supplier_product, -> { order 'id ASC' }, through: :sources_supplier_product, source: :from_product, autosave: true - has_many :suppliers, -> { distinct.order 'id ASC' }, through: :sources_supplier_products - has_one :supplier, -> { order 'id ASC' }, through: :sources_supplier_product - - has_many :consumers, -> { distinct.order 'id ASC' }, through: :to_products, source: :profile - has_one :consumer, -> { order 'id ASC' }, through: :to_product, source: :profile - - # overhide original, FIXME: rename to available_and_supplier_active - scope :available, -> { - joins(:suppliers). - where 'products.available = ? AND suppliers_plugin_suppliers.active = ?', true, true - } - scope :unavailable, -> { - where 'products.available <> ? OR suppliers_plugin_suppliers.active <> ?', true, true - } - scope :with_available, -> (available) { - op = if available then '=' else '<>' end - cond = if available then 'AND' else 'OR' end - where "products.available #{op} ? #{cond} suppliers_plugin_suppliers.active #{op} ?", true, true - } - - scope :fp_name_like, -> (name) { where "from_products_products.name ILIKE ?", "%#{name}%" } - scope :fp_with_product_category_id, -> (id) { where 'from_products_products.product_category_id = ?', id } - - # prefer distributed_products has_many to use DistributedProduct scopes and eager loading - scope :distributed, -> { where type: 'SuppliersPlugin::DistributedProduct'} - scope :own, -> { where type: nil } - scope :supplied, -> { - # this remove duplicates and allow sorting on the fields, unlike distinct - group('products.id'). - where type: [nil, 'SuppliersPlugin::DistributedProduct'] - } - scope :supplied_for_count, -> { - distinct. - where type: [nil, 'SuppliersPlugin::DistributedProduct'] - } - - scope :from_supplier, -> (supplier) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier.id } - scope :from_supplier_id, -> (supplier_id) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier_id } - - after_create :distribute_to_consumers - - def own? - self.class == Product - end - def distributed? - self.class == SuppliersPlugin::DistributedProduct - end - def supplied? - self.own? or self.distributed? - end - - def supplier - # FIXME: use self.suppliers when rails support for nested preload comes - @supplier ||= self.sources_supplier_product.supplier rescue nil - @supplier ||= self.profile.self_supplier rescue nil - end - def supplier= value - @supplier = value - end - def supplier_id - self.supplier.id - end - def supplier_id= id - @supplier = profile.environment.profiles.find id - end - - def supplier_dummy? - self.supplier ? self.supplier.dummy? : self.profile.dummy? - end - - def distribute_to_consumer consumer, attrs = {} - distributed_product = consumer.distributed_products.where(profile_id: consumer.id, from_products_products: {id: self.id}).first - distributed_product ||= SuppliersPlugin::DistributedProduct.create! profile: consumer, from_product: self - distributed_product.update! attrs if attrs.present? - distributed_product - end - - def destroy_dependent - self.to_products.each do |to_product| - to_product.destroy if to_product.respond_to? :dependent? and to_product.dependent? - end - end - - # before_destroy and after_destroy don't work, - # see http://stackoverflow.com/questions/14175330/associations-not-loaded-in-before-destroy-callback - def destroy - self.class.transaction do - self.destroy_dependent - super - end - end - - def diff from = self.from_product - return @changed_attrs if @changed_attrs - @changed_attrs = [] - SuppliersPlugin::BaseProduct::CORE_DEFAULT_ATTRIBUTES.each do |attr| - @changed_attrs << attr if self[attr].present? and self[attr] != from[attr] - end - @changed_attrs - end - - protected - - def distribute_to_consumers - # shopping_cart creates products without a profile... - return unless self.profile - - self.profile.consumers.except_people.except_self.each do |consumer| - self.distribute_to_consumer consumer.profile - end - end - -end diff --git a/plugins/suppliers/lib/ext/products_plugin/product.rb b/plugins/suppliers/lib/ext/products_plugin/product.rb new file mode 100644 index 0000000..6571c15 --- /dev/null +++ b/plugins/suppliers/lib/ext/products_plugin/product.rb @@ -0,0 +1,178 @@ +require_dependency 'products_plugin/product' + +# FIXME: The lines bellow should be on the core +class Product + + extend CurrencyHelper::ClassMethods + has_currency :price + has_currency :discount + + scope :alphabetically, -> { order 'products.name ASC' } + + scope :available, -> { where available: true } + scope :unavailable, -> { where 'products.available <> true' } + scope :archived, -> { where archived: true } + scope :unarchived, -> { where 'products.archived <> true' } + + scope :with_available, -> (available) { where available: available } + scope :with_price, -> { where 'products.price > 0' } + + # FIXME: transliterate input and name column + scope :name_like, -> (name) { where "name ILIKE ?", "%#{name}%" } + scope :with_product_category_id, -> (id) { where product_category_id: id } + + scope :by_profile, -> (profile) { where profile_id: profile.id } + scope :by_profile_id, -> (profile_id) { where profile_id: profile_id } + + def self.product_categories_of products + ProductCategory.find products.collect(&:product_category_id).compact.select{ |id| not id.zero? } + end + + attr_accessible :external_id + settings_items :external_id, type: String, default: nil + + # should be on core, used by SuppliersPlugin::Import + attr_accessible :price_details + +end + +class Product + + attr_accessible :from_products, :from_product, :supplier_id, :supplier + + has_many :sources_from_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy + has_one :sources_from_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' + has_many :sources_to_products, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct', dependent: :destroy + has_one :sources_to_product, foreign_key: :from_product_id, class_name: 'SuppliersPlugin::SourceProduct' + has_many :to_products, -> { order 'id ASC' }, through: :sources_to_products + has_one :to_product, -> { order 'id ASC' }, through: :sources_to_product, autosave: true + has_many :from_products, -> { order 'id ASC' }, through: :sources_from_products + has_one :from_product, -> { order 'id ASC' }, through: :sources_from_product, autosave: true + + has_many :sources_from_2x_products, through: :from_products, source: :sources_from_products + has_one :sources_from_2x_product, through: :from_product, source: :sources_from_product + has_many :sources_to_2x_products, through: :to_products, source: :sources_to_products + has_one :sources_to_2x_product, through: :to_product, source: :sources_to_product + has_many :from_2x_products, through: :sources_from_2x_products, source: :from_product + has_one :from_2x_product, through: :sources_from_2x_product, source: :from_product + has_many :to_2x_products, through: :sources_to_2x_products, source: :to_product + has_one :to_2x_product, through: :sources_to_2x_product, source: :to_product + + # semantic alias for supplier_from_product(s) + has_many :sources_supplier_products, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' + has_one :sources_supplier_product, foreign_key: :to_product_id, class_name: 'SuppliersPlugin::SourceProduct' + has_many :supplier_products, -> { order 'id ASC' }, through: :sources_supplier_products, source: :from_product + has_one :supplier_product, -> { order 'id ASC' }, through: :sources_supplier_product, source: :from_product, autosave: true + has_many :suppliers, -> { distinct.order 'id ASC' }, through: :sources_supplier_products + has_one :supplier, -> { order 'id ASC' }, through: :sources_supplier_product + + has_many :consumers, -> { distinct.order 'id ASC' }, through: :to_products, source: :profile + has_one :consumer, -> { order 'id ASC' }, through: :to_product, source: :profile + + # overhide original, FIXME: rename to available_and_supplier_active + scope :available, -> { + joins(:suppliers). + where 'products.available = ? AND suppliers_plugin_suppliers.active = ?', true, true + } + scope :unavailable, -> { + where 'products.available <> ? OR suppliers_plugin_suppliers.active <> ?', true, true + } + scope :with_available, -> (available) { + op = if available then '=' else '<>' end + cond = if available then 'AND' else 'OR' end + where "products.available #{op} ? #{cond} suppliers_plugin_suppliers.active #{op} ?", true, true + } + + scope :fp_name_like, -> (name) { where "from_products_products.name ILIKE ?", "%#{name}%" } + scope :fp_with_product_category_id, -> (id) { where 'from_products_products.product_category_id = ?', id } + + # prefer distributed_products has_many to use DistributedProduct scopes and eager loading + scope :distributed, -> { where type: 'SuppliersPlugin::DistributedProduct'} + scope :own, -> { where type: nil } + scope :supplied, -> { + # this remove duplicates and allow sorting on the fields, unlike distinct + group('products.id'). + where type: [nil, 'SuppliersPlugin::DistributedProduct'] + } + scope :supplied_for_count, -> { + distinct. + where type: [nil, 'SuppliersPlugin::DistributedProduct'] + } + + scope :from_supplier, -> (supplier) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier.id } + scope :from_supplier_id, -> (supplier_id) { joins(:suppliers).where 'suppliers_plugin_suppliers.id = ?', supplier_id } + + after_create :distribute_to_consumers + + def own? + self.class == Product + end + def distributed? + self.class == SuppliersPlugin::DistributedProduct + end + def supplied? + self.own? or self.distributed? + end + + def supplier + # FIXME: use self.suppliers when rails support for nested preload comes + @supplier ||= self.sources_supplier_product.supplier rescue nil + @supplier ||= self.profile.self_supplier rescue nil + end + def supplier= value + @supplier = value + end + def supplier_id + self.supplier.id + end + def supplier_id= id + @supplier = profile.environment.profiles.find id + end + + def supplier_dummy? + self.supplier ? self.supplier.dummy? : self.profile.dummy? + end + + def distribute_to_consumer consumer, attrs = {} + distributed_product = consumer.distributed_products.where(profile_id: consumer.id, from_products_products: {id: self.id}).first + distributed_product ||= SuppliersPlugin::DistributedProduct.create! profile: consumer, from_product: self + distributed_product.update! attrs if attrs.present? + distributed_product + end + + def destroy_dependent + self.to_products.each do |to_product| + to_product.destroy if to_product.respond_to? :dependent? and to_product.dependent? + end + end + + # before_destroy and after_destroy don't work, + # see http://stackoverflow.com/questions/14175330/associations-not-loaded-in-before-destroy-callback + def destroy + self.class.transaction do + self.destroy_dependent + super + end + end + + def diff from = self.from_product + return @changed_attrs if @changed_attrs + @changed_attrs = [] + SuppliersPlugin::BaseProduct::CORE_DEFAULT_ATTRIBUTES.each do |attr| + @changed_attrs << attr if self[attr].present? and self[attr] != from[attr] + end + @changed_attrs + end + + protected + + def distribute_to_consumers + # shopping_cart creates products without a profile... + return unless self.profile + + self.profile.consumers.except_people.except_self.each do |consumer| + self.distribute_to_consumer consumer.profile + end + end + +end -- libgit2 0.21.2