From 35d84fd374e7db884c6780daa222974b8fe80328 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Sun, 23 Aug 2015 19:57:47 +0900 Subject: [PATCH 01/58] Temporary implement Variants --- core/app/models/comable/option_type.rb | 6 ++ core/app/models/comable/option_value.rb | 6 ++ core/app/models/comable/order_item.rb | 36 +++++++-- core/app/models/comable/product.rb | 39 ++++++++-- core/app/models/comable/stock.rb | 33 +++++++-- core/app/models/comable/variant.rb | 24 ++++++ .../app/models/concerns/comable/cart_owner.rb | 2 +- .../20150823071425_create_comable_variants.rb | 9 +++ ...50823072622_create_comable_option_types.rb | 11 +++ ...0823072955_create_comable_option_values.rb | 11 +++ ...0_create_comable_variants_option_values.rb | 8 ++ ...nge_comable_products_and_comable_stocks.rb | 73 +++++++++++++++++++ ...150823102819_change_comable_order_items.rb | 47 ++++++++++++ spec/factories/comable/option_types.rb | 5 ++ spec/factories/comable/option_values.rb | 5 ++ spec/factories/comable/products.rb | 39 ++++++---- spec/factories/comable/stocks.rb | 23 +++--- spec/factories/comable/variants.rb | 5 ++ 18 files changed, 334 insertions(+), 48 deletions(-) create mode 100644 core/app/models/comable/option_type.rb create mode 100644 core/app/models/comable/option_value.rb create mode 100644 core/app/models/comable/variant.rb create mode 100644 core/db/migrate/20150823071425_create_comable_variants.rb create mode 100644 core/db/migrate/20150823072622_create_comable_option_types.rb create mode 100644 core/db/migrate/20150823072955_create_comable_option_values.rb create mode 100644 core/db/migrate/20150823073250_create_comable_variants_option_values.rb create mode 100644 core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb create mode 100644 core/db/migrate/20150823102819_change_comable_order_items.rb create mode 100644 spec/factories/comable/option_types.rb create mode 100644 spec/factories/comable/option_values.rb create mode 100644 spec/factories/comable/variants.rb diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb new file mode 100644 index 00000000..a7e32512 --- /dev/null +++ b/core/app/models/comable/option_type.rb @@ -0,0 +1,6 @@ +module Comable + class OptionType < ActiveRecord::Base + has_many :option_values, class_name: Comable::OptionValue.name + belongs_to :product, class_name: Comable::Product.name + end +end diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb new file mode 100644 index 00000000..c703d45a --- /dev/null +++ b/core/app/models/comable/option_value.rb @@ -0,0 +1,6 @@ +module Comable + class OptionValue < ActiveRecord::Base + belongs_to :option_type, class_name: Comable::OptionType.name + has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values + end +end diff --git a/core/app/models/comable/order_item.rb b/core/app/models/comable/order_item.rb index eb75d4a8..ae304272 100644 --- a/core/app/models/comable/order_item.rb +++ b/core/app/models/comable/order_item.rb @@ -5,6 +5,7 @@ class OrderItem < ActiveRecord::Base include Comable::Liquidable include Comable::OrderItem::Csvable + belongs_to :variant, class_name: Comable::Variant.name, autosave: true belongs_to :stock, class_name: Comable::Stock.name, autosave: true belongs_to :order, class_name: Comable::Order.name, inverse_of: :order_items @@ -13,6 +14,7 @@ class OrderItem < ActiveRecord::Base liquid_methods :name, :name_with_sku, :code, :quantity, :price, :subtotal_price + delegate :stock, to: :variant delegate :product, to: :stock delegate :image_url, to: :product delegate :guest_token, to: :order @@ -58,6 +60,31 @@ def unstocked? end end + def sku_h_item_name + product.option_types.first.try(:name) + end + + def sku_v_item_name + product.option_types.second.try(:name) + end + + def sku_h_choice_name + variant.option_values.first.try(:name) + end + + def sku_v_choice_name + variant.option_values.second.try(:name) + end + + # + # Deprecated methods + # + deprecate :stock, deprecator: Comable::Deprecator.instance + deprecate :sku_h_item_name, deprecator: Comable::Deprecator.instance + deprecate :sku_v_item_name, deprecator: Comable::Deprecator.instance + deprecate :sku_h_choice_name, deprecator: Comable::Deprecator.instance + deprecate :sku_v_choice_name, deprecator: Comable::Deprecator.instance + private def valid_stock_quantity @@ -85,13 +112,8 @@ def increment_stock def current_attributes { - name: product.name, - code: stock.code, - price: stock.price, - sku_h_item_name: product.sku_h_item_name, - sku_v_item_name: product.sku_v_item_name, - sku_h_choice_name: stock.sku_h_choice_name, - sku_v_choice_name: stock.sku_v_choice_name + name: stock.name_with_sku, + price: stock.price } end end diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index c5946794..ca38202a 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -7,17 +7,15 @@ class Product < ActiveRecord::Base include Comable::Product::Csvable include Comable::Linkable - has_many :stocks, class_name: Comable::Stock.name, dependent: :destroy + has_many :variants, class_name: Comable::Variant.name, dependent: :destroy + has_many :stocks, class_name: Comable::Stock.name, through: :variants has_many :images, class_name: Comable::Image.name, dependent: :destroy + has_many :option_types, class_name: Comable::OptionType.name, dependent: :destroy has_and_belongs_to_many :categories, class_name: Comable::Category.name, join_table: :comable_products_categories accepts_nested_attributes_for :images, allow_destroy: true validates :name, presence: true, length: { maximum: 255 } - validates :code, presence: true, length: { maximum: 255 } - validates :price, presence: true, numericality: { greater_than_or_equal_to: 0, allow_blank: true } - validates :sku_h_item_name, length: { maximum: 255 } - validates :sku_v_item_name, length: { maximum: 255 } liquid_methods :id, :code, :name, :price, :images, :image_url @@ -53,10 +51,35 @@ def category_path_names=(category_path_names, delimiter: Comable::Category::DEFA self.categories = Comable::Category.find_by_path_names(category_path_names, delimiter: delimiter) end - private + def sku_h_item_name + option_types.first.try(:name) + end + + def sku_v_item_name + option_types.second.try(:name) + end + + def code + end - def create_stock - stocks.create(code: code) unless stocks.exists? + def code=(_code) end + + def price + variants.first.try(:price) + end + + def price=(price) + variants.each { |v| v.price = price } + end + + # + # Deprecated methods + # + deprecate :stocks, deprecator: Comable::Deprecator.instance + deprecate :sku_h_item_name, deprecator: Comable::Deprecator.instance + deprecate :sku_v_item_name, deprecator: Comable::Deprecator.instance + deprecate :code, deprecator: Comable::Deprecator.instance + deprecate :code=, deprecator: Comable::Deprecator.instance end end diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 00249384..4e9497d0 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -10,7 +10,7 @@ class Stock < ActiveRecord::Base include Comable::Liquidable include Comable::Stock::Csvable - belongs_to :product, class_name: Comable::Product.name + belongs_to :variant, class_name: Comable::Variant.name # # @!group Scope @@ -28,19 +28,18 @@ class Stock < ActiveRecord::Base # @!endgroup # - validates :product, presence: { message: Comable.t('admin.is_not_exists') } - validates :code, presence: true, length: { maximum: 255 } - validates :sku_h_choice_name, length: { maximum: 255 } - validates :sku_v_choice_name, length: { maximum: 255 } # TODO: add conditions (by limitless flag, backoder flag and etc..) validates :quantity, numericality: { greater_than_or_equal_to: 0 } + # TODO: Remove the columns for compatible + delegate :product, to: :variant delegate :name, to: :product - delegate :price, to: :product + delegate :price, to: :variant + delegate :code, to: :variant delegate :sku_h_item_name, to: :product delegate :sku_v_item_name, to: :product - ransack_options attribute_select: { associations: :product }, ransackable_attributes: { except: :product_id } + ransack_options attribute_select: { associations: :variant }, ransackable_attributes: { except: :variant_id } # 在庫の有無を取得する # @@ -67,5 +66,25 @@ def stocked?(quantity: 1) def unstocked?(quantity: 1) !stocked?(quantity: quantity) end + + def sku_h_choice_name + variant.option_values.first.try(:name) + end + + def sku_v_choice_name + variant.option_values.second.try(:name) + end + + # + # Deprecated methods + # + deprecate :product, deprecator: Comable::Deprecator.instance + deprecate :name, deprecator: Comable::Deprecator.instance + deprecate :code, deprecator: Comable::Deprecator.instance + deprecate :price, deprecator: Comable::Deprecator.instance + deprecate :sku_h_item_name, deprecator: Comable::Deprecator.instance + deprecate :sku_v_item_name, deprecator: Comable::Deprecator.instance + deprecate :sku_h_choice_name, deprecator: Comable::Deprecator.instance + deprecate :sku_v_choice_name, deprecator: Comable::Deprecator.instance end end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb new file mode 100644 index 00000000..e4212c06 --- /dev/null +++ b/core/app/models/comable/variant.rb @@ -0,0 +1,24 @@ +module Comable + class Variant < ActiveRecord::Base + belongs_to :product, class_name: Comable::Product.name + has_one :stock, class_name: Comable::Stock.name, dependent: :destroy + has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, dependent: :destroy, join_table: :comable_variants_option_values + + accepts_nested_attributes_for :option_values, allow_destroy: true + + #validates :product, presence: { message: Comable.t('admin.is_not_exists') } + validates :price, presence: true, numericality: { greater_than_or_equal_to: 0, allow_blank: true } + + def code + end + + def code=(_code) + end + + # + # Deprecated methods + # + deprecate :code, deprecator: Comable::Deprecator.instance + deprecate :code=, deprecator: Comable::Deprecator.instance + end +end diff --git a/core/app/models/concerns/comable/cart_owner.rb b/core/app/models/concerns/comable/cart_owner.rb index 5606e575..1287a35b 100644 --- a/core/app/models/concerns/comable/cart_owner.rb +++ b/core/app/models/concerns/comable/cart_owner.rb @@ -70,7 +70,7 @@ def add_stock_to_cart(stock, quantity) cart_item.quantity += quantity (cart_item.quantity > 0) ? cart_item.save : cart_item.destroy else - cart_items.build(stock_id: stock.id, quantity: quantity).save + cart_items.build(variant: stock.variant, quantity: quantity).save end end diff --git a/core/db/migrate/20150823071425_create_comable_variants.rb b/core/db/migrate/20150823071425_create_comable_variants.rb new file mode 100644 index 00000000..2b739c91 --- /dev/null +++ b/core/db/migrate/20150823071425_create_comable_variants.rb @@ -0,0 +1,9 @@ +class CreateComableVariants < ActiveRecord::Migration + def change + create_table :comable_variants do |t| + t.references :product, null: false + t.integer :price, null: false, default: 0 + t.timestamps null: false + end + end +end diff --git a/core/db/migrate/20150823072622_create_comable_option_types.rb b/core/db/migrate/20150823072622_create_comable_option_types.rb new file mode 100644 index 00000000..208d8aa4 --- /dev/null +++ b/core/db/migrate/20150823072622_create_comable_option_types.rb @@ -0,0 +1,11 @@ +class CreateComableOptionTypes < ActiveRecord::Migration + def change + create_table :comable_option_types do |t| + t.references :product, null: false + t.string :name, null: false + t.timestamps null: false + end + + add_index :comable_option_types, [:product_id, :name], unique: true + end +end diff --git a/core/db/migrate/20150823072955_create_comable_option_values.rb b/core/db/migrate/20150823072955_create_comable_option_values.rb new file mode 100644 index 00000000..ca628d1b --- /dev/null +++ b/core/db/migrate/20150823072955_create_comable_option_values.rb @@ -0,0 +1,11 @@ +class CreateComableOptionValues < ActiveRecord::Migration + def change + create_table :comable_option_values do |t| + t.references :option_type, null: false + t.string :name, null: false + t.timestamps null: false + end + + add_index :comable_option_values, [:option_type_id, :name], unique: true + end +end diff --git a/core/db/migrate/20150823073250_create_comable_variants_option_values.rb b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb new file mode 100644 index 00000000..cfcf9df6 --- /dev/null +++ b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb @@ -0,0 +1,8 @@ +class CreateComableVariantsOptionValues < ActiveRecord::Migration + def change + create_table :comable_variants_option_values do |t| + t.references :variant, null: false, index: true + t.references :option_value, null: false, index: true + end + end +end diff --git a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb new file mode 100644 index 00000000..de1eb011 --- /dev/null +++ b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb @@ -0,0 +1,73 @@ +class ChangeComableProductsAndComableStocks < ActiveRecord::Migration + def change + reversible do |dir| + change_table :comable_stocks do |t| + dir.up { t.references :variant } + dir.down { t.remove :variant_id } + end + + dir.up { up_records } + dir.down { down_records } + + dir.up { remove_index :comable_products, :code } + dir.down { add_index :comable_products, :code, unique: true } + + change_table :comable_products do |t| + dir.up { t.remove :code } + dir.down { t.string :code, null: false } + + dir.up { t.remove :price } + dir.down { t.string :price, null: false } + + dir.up { t.remove :sku_h_item_name } + dir.down { t.string :sku_h_item_name } + + dir.up { t.remove :sku_v_item_name } + dir.down { t.string :sku_v_item_name } + end + + dir.up { remove_index :comable_stocks, :code } + dir.down { add_index :comable_stocks, :code, unique: true } + + change_table :comable_stocks do |t| + dir.up { t.remove :product_id } + dir.down { t.references :product, null: false } + + dir.up { t.remove :code } + dir.down { t.string :code, null: false } + + dir.up { t.remove :sku_h_choice_name } + dir.down { t.string :sku_h_choice_name } + + dir.up { t.remove :sku_v_choice_name } + dir.down { t.string :sku_v_choice_name } + + dir.up { t.change :variant_id, :integer, null: false } + end + end + end + + private + + def up_records + Comable::Product.all.each do |product| + if product.sku_h_item_name? || product.sku_h_item_name? + option_type_h = product.option_types.create!(name: product.sku_h_item_name) if product.sku_h_item_name? + option_type_v = product.option_types.create!(name: product.sku_v_item_name) if product.sku_v_item_name? + + Comable::Stock.where(product_id: product.id).each do |stock| + option_values = [] + option_values << option_type_h.option_values.where(name: stock.sku_h_choice_name).first_or_initialize if option_type_h + option_values << option_type_v.option_values.where(name: stock.sku_v_choice_name).first_or_initialize if option_type_v + product.variants.create!(stock: stock, price: product.price, option_values: option_values) + end + else + stock = Comable::Stock.find_by(product_id: product.id) + product.variants.create!(stock: stock, price: product.price) + end + end + end + + def down_records + end +end diff --git a/core/db/migrate/20150823102819_change_comable_order_items.rb b/core/db/migrate/20150823102819_change_comable_order_items.rb new file mode 100644 index 00000000..c716b9a2 --- /dev/null +++ b/core/db/migrate/20150823102819_change_comable_order_items.rb @@ -0,0 +1,47 @@ +class ChangeComableOrderItems < ActiveRecord::Migration + def change + reversible do |dir| + change_table :comable_order_items do |t| + dir.up { t.references :variant } + dir.down { t.remove :variant_id } + end + + dir.up { up_records } + dir.down { down_records } + + change_table :comable_order_items do |t| + dir.up { t.remove :stock_id } + dir.down { t.references :stock, null: false } + + dir.up { t.remove :code } + dir.down { t.string :code, null: false } + + dir.up { t.remove :sku_h_item_name } + dir.down { t.string :sku_h_item_name } + + dir.up { t.remove :sku_v_item_name } + dir.down { t.string :sku_v_item_name } + + dir.up { t.remove :sku_h_choice_name } + dir.down { t.string :sku_h_choice_name } + + dir.up { t.remove :sku_v_choice_name } + dir.down { t.string :sku_v_choice_name } + + dir.up { t.change :variant_id, :integer, null: false } + end + end + end + + private + + def up_records + Comable::OrderItem.all.each do |order_item| + stock = Comable::Stock.find(order_item.stock_id) + order_item.update_attribute(:variant_id, stock.variant_id) + end + end + + def down_records + end +end diff --git a/spec/factories/comable/option_types.rb b/spec/factories/comable/option_types.rb new file mode 100644 index 00000000..bac56d42 --- /dev/null +++ b/spec/factories/comable/option_types.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :option_type, class: Comable::OptionType do + sequence(:name) { |n| "Option Type ##{n.next}" } + end +end diff --git a/spec/factories/comable/option_values.rb b/spec/factories/comable/option_values.rb new file mode 100644 index 00000000..77ed9aa8 --- /dev/null +++ b/spec/factories/comable/option_values.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :option_value, class: Comable::OptionValue do + sequence(:name) { |n| "Option Value ##{n.next}" } + end +end diff --git a/spec/factories/comable/products.rb b/spec/factories/comable/products.rb index 9cbbdfad..fd79a549 100644 --- a/spec/factories/comable/products.rb +++ b/spec/factories/comable/products.rb @@ -1,29 +1,42 @@ FactoryGirl.define do - factory :product, class: 'Comable::Product' do + factory :product, class: Comable::Product do sequence(:name) { |n| "test_product#{n.next}" } - sequence(:code) { |n| format('%07d', n.next) } - price 100 published_at Date.today + variants { [build(:variant)] } + trait :with_stock do - stocks { [create(:stock, :stocked)] } + stocks { [create(:stock, :stocked, variant: variants.first)] } end trait :sku do - sku_h_item_name 'カラー' - sku_v_item_name 'サイズ' - stocks do - [ - create(:stock, :stocked, sku_h_choice_name: 'レッド', sku_v_choice_name: 'S'), - create(:stock, :stocked, sku_h_choice_name: 'レッド', sku_v_choice_name: 'M'), - create(:stock, :stocked, sku_h_choice_name: 'レッド', sku_v_choice_name: 'L') + after(:build) do |product| + product.save! + + color = build(:option_type, name: 'Color') + size = build(:option_type, name: 'Size') + product.option_types = [color, size] + + color_red = color.option_values.build(name: 'Red') + size_s = size.option_values.build(name: 'S') + size_m = size.option_values.build(name: 'M') + size_l = size.option_values.build(name: 'L') + product.variants = [ + build(:variant, option_values: [color_red, size_s], stock: build(:stock)), + build(:variant, option_values: [color_red, size_m], stock: build(:stock)), + build(:variant, option_values: [color_red, size_l], stock: build(:stock)) ] end end trait :sku_h do - sku_h_item_name 'カラー' - stocks { [create(:stock, :stocked, sku_h_choice_name: 'レッド')] } + after(:build) do |product| + color = build(:option_type, name: 'Color') + product.option_types = [color] + + color_red = color.option_values.build(name: 'Red') + product.variants = [build(:variant, option_values: [color_red])] + end end end end diff --git a/spec/factories/comable/stocks.rb b/spec/factories/comable/stocks.rb index a22bfe6c..96a9053d 100644 --- a/spec/factories/comable/stocks.rb +++ b/spec/factories/comable/stocks.rb @@ -1,24 +1,23 @@ FactoryGirl.define do - factory :stock, class: 'Comable::Stock' do - sequence(:code) { |n| format('%07d', n.next) } + factory :stock, class: Comable::Stock do + variant_id 0 quantity 0 - product { build_stubbed(:product, code: code) } transient { sku_flag false } trait :sku do - transient { sku_flag true } - sku_h_choice_name 'レッド' - sku_v_choice_name 'S' - product { build_stubbed(:product, code: code, sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') } + # transient { sku_flag true } + # sku_h_choice_name 'レッド' + # sku_v_choice_name 'S' + # product { build_stubbed(:product, code: code, sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') } end trait :with_product do - after(:create) do |stock, evaluator| - attributes = { code: stock.code, stocks: [stock] } - attributes.update(sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') if evaluator.sku_flag - create(:product, attributes) - end + # after(:create) do |stock, evaluator| + # attributes = { code: stock.code, stocks: [stock] } + # attributes.update(sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') if evaluator.sku_flag + # create(:product, attributes) + # end end trait :unstocked do diff --git a/spec/factories/comable/variants.rb b/spec/factories/comable/variants.rb new file mode 100644 index 00000000..ca7ebc17 --- /dev/null +++ b/spec/factories/comable/variants.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :variant, class: Comable::Variant do + price 100 + end +end From 23b13f6989c1e34c56343e1c26c9b9efcd893685 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 09:10:21 +0900 Subject: [PATCH 02/58] Add the sku column to Variant --- core/app/models/comable/product.rb | 4 +++- core/app/models/comable/stock.rb | 9 ++++++++- .../db/migrate/20150823071425_create_comable_variants.rb | 1 + ...3073809_change_comable_products_and_comable_stocks.rb | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index ca38202a..8132701b 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -60,9 +60,11 @@ def sku_v_item_name end def code + variants.first.try(:sku) end - def code=(_code) + def code=(code) + variants.each { |v| v.sku = code } end def price diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 4e9497d0..3ae75d82 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -35,7 +35,6 @@ class Stock < ActiveRecord::Base delegate :product, to: :variant delegate :name, to: :product delegate :price, to: :variant - delegate :code, to: :variant delegate :sku_h_item_name, to: :product delegate :sku_v_item_name, to: :product @@ -75,6 +74,14 @@ def sku_v_choice_name variant.option_values.second.try(:name) end + def code + variant.sku + end + + def code=(code) + variant.sku = code + end + # # Deprecated methods # diff --git a/core/db/migrate/20150823071425_create_comable_variants.rb b/core/db/migrate/20150823071425_create_comable_variants.rb index 2b739c91..6b99357a 100644 --- a/core/db/migrate/20150823071425_create_comable_variants.rb +++ b/core/db/migrate/20150823071425_create_comable_variants.rb @@ -3,6 +3,7 @@ def change create_table :comable_variants do |t| t.references :product, null: false t.integer :price, null: false, default: 0 + t.string :sku t.timestamps null: false end end diff --git a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb index de1eb011..22588ed6 100644 --- a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb +++ b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb @@ -59,11 +59,11 @@ def up_records option_values = [] option_values << option_type_h.option_values.where(name: stock.sku_h_choice_name).first_or_initialize if option_type_h option_values << option_type_v.option_values.where(name: stock.sku_v_choice_name).first_or_initialize if option_type_v - product.variants.create!(stock: stock, price: product.price, option_values: option_values) + product.variants.create!(stock: stock, price: product.price, sku: stock.code, option_values: option_values) end else stock = Comable::Stock.find_by(product_id: product.id) - product.variants.create!(stock: stock, price: product.price) + product.variants.create!(stock: stock, price: product.price, sku: product.code) end end end From 938095d44242c31dc59ca7f81289df48808c6182 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 16:47:00 +0900 Subject: [PATCH 03/58] Fix to pass the test of OrdersController --- core/app/models/comable/order_item.rb | 1 - core/app/models/comable/product.rb | 15 ++++++++++++--- core/app/models/comable/stock.rb | 2 +- core/app/models/comable/variant.rb | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/core/app/models/comable/order_item.rb b/core/app/models/comable/order_item.rb index ae304272..254e0890 100644 --- a/core/app/models/comable/order_item.rb +++ b/core/app/models/comable/order_item.rb @@ -6,7 +6,6 @@ class OrderItem < ActiveRecord::Base include Comable::OrderItem::Csvable belongs_to :variant, class_name: Comable::Variant.name, autosave: true - belongs_to :stock, class_name: Comable::Stock.name, autosave: true belongs_to :order, class_name: Comable::Order.name, inverse_of: :order_items validates :quantity, numericality: { greater_than: 0 } diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 8132701b..f73d9377 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -7,8 +7,7 @@ class Product < ActiveRecord::Base include Comable::Product::Csvable include Comable::Linkable - has_many :variants, class_name: Comable::Variant.name, dependent: :destroy - has_many :stocks, class_name: Comable::Stock.name, through: :variants + has_many :variants, class_name: Comable::Variant.name, inverse_of: :product, dependent: :destroy has_many :images, class_name: Comable::Image.name, dependent: :destroy has_many :option_types, class_name: Comable::OptionType.name, dependent: :destroy has_and_belongs_to_many :categories, class_name: Comable::Category.name, join_table: :comable_products_categories @@ -19,7 +18,7 @@ class Product < ActiveRecord::Base liquid_methods :id, :code, :name, :price, :images, :image_url - ransack_options attribute_select: { associations: :stocks } + ransack_options attribute_select: { associations: :variants } linkable_columns_keys use_index: true @@ -75,6 +74,16 @@ def price=(price) variants.each { |v| v.price = price } end + has_many :stocks, class_name: Comable::Stock.name, through: :variants + + #def stocks + # variants.map(&:stock) + #end + + def stocks=(stocks) + stocks.map { |stock| variants.build(stock: stock) } + end + # # Deprecated methods # diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 3ae75d82..e13dfc6b 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -10,7 +10,7 @@ class Stock < ActiveRecord::Base include Comable::Liquidable include Comable::Stock::Csvable - belongs_to :variant, class_name: Comable::Variant.name + belongs_to :variant, class_name: Comable::Variant.name, inverse_of: :stock # # @!group Scope diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index e4212c06..e2e51cb0 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -1,7 +1,7 @@ module Comable class Variant < ActiveRecord::Base - belongs_to :product, class_name: Comable::Product.name - has_one :stock, class_name: Comable::Stock.name, dependent: :destroy + belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants + has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, dependent: :destroy, join_table: :comable_variants_option_values accepts_nested_attributes_for :option_values, allow_destroy: true From b0b1872a7c6e2fb07d7ebf92b1f2dddf686aa81e Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 17:47:18 +0900 Subject: [PATCH 04/58] Fix to pass the test of Admin::StocksController --- .../controllers/comable/admin/stocks_controller.rb | 7 +++++-- .../comable/admin/stocks_controller_spec.rb | 8 ++++---- core/app/models/comable/stock.rb | 14 +++++++++++++- core/app/models/comable/stock/csvable.rb | 11 +++++------ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/backend/app/controllers/comable/admin/stocks_controller.rb b/backend/app/controllers/comable/admin/stocks_controller.rb index 961ff69a..89c33518 100644 --- a/backend/app/controllers/comable/admin/stocks_controller.rb +++ b/backend/app/controllers/comable/admin/stocks_controller.rb @@ -10,7 +10,7 @@ class StocksController < Comable::Admin::ApplicationController def index @q = @stocks.ransack(params[:q]) - @stocks = @q.result.includes(:product).page(params[:page]).accessible_by(current_ability) + @stocks = @q.result.includes(variant: :product).page(params[:page]).accessible_by(current_ability) end def show @@ -21,6 +21,9 @@ def new end def create + # TODO: Remove + @stock.build_variant(product: @product) unless @stock.variant + if @stock.save redirect_to comable.admin_stock_path(@stock), notice: Comable.t('successful') else @@ -52,7 +55,7 @@ def destroy def export q = @stocks.ransack(params[:q]) - stocks = q.result.includes(:product).accessible_by(current_ability) + stocks = q.result.includes(variant: :product).accessible_by(current_ability) respond_to_export_with stocks end diff --git a/backend/spec/controllers/comable/admin/stocks_controller_spec.rb b/backend/spec/controllers/comable/admin/stocks_controller_spec.rb index 7e7e6e2b..74fc1a4e 100644 --- a/backend/spec/controllers/comable/admin/stocks_controller_spec.rb +++ b/backend/spec/controllers/comable/admin/stocks_controller_spec.rb @@ -4,7 +4,7 @@ let(:comable) { controller.comable } let(:valid_attributes) { attributes_for(:stock) } - let(:invalid_attributes) { valid_attributes.merge(code: 'x' * 1024) } + let(:invalid_attributes) { valid_attributes.merge(quantity: -100) } let(:product) { create(:product) } @@ -75,7 +75,7 @@ let!(:stock) { create(:stock, product: product) } describe 'with valid params' do - let(:new_attributes) { { code: "new_#{stock.code}" } } + let(:new_attributes) { { quantity: stock.quantity + 100 } } it 'updates the requested stock' do put :update, id: stock.to_param, stock: new_attributes @@ -124,8 +124,8 @@ it 'exports the csv file' do stock = create(:stock, product: product) get :export, format: :csv - expect(response.body).to include(product.code) - expect(response.body).to include(stock.code) + expect(response.body).to include(product.id.to_s) + expect(response.body).to include(stock.id.to_s) expect(response.body).to include(stock.quantity.to_s) end diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index e13dfc6b..9a714cad 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -79,7 +79,19 @@ def code end def code=(code) - variant.sku = code + if variant + variant.sku = code + else + build_variant(sku: code) + end + end + + def product=(product) + if variant + variant.product = product + else + build_variant(product: product) + end end # diff --git a/core/app/models/comable/stock/csvable.rb b/core/app/models/comable/stock/csvable.rb index 5afe9415..276cfa1b 100644 --- a/core/app/models/comable/stock/csvable.rb +++ b/core/app/models/comable/stock/csvable.rb @@ -7,19 +7,18 @@ module Csvable included do comma do - product_code - code + product_id + id quantity sku_h_choice_name sku_v_choice_name end end - delegate :code, to: :product, prefix: true, allow_nil: true + delegate :id, to: :product, prefix: true, allow_nil: true - def product_code=(code) - return if product_code == code - self.product = Comable::Product.find_by(code: code) + def product_id=(id) + self.product = Comable::Product.find_by(id: id) end end end From 5fc3528993c317a57612aa62eeca8a14f1c259f9 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 18:07:54 +0900 Subject: [PATCH 05/58] Fix to pass the test of User --- core/app/models/comable/product.rb | 2 ++ core/spec/models/comable/user_spec.rb | 2 +- spec/factories/comable/stocks.rb | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index f73d9377..f3ea0708 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -12,7 +12,9 @@ class Product < ActiveRecord::Base has_many :option_types, class_name: Comable::OptionType.name, dependent: :destroy has_and_belongs_to_many :categories, class_name: Comable::Category.name, join_table: :comable_products_categories + accepts_nested_attributes_for :variants, allow_destroy: true accepts_nested_attributes_for :images, allow_destroy: true + accepts_nested_attributes_for :option_types, allow_destroy: true validates :name, presence: true, length: { maximum: 255 } diff --git a/core/spec/models/comable/user_spec.rb b/core/spec/models/comable/user_spec.rb index bc588d27..6957f7ad 100644 --- a/core/spec/models/comable/user_spec.rb +++ b/core/spec/models/comable/user_spec.rb @@ -29,7 +29,7 @@ order = subject.incomplete_order expect(order.order_items.size).to eq(0) - order.order_items.create(stock: stock, quantity: 1) + order.order_items.create(variant: stock.variant, quantity: 1) expect(order.order_items.size).to eq(1) expect(order.reload.order_items.size).to eq(1) end diff --git a/spec/factories/comable/stocks.rb b/spec/factories/comable/stocks.rb index 96a9053d..f23a677b 100644 --- a/spec/factories/comable/stocks.rb +++ b/spec/factories/comable/stocks.rb @@ -6,18 +6,19 @@ transient { sku_flag false } trait :sku do - # transient { sku_flag true } - # sku_h_choice_name 'レッド' - # sku_v_choice_name 'S' - # product { build_stubbed(:product, code: code, sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') } + transient { sku_flag true } end trait :with_product do - # after(:create) do |stock, evaluator| - # attributes = { code: stock.code, stocks: [stock] } - # attributes.update(sku_h_item_name: 'カラー', sku_v_item_name: 'サイズ') if evaluator.sku_flag - # create(:product, attributes) - # end + after(:create) do |stock, evaluator| + option_types_attributes = [name: 'Color'] + [name: 'Size'] if evaluator.sku_flag + product = build(:product, option_types_attributes: option_types_attributes || []) + + option_values_attributes = [option_type: product.option_types.first, name: 'Red'] + [option_type: product.option_types.first, name: 'S'] if evaluator.sku_flag + variant = build(:variant, product: product, option_values_attributes: option_values_attributes || []) + + stock.variant = variant + end end trait :unstocked do From 65315066fbeda68311462b744856419c505318bb Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 18:36:10 +0900 Subject: [PATCH 06/58] Fix to pass the test of Order --- core/app/models/comable/order_item.rb | 12 +++++++++++- .../20150823102819_change_comable_order_items.rb | 4 ++-- spec/factories/comable/order_items.rb | 4 ++-- spec/factories/comable/variants.rb | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/app/models/comable/order_item.rb b/core/app/models/comable/order_item.rb index 254e0890..8acf023f 100644 --- a/core/app/models/comable/order_item.rb +++ b/core/app/models/comable/order_item.rb @@ -9,6 +9,7 @@ class OrderItem < ActiveRecord::Base belongs_to :order, class_name: Comable::Order.name, inverse_of: :order_items validates :quantity, numericality: { greater_than: 0 } + validates :sku, length: { maximum: 255 } validate :valid_stock_quantity liquid_methods :name, :name_with_sku, :code, :quantity, :price, :subtotal_price @@ -75,6 +76,14 @@ def sku_v_choice_name variant.option_values.second.try(:name) end + def stock=(stock) + if variant + variant.stock = stock + else + build_variant(stock: stock) + end + end + # # Deprecated methods # @@ -112,7 +121,8 @@ def increment_stock def current_attributes { name: stock.name_with_sku, - price: stock.price + price: stock.price, + sku: variant.sku } end end diff --git a/core/db/migrate/20150823102819_change_comable_order_items.rb b/core/db/migrate/20150823102819_change_comable_order_items.rb index c716b9a2..4f0e8e6c 100644 --- a/core/db/migrate/20150823102819_change_comable_order_items.rb +++ b/core/db/migrate/20150823102819_change_comable_order_items.rb @@ -13,8 +13,8 @@ def change dir.up { t.remove :stock_id } dir.down { t.references :stock, null: false } - dir.up { t.remove :code } - dir.down { t.string :code, null: false } + dir.up { t.rename :code, :sku; t.change :sku, :string, null: true } + dir.down { t.rename :sku, :code; t.change :code, :string, null: false } dir.up { t.remove :sku_h_item_name } dir.down { t.string :sku_h_item_name } diff --git a/spec/factories/comable/order_items.rb b/spec/factories/comable/order_items.rb index 51ed3d29..93a7ac5c 100644 --- a/spec/factories/comable/order_items.rb +++ b/spec/factories/comable/order_items.rb @@ -1,10 +1,10 @@ FactoryGirl.define do factory :order_item, class: Comable::OrderItem do sequence(:name) { |n| "test_product#{n.next}" } - sequence(:code) { |n| format('%07d', n.next) } + sequence(:sku) { |n| format('%07d', n.next) } quantity 10 price 100 - stock { create(:stock, :with_product, quantity: quantity) } + variant { create(:variant, stock: build(:stock, quantity: quantity), product: build(:product)) } order { build_stubbed(:order) } trait :sku do diff --git a/spec/factories/comable/variants.rb b/spec/factories/comable/variants.rb index ca7ebc17..88c6faee 100644 --- a/spec/factories/comable/variants.rb +++ b/spec/factories/comable/variants.rb @@ -1,5 +1,6 @@ FactoryGirl.define do factory :variant, class: Comable::Variant do price 100 + sequence(:sku) { |n| format('%07d', n.next) } end end From be7cf522660278e29a7f052850788ef17c448960 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 18:54:56 +0900 Subject: [PATCH 07/58] Fix to pass the test of Product and Stock --- core/app/models/comable/variant.rb | 3 ++- core/spec/models/comable/product_spec.rb | 34 +++++++----------------- core/spec/models/comable/stock_spec.rb | 23 +--------------- 3 files changed, 13 insertions(+), 47 deletions(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index e2e51cb0..0678f58b 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -7,7 +7,8 @@ class Variant < ActiveRecord::Base accepts_nested_attributes_for :option_values, allow_destroy: true #validates :product, presence: { message: Comable.t('admin.is_not_exists') } - validates :price, presence: true, numericality: { greater_than_or_equal_to: 0, allow_blank: true } + validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :sku, length: { maximum: 255 } def code end diff --git a/core/spec/models/comable/product_spec.rb b/core/spec/models/comable/product_spec.rb index c7b0a4a3..285bcf95 100644 --- a/core/spec/models/comable/product_spec.rb +++ b/core/spec/models/comable/product_spec.rb @@ -1,43 +1,29 @@ describe Comable::Product do - it { should be_truthy } + it { is_expected.to have_many(:variants).class_name(Comable::Variant.name) } + it { is_expected.to have_many(:images).class_name(Comable::Image.name) } + it { is_expected.to have_and_belong_to_many(:categories).class_name(Comable::Category.name) } - describe 'associations' do - it { is_expected.to have_many(:stocks).class_name(Comable::Stock.name) } - it { is_expected.to have_many(:images).class_name(Comable::Image.name) } - it { is_expected.to have_and_belong_to_many(:categories).class_name(Comable::Category.name) } - end - - describe 'validations' do - it { is_expected.to validate_presence_of(:name) } - it { is_expected.to validate_presence_of(:code) } - it { is_expected.to validate_presence_of(:price) } - - it { is_expected.to validate_length_of(:name).is_at_most(255) } - it { is_expected.to validate_length_of(:code).is_at_most(255) } - it { is_expected.to validate_length_of(:sku_h_item_name).is_at_most(255) } - it { is_expected.to validate_length_of(:sku_v_item_name).is_at_most(255) } + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } - it { is_expected.to validate_numericality_of(:price).is_greater_than_or_equal_to(0) } - end - - it 'where' do - expect { described_class.where(id: [1, 2]) }.not_to raise_error - end - - describe 'published?' do + describe '#published?' do subject { build(:product) } + it 'should be false when published_at is nil' do subject.published_at = nil expect(subject.published?).to be false end + it 'should be false when published_at greater than now' do subject.published_at = Time.now + 1 expect(subject.published?).to be false end + it 'should be true when published_at equal now' do subject.published_at = Time.now expect(subject.published?).to be true end + it 'should be true when published_at less than now' do subject.published_at = Time.now - 1 expect(subject.published?).to be true diff --git a/core/spec/models/comable/stock_spec.rb b/core/spec/models/comable/stock_spec.rb index 1df74855..a191ff0d 100644 --- a/core/spec/models/comable/stock_spec.rb +++ b/core/spec/models/comable/stock_spec.rb @@ -1,24 +1,3 @@ describe Comable::Stock do - it { expect { described_class.new }.to_not raise_error } - - context 'belongs_to' do - let(:stock) { create(:stock, :with_product) } - - describe 'product' do - subject { stock.product } - it { should be } - its(:name) { should be } - end - end - - describe 'validations' do - it { is_expected.to validate_presence_of(:product).with_message(Comable.t('admin.is_not_exists')) } - it { is_expected.to validate_presence_of(:code) } - - it { is_expected.to validate_length_of(:code).is_at_most(255) } - it { is_expected.to validate_length_of(:sku_h_choice_name).is_at_most(255) } - it { is_expected.to validate_length_of(:sku_v_choice_name).is_at_most(255) } - - it { is_expected.to validate_numericality_of(:quantity).is_greater_than_or_equal_to(0) } - end + it { is_expected.to validate_numericality_of(:quantity).is_greater_than_or_equal_to(0) } end From f0a8b792a4e36a5304b6f85bbb0015da1df89217 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 19:04:31 +0900 Subject: [PATCH 08/58] Fix to pass the test of OrderMailer --- spec/factories/comable/order_items.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/spec/factories/comable/order_items.rb b/spec/factories/comable/order_items.rb index 93a7ac5c..71d9e60f 100644 --- a/spec/factories/comable/order_items.rb +++ b/spec/factories/comable/order_items.rb @@ -8,11 +8,17 @@ order { build_stubbed(:order) } trait :sku do - sku_h_item_name 'カラー' - sku_v_item_name 'サイズ' - sku_h_choice_name 'レッド' - sku_v_choice_name 'S' - stock { create(:stock, :sku, :with_product, quantity: quantity) } + after(:build) do |order_item| + product = order_item.variant.try(:product) || build(:product) + product.option_types_attributes = [name: 'Color'] + [name: 'Size'] + + variant = order_item.variant || build(:variant) + variant.product = product + variant.stock = build(:stock, quantity: order_item.quantity) if variant.stock + variant.option_values_attributes = [option_type: product.option_types.first, name: 'Red'] + [option_type: product.option_types.first, name: 'S'] + + order_item.variant = variant + end end end end From dbe9c7559cef89d881e46ff25b31d090b6acd330 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 24 Aug 2015 19:18:54 +0900 Subject: [PATCH 09/58] Fix to pass the test of CartsController --- spec/factories/comable/products.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/factories/comable/products.rb b/spec/factories/comable/products.rb index fd79a549..f9ed90f1 100644 --- a/spec/factories/comable/products.rb +++ b/spec/factories/comable/products.rb @@ -22,9 +22,9 @@ size_m = size.option_values.build(name: 'M') size_l = size.option_values.build(name: 'L') product.variants = [ - build(:variant, option_values: [color_red, size_s], stock: build(:stock)), - build(:variant, option_values: [color_red, size_m], stock: build(:stock)), - build(:variant, option_values: [color_red, size_l], stock: build(:stock)) + build(:variant, option_values: [color_red, size_s], stock: build(:stock, :stocked)), + build(:variant, option_values: [color_red, size_m], stock: build(:stock, :stocked)), + build(:variant, option_values: [color_red, size_l], stock: build(:stock, :stocked)) ] end end From d406a1312dab9f6b1db5f24fdcf8a1b2cf4275d9 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 00:20:06 +0900 Subject: [PATCH 10/58] Fix to pass the test of ProductsController and Admin::OrdersController --- .../spec/controllers/comable/admin/orders_controller_spec.rb | 2 +- core/app/models/comable/order_item/csvable.rb | 2 +- spec/factories/comable/products.rb | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/spec/controllers/comable/admin/orders_controller_spec.rb b/backend/spec/controllers/comable/admin/orders_controller_spec.rb index 6957dba3..1d88e5fe 100644 --- a/backend/spec/controllers/comable/admin/orders_controller_spec.rb +++ b/backend/spec/controllers/comable/admin/orders_controller_spec.rb @@ -76,7 +76,7 @@ expect(response.body).to include(order.code) expect(response.body).to include(order.bill_address.first_name) expect(response.body).to include(order.bill_address.family_name) - expect(response.body).to include(order_item.code) + expect(response.body).to include(order_item.sku) end it 'exports the xlsx file' do diff --git a/core/app/models/comable/order_item/csvable.rb b/core/app/models/comable/order_item/csvable.rb index 3eefdeea..dcc59a15 100644 --- a/core/app/models/comable/order_item/csvable.rb +++ b/core/app/models/comable/order_item/csvable.rb @@ -18,7 +18,7 @@ module Csvable __association__ order: { bill_address: :detail } __association__ order: { bill_address: :phone_number } name - code + sku price sku_h_item_name sku_v_item_name diff --git a/spec/factories/comable/products.rb b/spec/factories/comable/products.rb index f9ed90f1..2c6113c0 100644 --- a/spec/factories/comable/products.rb +++ b/spec/factories/comable/products.rb @@ -31,6 +31,8 @@ trait :sku_h do after(:build) do |product| + product.save! + color = build(:option_type, name: 'Color') product.option_types = [color] From ff189923fe53f02643b5523360c45a8c536393e6 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 00:28:23 +0900 Subject: [PATCH 11/58] Fix to pass the test of CartOwner --- core/spec/models/concerns/comable/cart_owner_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/spec/models/concerns/comable/cart_owner_spec.rb b/core/spec/models/concerns/comable/cart_owner_spec.rb index b7ad2d3e..9bed9711 100644 --- a/core/spec/models/concerns/comable/cart_owner_spec.rb +++ b/core/spec/models/concerns/comable/cart_owner_spec.rb @@ -16,7 +16,7 @@ class DummyUser end context 'with errors' do - let(:stock) { build(:stock, :unstocked) } + let(:stock) { create(:stock, :unstocked, :with_product) } let(:cart_item) { subject.cart_items.first } before { allow(cart_item).to receive(:stock).and_return(stock) } From 4c03b87a54cc9f65716d91f879cd21b51decc6dd Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 00:39:55 +0900 Subject: [PATCH 12/58] Fix to pass the test of Views --- backend/app/views/comable/admin/products/index.slim | 2 +- backend/app/views/comable/admin/stocks/index.slim | 2 +- backend/spec/views/comable/admin/stocks/index.slim_spec.rb | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/app/views/comable/admin/products/index.slim b/backend/app/views/comable/admin/products/index.slim index 1c32fc6d..42969f90 100644 --- a/backend/app/views/comable/admin/products/index.slim +++ b/backend/app/views/comable/admin/products/index.slim @@ -47,7 +47,7 @@ = link_to Comable.t('admin.advanced_search'), 'javascript:void(0)', 'data-toggle' => 'collapse', 'data-target' => '#comable-advanced-search' li = link_to Comable.t('admin.clear_search_conditions'), comable.admin_products_path - = f.text_field :code_or_name_cont_any_splitted, class: 'form-control' + = f.text_field :name_or_variants_sku_cont_any_splitted, class: 'form-control' span.input-group-btn = f.submit Comable.t('admin.search'), class: 'btn btn-default' diff --git a/backend/app/views/comable/admin/stocks/index.slim b/backend/app/views/comable/admin/stocks/index.slim index 769d28ee..c054d3aa 100644 --- a/backend/app/views/comable/admin/stocks/index.slim +++ b/backend/app/views/comable/admin/stocks/index.slim @@ -44,7 +44,7 @@ = link_to Comable.t('admin.advanced_search'), 'javascript:void(0)', 'data-toggle' => 'collapse', 'data-target' => '#comable-advanced-search' li = link_to Comable.t('admin.clear_search_conditions'), comable.admin_stocks_path - = f.text_field :code_cont, class: 'form-control' + = f.text_field :variant_sku_cont, class: 'form-control' span.input-group-btn = f.submit Comable.t('admin.search'), class: 'btn btn-default' diff --git a/backend/spec/views/comable/admin/stocks/index.slim_spec.rb b/backend/spec/views/comable/admin/stocks/index.slim_spec.rb index 9714cac7..7cfd096d 100644 --- a/backend/spec/views/comable/admin/stocks/index.slim_spec.rb +++ b/backend/spec/views/comable/admin/stocks/index.slim_spec.rb @@ -1,6 +1,7 @@ describe 'comable/admin/stocks/index' do let!(:product) { create(:product) } - let!(:stocks) { create_list(:stock, 2, product: product) } + let!(:variant) { build(:variant, product: product) } + let!(:stocks) { create_list(:stock, 2, variant: variant) } let(:q) { product.stocks.ransack } From 76fd6b2036608d67b643215a1ee8a18cec66058c Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 01:31:49 +0900 Subject: [PATCH 13/58] Fix to pass the test of Samples --- core/app/models/comable/variant.rb | 23 ++++---- sample/db/samples/orders.rb | 8 +-- sample/db/samples/stocks.rb | 78 --------------------------- sample/db/samples/variants.rb | 84 ++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 92 deletions(-) delete mode 100644 sample/db/samples/stocks.rb create mode 100644 sample/db/samples/variants.rb diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 0678f58b..5a73e352 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -5,21 +5,24 @@ class Variant < ActiveRecord::Base has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, dependent: :destroy, join_table: :comable_variants_option_values accepts_nested_attributes_for :option_values, allow_destroy: true + accepts_nested_attributes_for :stock #validates :product, presence: { message: Comable.t('admin.is_not_exists') } validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } - def code + # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 + def option_values_attributes=(attributes) + if attributes.is_a? Array + existed_option_values = attributes.map do |attr| + variant = Comable::OptionValue.find_by(option_type: attr[:option_type], name: attr[:name]) unless attr[:id] + next unless variant + attr[:id] = variant.id + variant + end.compact + self.attributes = { option_values: existed_option_values } + end + super end - - def code=(_code) - end - - # - # Deprecated methods - # - deprecate :code, deprecator: Comable::Deprecator.instance - deprecate :code=, deprecator: Comable::Deprecator.instance end end diff --git a/sample/db/samples/orders.rb b/sample/db/samples/orders.rb index 82c3f2b9..85fa5ad7 100644 --- a/sample/db/samples/orders.rb +++ b/sample/db/samples/orders.rb @@ -1,5 +1,5 @@ Comable::Sample.import('products') -Comable::Sample.import('stocks') +Comable::Sample.import('variants') Comable::Sample.import('addresses') bill_address = Comable::Address.first @@ -17,7 +17,7 @@ total_price: 500 + suede_dress.price, order_items_attributes: [ { - stock: suede_dress.stocks.first, + variant: suede_dress.variants.first, quantity: 1 } ] @@ -31,11 +31,11 @@ total_price: 500 + (suede_dress.price * 2) + (girly_coat.price * 3), order_items_attributes: [ { - stock: suede_dress.stocks.first, + variant: suede_dress.variants.first, quantity: 2 }, { - stock: girly_coat.stocks.first, + variant: girly_coat.variants.first, quantity: 3 } ] diff --git a/sample/db/samples/stocks.rb b/sample/db/samples/stocks.rb deleted file mode 100644 index bc32cd2a..00000000 --- a/sample/db/samples/stocks.rb +++ /dev/null @@ -1,78 +0,0 @@ -Comable::Sample.import('products') - -suede_dress = Comable::Product.where(name: Comable::Sample.t(:suede_dress)).first! -girly_coat = Comable::Product.where(name: Comable::Sample.t(:girly_coat)).first! -fur_gloves = Comable::Product.where(name: Comable::Sample.t(:fur_gloves)).first! -leather_boots = Comable::Product.where(name: Comable::Sample.t(:leather_boots)).first! - -suede_dress.update_attributes!(sku_h_item_name: Comable::Sample.t(:size), sku_v_item_name: Comable::Sample.t(:color)) -girly_coat.update_attributes!(sku_h_item_name: Comable::Sample.t(:size), sku_v_item_name: Comable::Sample.t(:color)) -leather_boots.update_attributes!(sku_h_item_name: Comable::Sample.t(:size)) - -default_stock_attributes = { - quantity: 10 -} - -stocks_attributes = [ - { - product: suede_dress, - code: "#{suede_dress.code}-SN", - sku_h_choice_name: 'S', - sku_v_choice_name: Comable::Sample.t(:navy) - }, - { - product: suede_dress, - code: "#{suede_dress.code}-MN", - sku_h_choice_name: 'M', - sku_v_choice_name: Comable::Sample.t(:navy) - }, - { - product: girly_coat, - code: "#{girly_coat.code}-SB", - sku_h_choice_name: 'S', - sku_v_choice_name: Comable::Sample.t(:beige) - }, - { - product: girly_coat, - code: "#{girly_coat.code}-MB", - sku_h_choice_name: 'M', - sku_v_choice_name: Comable::Sample.t(:beige) - }, - { - product: girly_coat, - code: "#{girly_coat.code}-SN", - sku_h_choice_name: 'S', - sku_v_choice_name: Comable::Sample.t(:navy) - }, - { - product: girly_coat, - code: "#{girly_coat.code}-MN", - sku_h_choice_name: 'M', - sku_v_choice_name: Comable::Sample.t(:navy) - }, - { - product: fur_gloves, - code: fur_gloves.code - }, - { - product: leather_boots, - code: "#{leather_boots.code}-S", - sku_h_choice_name: 'S', - quantity: 0 - }, - { - product: leather_boots, - code: "#{leather_boots.code}-M", - sku_h_choice_name: 'M', - quantity: 1 - }, - { - product: leather_boots, - code: "#{leather_boots.code}-L", - sku_h_choice_name: 'L' - } -] - -stocks_attributes.each do |attributes| - Comable::Stock.create!(default_stock_attributes.merge(attributes)) -end diff --git a/sample/db/samples/variants.rb b/sample/db/samples/variants.rb new file mode 100644 index 00000000..e0212ba0 --- /dev/null +++ b/sample/db/samples/variants.rb @@ -0,0 +1,84 @@ +Comable::Sample.import('products') + +suede_dress = Comable::Product.where(name: Comable::Sample.t(:suede_dress)).first! +girly_coat = Comable::Product.where(name: Comable::Sample.t(:girly_coat)).first! +fur_gloves = Comable::Product.where(name: Comable::Sample.t(:fur_gloves)).first! +leather_boots = Comable::Product.where(name: Comable::Sample.t(:leather_boots)).first! + +suede_dress_size = suede_dress.option_types.create!(name: Comable::Sample.t(:size)) +suede_dress_color = suede_dress.option_types.create!(name: Comable::Sample.t(:color)) + +girly_coat_size = girly_coat.option_types.create!(name: Comable::Sample.t(:size)) +girly_coat_color = girly_coat.option_types.create!(name: Comable::Sample.t(:color)) + +leather_boots_size = leather_boots.option_types.create!(name: Comable::Sample.t(:size)) + +default_stock_attributes = { + quantity: 10 +} + +variants_attributes = [ + { + product: suede_dress, + option_values_attributes: [option_type: suede_dress_size, name: 'S'] + [option_type: suede_dress_color, name: Comable::Sample.t(:navy)], + stock_attributes: default_stock_attributes, + sku: 'SUEDE-DRESS-SN' + }, + { + product: suede_dress, + option_values_attributes: [option_type: suede_dress_size, name: 'M'] + [option_type: suede_dress_color, name: Comable::Sample.t(:navy)], + stock_attributes: default_stock_attributes, + sku: 'SUEDE-DRESS-MN' + }, + { + product: girly_coat, + option_values_attributes: [option_type: girly_coat_size, name: 'S'] + [option_type: girly_coat_color, name: Comable::Sample.t(:beige)], + stock_attributes: default_stock_attributes, + sku: 'GIRLY-COAT-SB' + }, + { + product: girly_coat, + option_values_attributes: [option_type: girly_coat_size, name: 'M'] + [option_type: girly_coat_color, name: Comable::Sample.t(:beige)], + stock_attributes: default_stock_attributes, + sku: 'GIRLY-COAT-MB' + }, + { + product: girly_coat, + option_values_attributes: [option_type: girly_coat_size, name: 'S'] + [option_type: girly_coat_color, name: Comable::Sample.t(:navy)], + stock_attributes: default_stock_attributes, + sku: 'GIRLY-COAT-SN' + }, + { + product: girly_coat, + option_values_attributes: [option_type: girly_coat_size, name: 'M'] + [option_type: girly_coat_color, name: Comable::Sample.t(:navy)], + stock_attributes: default_stock_attributes, + sku: 'GIRLY-COAT-MN' + }, + { + product: fur_gloves, + stock_attributes: default_stock_attributes, + sku: 'FUR-GLOVES' + }, + { + product: leather_boots, + option_values_attributes: [option_type: leather_boots_size, name: 'S'], + stock_attributes: { quantity: 0 }, + sku: 'LEATHER-BOOTS-S' + }, + { + product: leather_boots, + option_values_attributes: [option_type: leather_boots_size, name: 'M'], + stock_attributes: { quantity: 1 }, + sku: 'LEATHER-BOOTS-M' + }, + { + product: leather_boots, + option_values_attributes: [option_type: leather_boots_size, name: 'L'], + stock_attributes: default_stock_attributes, + sku: 'LEATHER-BOOTS-L' + } +] + +variants_attributes.each do |attributes| + Comable::Variant.create!(attributes) +end From b9a21d863778fd15d046faf8ea8c5ea4906c35f5 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 01:57:32 +0900 Subject: [PATCH 14/58] Refactoring with rubocop --- core/app/models/comable/product.rb | 4 --- core/app/models/comable/variant.rb | 2 +- ...nge_comable_products_and_comable_stocks.rb | 31 ++++++++++++------- ...150823102819_change_comable_order_items.rb | 7 +++-- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index f3ea0708..cf768589 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -78,10 +78,6 @@ def price=(price) has_many :stocks, class_name: Comable::Stock.name, through: :variants - #def stocks - # variants.map(&:stock) - #end - def stocks=(stocks) stocks.map { |stock| variants.build(stock: stock) } end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 5a73e352..67e3cbeb 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -7,7 +7,7 @@ class Variant < ActiveRecord::Base accepts_nested_attributes_for :option_values, allow_destroy: true accepts_nested_attributes_for :stock - #validates :product, presence: { message: Comable.t('admin.is_not_exists') } + validates :product, presence: { message: Comable.t('admin.is_not_exists') } validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } diff --git a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb index 22588ed6..3a2554cc 100644 --- a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb +++ b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb @@ -52,22 +52,31 @@ def change def up_records Comable::Product.all.each do |product| if product.sku_h_item_name? || product.sku_h_item_name? - option_type_h = product.option_types.create!(name: product.sku_h_item_name) if product.sku_h_item_name? - option_type_v = product.option_types.create!(name: product.sku_v_item_name) if product.sku_v_item_name? - - Comable::Stock.where(product_id: product.id).each do |stock| - option_values = [] - option_values << option_type_h.option_values.where(name: stock.sku_h_choice_name).first_or_initialize if option_type_h - option_values << option_type_v.option_values.where(name: stock.sku_v_choice_name).first_or_initialize if option_type_v - product.variants.create!(stock: stock, price: product.price, sku: stock.code, option_values: option_values) - end + create_variants_for(product) else - stock = Comable::Stock.find_by(product_id: product.id) - product.variants.create!(stock: stock, price: product.price, sku: product.code) + create_variant_for(product) end end end def down_records + # TODO: Implement + end + + def create_variant_for(product) + stock = Comable::Stock.find_by(product_id: product.id) + product.variants.create!(stock: stock, price: product.price, sku: product.code) + end + + def create_variants_for(product) + option_type_h = product.option_types.create!(name: product.sku_h_item_name) if product.sku_h_item_name? + option_type_v = product.option_types.create!(name: product.sku_v_item_name) if product.sku_v_item_name? + + Comable::Stock.where(product_id: product.id).each do |stock| + option_values = [] + option_values << option_type_h.option_values.where(name: stock.sku_h_choice_name).first_or_initialize if option_type_h + option_values << option_type_v.option_values.where(name: stock.sku_v_choice_name).first_or_initialize if option_type_v + product.variants.create!(stock: stock, price: product.price, sku: stock.code, option_values: option_values) + end end end diff --git a/core/db/migrate/20150823102819_change_comable_order_items.rb b/core/db/migrate/20150823102819_change_comable_order_items.rb index 4f0e8e6c..cd3b4f20 100644 --- a/core/db/migrate/20150823102819_change_comable_order_items.rb +++ b/core/db/migrate/20150823102819_change_comable_order_items.rb @@ -13,8 +13,11 @@ def change dir.up { t.remove :stock_id } dir.down { t.references :stock, null: false } - dir.up { t.rename :code, :sku; t.change :sku, :string, null: true } - dir.down { t.rename :sku, :code; t.change :code, :string, null: false } + dir.up { t.rename :code, :sku } + dir.down { t.rename :sku, :code } + + dir.up { t.change :sku, :string, null: true } + dir.down { t.change :code, :string, null: false } dir.up { t.remove :sku_h_item_name } dir.down { t.string :sku_h_item_name } From a06eae1e13272dfe3ef4f5d783cc437a4d9d0449 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 02:05:53 +0900 Subject: [PATCH 15/58] Add the test for Variant --- core/spec/models/comable/variant_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/spec/models/comable/variant_spec.rb diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb new file mode 100644 index 00000000..a5c2590b --- /dev/null +++ b/core/spec/models/comable/variant_spec.rb @@ -0,0 +1,10 @@ +describe Comable::Variant do + it { is_expected.to belong_to(:product).class_name(Comable::Product.name).inverse_of(:variants) } + it { is_expected.to have_one(:stock).class_name(Comable::Stock.name).inverse_of(:variant).dependent(:destroy).autosave(true) } + it { is_expected.to have_and_belong_to_many(:option_values).class_name(Comable::OptionValue.name).dependent(:destroy) } + + it { is_expected.to validate_presence_of(:product).with_message(Comable.t('admin.is_not_exists')) } + it { is_expected.to validate_presence_of(:price) } + it { is_expected.to validate_numericality_of(:price).is_greater_than_or_equal_to(0) } + it { is_expected.to validate_length_of(:sku).is_at_most(255) } +end From f956c76e515cdb65ffc89e4fd52b9044c0abc925 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 09:18:11 +0900 Subject: [PATCH 16/58] Ends support for Rails 3 --- .travis.yml | 9 +++------ README.md | 4 ++-- backend/comable-backend.gemspec | 2 +- core/comable-core.gemspec | 2 +- frontend/comable-frontend.gemspec | 2 +- gemfiles/active_record_32.gemfile | 9 --------- 6 files changed, 8 insertions(+), 20 deletions(-) delete mode 100644 gemfiles/active_record_32.gemfile diff --git a/.travis.yml b/.travis.yml index 295dfea9..d15434fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,9 @@ language: - ruby rvm: - 2.1.5 - - 2.2.0 + - 2.2.3 - ruby-head gemfile: - - gemfiles/active_record_32.gemfile - gemfiles/active_record_40.gemfile - gemfiles/active_record_41.gemfile - gemfiles/active_record_42.gemfile @@ -16,10 +15,8 @@ before_script: - bundle exec rake db:migrate RAILS_ENV=test matrix: exclude: - - rvm: 2.2.0 - gemfile: gemfiles/active_record_32.gemfile - - rvm: ruby-head - gemfile: gemfiles/active_record_32.gemfile + - rvm: 2.1.5 + gemfile: gemfiles/active_record_edge.gemfile allow_failures: - rvm: ruby-head - gemfile: gemfiles/active_record_edge.gemfile diff --git a/README.md b/README.md index 2d144d68..798076ac 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ Go to your browser and open `http://localhost:3000/admin`. ## Requirements -* Ruby on Rails 3.2, 4.1, 4.2 +* Ruby on Rails 4.0, 4.1, 4.2 * Ruby 2.1, 2.2 -And `strong_parameters`, `everywhere` gems are required only for Rails 3. +Rails 3 is not supported. ## Development diff --git a/backend/comable-backend.gemspec b/backend/comable-backend.gemspec index 4e75eecf..35c0872d 100644 --- a/backend/comable-backend.gemspec +++ b/backend/comable-backend.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.add_dependency 'comable-core', version - s.add_dependency 'rails', '>= 3.2.0', '< 5' + s.add_dependency 'rails', '>= 4.0.0', '< 5' s.add_dependency 'slim-rails', '~> 3.0.1' s.add_dependency 'sass-rails' # TODO: '~> 5.0.1' s.add_dependency 'coffee-rails', '>= 3.2.2', '< 4.2' diff --git a/core/comable-core.gemspec b/core/comable-core.gemspec index b475fa39..5fe0a204 100644 --- a/core/comable-core.gemspec +++ b/core/comable-core.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.1.5' - s.add_dependency 'rails', '>= 3.2.0', '< 5' + s.add_dependency 'rails', '>= 4.0.0', '< 5' s.add_dependency 'devise', '~> 3.5', '<= 3.5.1' s.add_dependency 'enumerize', '~> 0.9.0' s.add_dependency 'state_machine', '~> 1.2.0' diff --git a/frontend/comable-frontend.gemspec b/frontend/comable-frontend.gemspec index 1123a05a..ee7890ce 100644 --- a/frontend/comable-frontend.gemspec +++ b/frontend/comable-frontend.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |s| s.add_dependency 'comable-core', version - s.add_dependency 'rails', '>= 3.2.0', '< 5' + s.add_dependency 'rails', '>= 4.0.0', '< 5' s.add_dependency 'slim-rails', '~> 3.0.1' s.add_dependency 'sass-rails' # TODO: '~> 5.0.1' s.add_dependency 'coffee-rails', '>= 3.2.2', '< 4.2' diff --git a/gemfiles/active_record_32.gemfile b/gemfiles/active_record_32.gemfile deleted file mode 100644 index 29513c11..00000000 --- a/gemfiles/active_record_32.gemfile +++ /dev/null @@ -1,9 +0,0 @@ -eval File.read(File.expand_path('../../Gemfile.common', __FILE__)) - -gem 'railties', '~> 3.2.19' -gem 'activerecord', '~> 3.2.19', require: 'active_record' - -gem 'strong_parameters' -gem 'everywhere' - -gemspec path: '../' From 7bcda682d522e7c88724bff4530291b349af5cf6 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 10:20:46 +0900 Subject: [PATCH 17/58] Fix the problem that fail the test of Samples sometimes --- sample/lib/comable/sample.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/lib/comable/sample.rb b/sample/lib/comable/sample.rb index 0958e791..4f72da1a 100644 --- a/sample/lib/comable/sample.rb +++ b/sample/lib/comable/sample.rb @@ -33,7 +33,7 @@ def import(filename) def definitions Dir["#{default_dir}/*.rb"].map do |filepath| File.basename(filepath, '.rb') - end + end.sort end def default_dir From 850a912a3f249996c2c05b96b887cdaf11455b05 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 11:01:31 +0900 Subject: [PATCH 18/58] Rails 4.0: Fix the test to work --- core/app/models/comable/variant.rb | 2 +- core/spec/models/comable/variant_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 67e3cbeb..c153500f 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -2,7 +2,7 @@ module Comable class Variant < ActiveRecord::Base belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true - has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, dependent: :destroy, join_table: :comable_variants_option_values + has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values accepts_nested_attributes_for :option_values, allow_destroy: true accepts_nested_attributes_for :stock diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb index a5c2590b..ee5d5cd7 100644 --- a/core/spec/models/comable/variant_spec.rb +++ b/core/spec/models/comable/variant_spec.rb @@ -1,7 +1,7 @@ describe Comable::Variant do it { is_expected.to belong_to(:product).class_name(Comable::Product.name).inverse_of(:variants) } it { is_expected.to have_one(:stock).class_name(Comable::Stock.name).inverse_of(:variant).dependent(:destroy).autosave(true) } - it { is_expected.to have_and_belong_to_many(:option_values).class_name(Comable::OptionValue.name).dependent(:destroy) } + it { is_expected.to have_and_belong_to_many(:option_values).class_name(Comable::OptionValue.name) } it { is_expected.to validate_presence_of(:product).with_message(Comable.t('admin.is_not_exists')) } it { is_expected.to validate_presence_of(:price) } From aa8f948325cf536ef524188c894f03a9656345cd Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 11:13:36 +0900 Subject: [PATCH 19/58] Add the test for OptionType and OptionValue --- core/app/models/comable/option_type.rb | 2 ++ core/app/models/comable/option_value.rb | 2 ++ core/spec/models/comable/option_type_spec.rb | 7 +++++++ core/spec/models/comable/option_value_spec.rb | 7 +++++++ 4 files changed, 18 insertions(+) create mode 100644 core/spec/models/comable/option_type_spec.rb create mode 100644 core/spec/models/comable/option_value_spec.rb diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index a7e32512..33c2e19a 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -2,5 +2,7 @@ module Comable class OptionType < ActiveRecord::Base has_many :option_values, class_name: Comable::OptionValue.name belongs_to :product, class_name: Comable::Product.name + + validates :name, presence: true, length: { maximum: 255 } end end diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index c703d45a..658aae96 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -2,5 +2,7 @@ module Comable class OptionValue < ActiveRecord::Base belongs_to :option_type, class_name: Comable::OptionType.name has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values + + validates :name, presence: true, length: { maximum: 255 } end end diff --git a/core/spec/models/comable/option_type_spec.rb b/core/spec/models/comable/option_type_spec.rb new file mode 100644 index 00000000..3b55e8d3 --- /dev/null +++ b/core/spec/models/comable/option_type_spec.rb @@ -0,0 +1,7 @@ +describe Comable::OptionType do + it { is_expected.to have_many(:option_values).class_name(Comable::OptionValue.name) } + it { is_expected.to belong_to(:product).class_name(Comable::Product.name) } + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } +end diff --git a/core/spec/models/comable/option_value_spec.rb b/core/spec/models/comable/option_value_spec.rb new file mode 100644 index 00000000..92444e38 --- /dev/null +++ b/core/spec/models/comable/option_value_spec.rb @@ -0,0 +1,7 @@ +describe Comable::OptionValue do + it { is_expected.to belong_to(:option_type).class_name(Comable::OptionType.name) } + it { is_expected.to have_and_belong_to_many(:variants).class_name(Comable::Variant.name) } + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } +end From a144fb8b1d4aa29b87f4b2174626831deb4cd2b7 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Tue, 25 Aug 2015 14:57:28 +0900 Subject: [PATCH 20/58] Implement the pages for Variant --- .../comable/admin/variants_controller.rb | 63 ++++++++++++ .../views/comable/admin/products/_form.slim | 52 +++++++++- .../comable/admin/variants/._form.slim.swo | Bin 0 -> 16384 bytes .../views/comable/admin/variants/_form.slim | 59 +++++++++++ .../views/comable/admin/variants/edit.slim | 41 ++++++++ .../views/comable/admin/variants/index.slim | 97 ++++++++++++++++++ .../app/views/comable/admin/variants/new.slim | 16 +++ backend/config/routes.rb | 2 + core/app/models/comable/option_type.rb | 2 + core/app/models/comable/option_value.rb | 2 + core/app/models/comable/variant.rb | 8 ++ core/config/locales/en.yml | 8 ++ core/config/locales/ja.yml | 7 ++ 13 files changed, 354 insertions(+), 3 deletions(-) create mode 100644 backend/app/controllers/comable/admin/variants_controller.rb create mode 100644 backend/app/views/comable/admin/variants/._form.slim.swo create mode 100644 backend/app/views/comable/admin/variants/_form.slim create mode 100644 backend/app/views/comable/admin/variants/edit.slim create mode 100644 backend/app/views/comable/admin/variants/index.slim create mode 100644 backend/app/views/comable/admin/variants/new.slim diff --git a/backend/app/controllers/comable/admin/variants_controller.rb b/backend/app/controllers/comable/admin/variants_controller.rb new file mode 100644 index 00000000..afccfd53 --- /dev/null +++ b/backend/app/controllers/comable/admin/variants_controller.rb @@ -0,0 +1,63 @@ +require_dependency 'comable/admin/application_controller' + +module Comable + module Admin + class VariantsController < Comable::Admin::ApplicationController + load_and_authorize_resource :product, class: Comable::Product.name + load_and_authorize_resource :variant, class: Comable::Variant.name, through: :product + + def index + @q = @variants.ransack(params[:q]) + @variants = @q.result.includes(:product).page(params[:page]).accessible_by(current_ability) + end + + def show + render :edit + end + + def new + end + + def create + if @variant.save + redirect_to comable.admin_product_variant_path(@product, @variant), notice: Comable.t('successful') + else + flash.now[:alert] = Comable.t('failure') + render :new + end + end + + def edit + end + + def update + if @variant.update_attributes(variant_params) + redirect_to comable.admin_product_variant_path(@product, @variant), notice: Comable.t('successful') + else + flash.now[:alert] = Comable.t('failure') + render :edit + end + end + + def destroy + if @variant.destroy + redirect_to comable.admin_product_variants_path(@product), notice: Comable.t('successful') + else + flash.now[:alert] = Comable.t('failure') + render :edit + end + end + + private + + def variant_params + params.require(:variant).permit( + :price, + :sku, + stock_attributes: [:quantity], + option_values_attributes: [:id, :name] + ) + end + end + end +end diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 245b9d41..865ff2d7 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -41,9 +41,9 @@ li = category.path.map(&:name).join(' > ') - hr + / hr - fieldset + / fieldset .col-md-3 legend = Comable.t('admin.stocks') @@ -138,6 +138,53 @@ hr + fieldset + .col-md-3 + legend + = Comable.t('admin.variants') + + .col-md-9 + - if @product.variants.any? + table.table.table-striped + thead + th + = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:quantity) + - @product.option_types.each do |option_type| + th + = option_type.name + th + = Comable.t('admin.operation') + tbody + - @product.variants.each do |variant| + tr + td + = variant.sku + td + - quantity = variant.quantity + strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" + = number_with_delimiter quantity + - variant.option_values.each do |option_value| + th + = option_value.name + td + = link_to comable.admin_stock_path(variant.stock), class: 'btn btn-default' + i.fa.fa-edit> + = Comable.t('admin.actions.edit') + + .form-group + = f.label :sku_h_item_name + = f.text_field :sku_h_item_name + .help-block + = Comable.t('admin.please_fill_when_using_sku') + + .form-group + = f.label :sku_v_item_name + = f.text_field :sku_v_item_name + + hr + fieldset .col-md-3 legend @@ -154,4 +201,3 @@ = f.radio_button :published_at, :unpublished, checked: @product.published_at.blank? = Comable.t('admin.nav.products.unpublished') = f.text_field :published_at, value: @product.published_at.try(:strftime, '%Y-%m-%d %H:%M'), class: 'datetimepicker' - diff --git a/backend/app/views/comable/admin/variants/._form.slim.swo b/backend/app/views/comable/admin/variants/._form.slim.swo new file mode 100644 index 0000000000000000000000000000000000000000..6e9e2b749d30b53845f5e6e7a5c10f052c25e9cd GIT binary patch literal 16384 zcmeI3UyLM09mi|+#6N%-h$P%4T6f1XySm-8>k*ZKxx08gXz;>mJR=y=)=byTG~3hN zcGt}8vb*9R;w40sCmx7!iEt2n@qt7Qcrp5*#sHDT2Tu^A!58tO@kxn6zt!EgxK{_Ow<%{jrnuL*|VH$GZr5W#Q%1w;nr4&i|Q^O+V^6 zHj_D1kHFz!=nK>J+sw87K6AW@<-d?BtVdzH-bT5W%j+%HUgw@&XF*UOI(!smIn3@m zUVX?yhj}8ZTOB{_o004ECn~QJD-Tp2*xdsI(J?-JB{}kewN+W@@S%hBnyXLmUcSny zJWzR{@<8Q*$^(@LDi2g1s60@4;Q!78B3K~zqE&B7TXu7LUMQX4oL+xCJxdvRsB+0w z^;vnK@<8Q*$^(@LDi2g1s60@4pz=WFfyx7w2PzNzH+q2CgnZ`hgnaHDunzzKSMmSv zuOj4k;3;qxJP5u4?gK;Mfc@ZwcM|dpI19c4MxX``fak9yy|@D$ht26*ubLVf{$ z3?2es2DgFtgZF_&a6PyVTn^s2oRACPSK#O1``|4268Iw60`~wPFz_j`3hLk>xC;CY z?Ry!#1il9z1NQ;h?%TmX;Dd|cMX-qL>(jIgxc({l3HTb=0H=Tf4uJjO)wiK7zy)`J zWpFik?X8473mykM;7;&f@bG1XoB=&>8r%x51($&fZ$TTuBjA3}1r1OG`@kRe5poVZ z1nva4ft$f~;P2R^c@6v#oB^K%`@mnZx$^?}5qKIr0nUL(!6V?Cpbyr-dw_P(84j1V zxNC;I?+-aCmp7=>p`DKDx9;ZcaweryWyg!ppK@sxOqD7$Ntgi-BPSBvK1OyiZw77S zk~0XUUY%+}lRACY<(6RMRg_Yu)`+ru<(JfrUrtp}@anj1DP?w5(mN$#TuznWGh6C& z=T=%2*u#SVL>0vf_iA&gbm?*ib4wD1%lW-EuS8+|%y>(5vzbYlgU}@flc_0sgJO!j zn(7GMh&e^ai_<3c1L63dHDvC9n;z?r&#!dPQm#XC-3C3LzEEfCx+9abc8}UV-S+ds z@>{`%V%GSTEL=`4D45WQdCk*2vQ1QF1NU(HKA|C4*AZDplXlFw$7rL?1n>Hx!=p6U zlrIz--OwKdBoj#0V7`PJiw&6raaYct-3ZP&ycMz$J(0Y-*l3(cbZ(l=byi3awu&?!f&s~qqx)A z&ihmr6FjdtS|4O}%Dl9~Jh@zx+4fzdZyPu1I@3X5iltiorm7j&9@Ump-7fFqz1H|b z$sfU0VlNj`RWH;P7L5!-r=8#KnwfgWr5P$UI9~TsEtO*=?%Uj>rKu^-PrWL$R*BykVD)r>QGSRCR2k*>y}8%f4j(FdV`AwC%DeY92bgRkNK)&Kh=2u6|RkwAsui z-5 z-b_WB-16<>OrqAk-CSjogeUz99XYn>HR#&vD!d{4twC$C7mYYl5w%Qz?#NlZR@Ub7 zj;LX*rs~q3qBzu;El&!`xQ_;{e*7FW@e01pJ%W1*kE?i|`)q2+3T?#WcO`2fzNPNI zE4e=~3pS~IJ3qAgJc1V7QkN+ zyZ;892j2!V&R+n}AwGWqY=Bds34V)M{5&`Vz6u2R1Xu>|1}`Bd|26m-I0tsXHW+~+ zSO<53Q{VvD53T^OBW8aU{1OOo3%CJX3%-k(eH%#sy&-*A9hCJ++W=tNG-aS_R7ZTmN4A4Fo-(2{FvtFQ``qcjiJCKSTIakY1hrFiMv5cT%McM8}wT|v__8TSvJSdM=`EK zI<~uz3t=wvy>1@OEvd5fmL^?YrpIVaPL+nuye_6O+MtCJYg&;J3q2WWqf85r53953UN~!ZiXXkSHZiW(|bWB8q1-0`|gx@1ig?lm9(B4Kji zmWJ$v9?yo#`2IlBjFOCIv9__G#m`!59z!Rpv{+NcsAg}_XC59KB6RRH;qmmdlT2tbJh~(Z6;5G*=BSlmvzyc zQ@C@_I~gJx|= z#+w;WF>xJBYs;}rES=V?4?i>XN0|Xrnknmw0_Gi^bX%#1fz9QNl|Ouyfyr5kW5+ID P`&cc1gElY|#zN#@?dR}) literal 0 HcmV?d00001 diff --git a/backend/app/views/comable/admin/variants/_form.slim b/backend/app/views/comable/admin/variants/_form.slim new file mode 100644 index 00000000..d89dfc14 --- /dev/null +++ b/backend/app/views/comable/admin/variants/_form.slim @@ -0,0 +1,59 @@ += error_messages_for @variant + += form_for [comable, :admin, @product, @variant] do |f| + .hidden + = f.submit + + fieldset + .col-md-3 + .media + = link_to comable.admin_product_path(@product), class: 'media-left' do + = image_tag @product.image_url, width: 100, class: 'media-object' + .media-body + h4.media-heading + = @product.name + p + | #{@product.variants.size} #{Comable.t('admin.variants').downcase} + p + = link_to 'Back to product', comable.admin_product_path(@product) + + .media + ul.list-group + - @product.variants.each do |variant| + li.list-group-item class="#{'disabled' if variant == @variant}" + = link_to variant.name, comable.admin_product_variant_path(@product, variant) + + .col-md-9 + = f.fields_for :option_values do |ff| + .form-group + = ff.label :name, ff.object.option_type.name + = ff.text_field :name + + hr + + fieldset + .col-md-3 + legend + = Comable.t('admin.pricing') + + .col-md-9 + .form-group + = f.label :price + = f.text_field :price + + hr + + fieldset + .col-md-3 + legend + = Comable.t('admin.stocks') + + .col-md-9 + .form-group + = f.label :sku + = f.text_field :sku + + = f.fields_for :stock do |ff| + .form-group + = ff.label :quantity + = ff.text_field :quantity diff --git a/backend/app/views/comable/admin/variants/edit.slim b/backend/app/views/comable/admin/variants/edit.slim new file mode 100644 index 00000000..88588a94 --- /dev/null +++ b/backend/app/views/comable/admin/variants/edit.slim @@ -0,0 +1,41 @@ +.comable-page + .comable-main-fixed-top + .comable-page-heading + ul.pull-right.list-inline + li.dropdown + = link_to '#', class: 'btn btn-default', 'data-toggle' => 'dropdown' do + i.fa.fa-bars + ul.dropdown-menu.dropdown-menu-right + li + = link_to comable.product_path(@product) do + span.fa.fa-external-link> + = Comable.t('admin.check_this_product_in_frontend') + - if @product.sku? || @product.stocks.empty? + li.divider + li.dropdown-header + = Comable.t('admin.nav.stock') + li + = link_to Comable.t('admin.actions.new'), comable.new_admin_product_stock_path(@product) + li + = link_to_save + + h1.page-header + ol.breadcrumb + li> + = link_to Comable.t('admin.nav.product'), comable.admin_products_path + li.active + = @product.name + + .comable-page-body + = render 'form' + hr + .panel.panel-danger + .panel-heading type="button" data-toggle="collapse" data-target="#comable-danger" + strong + span.fa.fa-angle-down> + = Comable.t('admin.actions.destroy') + #comable-danger.collapse + .panel-body + p + = Comable.t('admin.confirmation_to_remove_product') + = link_to Comable.t('admin.actions.destroy'), comable.admin_product_path(@product), method: :delete, class: 'btn btn-danger' diff --git a/backend/app/views/comable/admin/variants/index.slim b/backend/app/views/comable/admin/variants/index.slim new file mode 100644 index 00000000..42969f90 --- /dev/null +++ b/backend/app/views/comable/admin/variants/index.slim @@ -0,0 +1,97 @@ +.comable-page + .comable-page-heading + ul.pull-right.list-inline + li + .btn-group role="group" + = link_to_previous_page @products, '<', class: 'btn btn-default' do + .btn.btn-default disabled="disabled" + | < + = link_to_next_page @products, '>', class: 'btn btn-default' do + .btn.btn-default disabled="disabled" + | > + li + = link_to comable.root_path do + = link_to Comable.t('admin.actions.new'), comable.new_admin_product_path, class: 'btn btn-default' + li.dropdown + = link_to '#', class: 'btn btn-default', 'data-toggle' => 'dropdown' do + = Comable.t('admin.more') + i.fa.fa-angle-down< + ul.dropdown-menu.dropdown-menu-right + li + = link_to Comable.t('admin.export_to_csv'), comable.export_admin_products_path(format: :csv, q: params[:q]) + li + = link_to Comable.t('admin.export_to_excel'), comable.export_admin_products_path(format: :xlsx, q: params[:q]) + li.divider + li + a.btn-file + span> + = Comable.t('admin.import') + = form_tag comable.import_admin_products_path, multipart: true do + = file_field_tag :file + + h1.page-header + = Comable.t('admin.nav.product') + small< + | #{@products.total_count} #{Comable.t('admin.results')} + + .comable-page-body + .comable-search + = search_form_for @q, url: comable.admin_products_path do |f| + .input-group + span.input-group-btn + button.btn.btn-default.dropdown-toggle type="button" data-toggle="dropdown" + i.fa.fa-search + span.caret< + ul.dropdown-menu role="menu" + li + = link_to Comable.t('admin.advanced_search'), 'javascript:void(0)', 'data-toggle' => 'collapse', 'data-target' => '#comable-advanced-search' + li + = link_to Comable.t('admin.clear_search_conditions'), comable.admin_products_path + = f.text_field :name_or_variants_sku_cont_any_splitted, class: 'form-control' + span.input-group-btn + = f.submit Comable.t('admin.search'), class: 'btn btn-default' + + = render 'comable/admin/shared/advanced_search', f: f + + section + - if @products.empty? + = Comable.t('admin.not_found') + - else + table.table.table-striped + thead + th + th + = sort_link [:comable, @q], :code + th + = sort_link [:comable, @q], :name + th + = sort_link [:comable, @q], :price + th + = Comable.t('admin.stocks') + tbody + - @products.each do |product| + - quantity = product.stocks.to_a.sum(&:quantity) + tr + td.comable-image + = link_to comable.admin_product_path(product), class: 'thumbnail' do + = image_tag product.image_url, width: '100%' + td + = link_to product.code, comable.admin_product_path(product) + - unless product.published? + span.fa.fa-eye-slash.text-muted< + td + = product.name + td + = number_to_currency product.price + td + ul.list-unstyled + - product.stocks.each do |stock| + li + strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" + = number_with_delimiter stock.quantity + - if stock.sku? + span< + | (#{stock.sku_name}) + + .text-center + = paginate @products, theme: :comable_backend diff --git a/backend/app/views/comable/admin/variants/new.slim b/backend/app/views/comable/admin/variants/new.slim new file mode 100644 index 00000000..2f6cccad --- /dev/null +++ b/backend/app/views/comable/admin/variants/new.slim @@ -0,0 +1,16 @@ +.comable-page + .comable-main-fixed-top + .comable-page-heading + ul.pull-right.list-inline + li + = link_to_save + + h1.page-header + ol.breadcrumb + li> + = link_to Comable.t('admin.nav.product'), comable.admin_products_path + li.active + = Comable.t('admin.actions.new') + + .comable-page-body + = render 'form' diff --git a/backend/config/routes.rb b/backend/config/routes.rb index a51a023f..23c23b0e 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -5,6 +5,8 @@ resource :dashboard, only: :show resources :products do + resources :variants + resources :stocks collection do diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index 33c2e19a..0d6221ad 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -4,5 +4,7 @@ class OptionType < ActiveRecord::Base belongs_to :product, class_name: Comable::Product.name validates :name, presence: true, length: { maximum: 255 } + + default_scope { order(:id) } end end diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index 658aae96..84bb841b 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -4,5 +4,7 @@ class OptionValue < ActiveRecord::Base has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values validates :name, presence: true, length: { maximum: 255 } + + default_scope { order(:option_type_id) } end end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index c153500f..5e135e34 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -11,6 +11,14 @@ class Variant < ActiveRecord::Base validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } + def name + option_values.map { |option_value| option_value.name }.join(' x ') + end + + def quantity + stock.quantity + end + # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 def option_values_attributes=(attributes) if attributes.is_a? Array diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 3cc8510f..96dc5232 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -165,6 +165,8 @@ en: please_see_following_page_for_syntax: 'Please see the following page for the syntax:' please_select_file_form_directory_tree_to_edit: 'Please select a file form the directory tree to edit.' here_editor_will_be_displayed_and_you_can_edit_file: 'Here the editor will be displayed, you can edit file.' + variants: 'Variants' + pricing: 'Pricing' nav: dashboard: 'Dashboard' order: 'Orders' @@ -419,6 +421,12 @@ en: url: 'Name' created_at: 'Created at' updated_at: 'Updated at' + comable/variant: + id: 'ID' + sku: 'SKU' + price: 'Price' + product: 'Product' + enumerize: comable/user: role: diff --git a/core/config/locales/ja.yml b/core/config/locales/ja.yml index ceff6581..2dfc88f3 100644 --- a/core/config/locales/ja.yml +++ b/core/config/locales/ja.yml @@ -165,6 +165,8 @@ ja: please_see_following_page_for_syntax: '構文については次のページを参照してください:' please_select_file_form_directory_tree_to_edit: 'ディレクトリーツリーから編集したいファイルを選択してください。' here_editor_will_be_displayed_and_you_can_edit_file: 'ここにエディタが表示されてファイルの内容を編集できます。' + variants: 'バリエーション' + pricing: '価格' nav: dashboard: ダッシュボード order: 注文管理 @@ -419,6 +421,11 @@ ja: url: URL created_at: 作成日 updated_at: 更新日 + comable/variant: + id: 'ID' + sku: 'SKUコード' + price: '価格' + product: '商品' enumerize: comable/user: From 9433b7f7b2e0ed1ed42b7410941cfd9cd2b123b0 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Wed, 26 Aug 2015 19:30:16 +0900 Subject: [PATCH 21/58] Temporary implement the page to create a variant --- .../comable/admin/dispatcher.coffee | 1 + .../comable/admin/navigations.coffee | 5 +- .../javascripts/comable/admin/pages.coffee | 9 +- .../javascripts/comable/admin/products.coffee | 4 - .../javascripts/comable/admin/search.coffee | 12 +- .../javascripts/comable/admin/variants.coffee | 90 ++++++++++++++ .../stylesheets/comable/admin/_variants.scss | 29 +++++ .../comable/admin/application.scss | 1 + .../comable/admin/products_controller.rb | 12 +- .../comable/admin/application_helper.rb | 22 +++- .../views/comable/admin/products/_form.slim | 116 +++++------------- .../admin/shared/_option_types_fields.slim | 11 ++ .../comable/admin/shared/_variant_form.slim | 26 ++++ .../admin/shared/_variants_fields.slim | 12 ++ .../comable/admin/variants/._form.slim.swo | Bin 16384 -> 0 bytes .../views/comable/admin/variants/_form.slim | 27 +--- .../views/comable/admin/variants/edit.slim | 18 +-- core/app/models/comable/option_type.rb | 11 ++ core/app/models/comable/product.rb | 4 + core/app/models/comable/variant.rb | 25 +++- core/config/locales/en.yml | 3 + core/config/locales/ja.yml | 3 + 22 files changed, 282 insertions(+), 159 deletions(-) create mode 100644 backend/app/assets/javascripts/comable/admin/variants.coffee create mode 100644 backend/app/assets/stylesheets/comable/admin/_variants.scss create mode 100644 backend/app/views/comable/admin/shared/_option_types_fields.slim create mode 100644 backend/app/views/comable/admin/shared/_variant_form.slim create mode 100644 backend/app/views/comable/admin/shared/_variants_fields.slim delete mode 100644 backend/app/views/comable/admin/variants/._form.slim.swo diff --git a/backend/app/assets/javascripts/comable/admin/dispatcher.coffee b/backend/app/assets/javascripts/comable/admin/dispatcher.coffee index 21aa6c12..1d016236 100644 --- a/backend/app/assets/javascripts/comable/admin/dispatcher.coffee +++ b/backend/app/assets/javascripts/comable/admin/dispatcher.coffee @@ -21,5 +21,6 @@ class Dispatcher new Page when 'products:new', 'products:show', 'products:edit', 'products:update', 'products:create' new Product + new Variant when 'navigations:new', 'navigations:show', 'navigations:edit', 'navigations:update', 'navigations:create' new Navigation diff --git a/backend/app/assets/javascripts/comable/admin/navigations.coffee b/backend/app/assets/javascripts/comable/admin/navigations.coffee index 1ad3043e..748ae0de 100644 --- a/backend/app/assets/javascripts/comable/admin/navigations.coffee +++ b/backend/app/assets/javascripts/comable/admin/navigations.coffee @@ -1,11 +1,8 @@ class @Navigation constructor: -> - $(document).ready(@ready) - @add_event() - - ready: => @navigation_items = $('#navigation-items') @add_fields = $('.add_fields') + @add_event() # linkable_idの検索 search_linkable_ids: -> diff --git a/backend/app/assets/javascripts/comable/admin/pages.coffee b/backend/app/assets/javascripts/comable/admin/pages.coffee index ef0e910b..a45024b0 100644 --- a/backend/app/assets/javascripts/comable/admin/pages.coffee +++ b/backend/app/assets/javascripts/comable/admin/pages.coffee @@ -1,16 +1,13 @@ class @Page constructor: -> - $(document).ready(@ready) - @add_event_to_set_visibility() - @add_event_to_set_page_title() - @add_event_to_set_meta_description() - - ready: => @radio_published = $('#page_published_at_published') @radio_unpublished = $('#page_published_at_unpublished') @published_at = $('#page_published_at') @initialize_visibility() + @add_event_to_set_visibility() + @add_event_to_set_page_title() + @add_event_to_set_meta_description() # 公開/非公開の制御 initialize_visibility: -> diff --git a/backend/app/assets/javascripts/comable/admin/products.coffee b/backend/app/assets/javascripts/comable/admin/products.coffee index 9283dbd4..e404fa33 100644 --- a/backend/app/assets/javascripts/comable/admin/products.coffee +++ b/backend/app/assets/javascripts/comable/admin/products.coffee @@ -22,9 +22,6 @@ $(document).ready(-> class @Product constructor: -> - $(document).ready(@ready) - - ready: => @radio_published = $('#product_published_at_published') @radio_unpublished = $('#product_published_at_unpublished') @published_at = $('#product_published_at') @@ -57,4 +54,3 @@ class @Product @published_at.blur( => @radio_unpublished.click() unless @published_at.val() ) - diff --git a/backend/app/assets/javascripts/comable/admin/search.coffee b/backend/app/assets/javascripts/comable/admin/search.coffee index 39c2c8a3..bfcfab9f 100644 --- a/backend/app/assets/javascripts/comable/admin/search.coffee +++ b/backend/app/assets/javascripts/comable/admin/search.coffee @@ -1,4 +1,4 @@ -class Search +class DynamicField constructor: (@templates = {}) -> remove_fields: (button) -> @@ -22,14 +22,14 @@ class Search # --- # main # --- -search = new Search() +dynamic_field = new DynamicField -$(document).on('click', '.ransack.add_fields', -> - search.add_fields(this, $(this).data('fieldType'), $(this).data('content')) +$(document).on('click', '.add_fields', -> + dynamic_field.add_fields(this, $(this).data('fieldType'), $(this).data('content')) false ) -$(document).on('click', '.ransack.remove_fields', -> - search.remove_fields(this) +$(document).on('click', '.remove_fields', -> + dynamic_field.remove_fields(this) false ) diff --git a/backend/app/assets/javascripts/comable/admin/variants.coffee b/backend/app/assets/javascripts/comable/admin/variants.coffee new file mode 100644 index 00000000..7353c3a9 --- /dev/null +++ b/backend/app/assets/javascripts/comable/admin/variants.coffee @@ -0,0 +1,90 @@ +class @Variant + initialized: false + + constructor: -> + @initialize_tagits() + @register_click_event_to_add_variant_button() + @register_click_event_to_remove_variant_button() + @initialized = true + + initialize_tagits: -> + _this = @ + $('.js-tagit-option-values').each( -> + _this.initialize_tagit(this) + ) + + initialize_tagit: (element) -> + $element = $(element) + index = $element.data('index') + $element.tagit({ + fieldName: 'product[option_types_attributes][' + index + '][values][]', + caseSensitive: false, + removeConfirmation: true, + afterTagAdded: @rebuild_variants, + afterTagRemoved: @rebuild_variants + }) + + register_click_event_to_add_variant_button: -> + $('.js-add-variats').click( => + setTimeout( => + @initialize_tagits() + , 1) + ) + + register_click_event_to_remove_variant_button: -> + $(document).on('click', '.js-remove-variant', -> + $(this).closest('.js-new-variants').remove() + ) + + rebuild_variants: (event, ui) => + return unless @initialized + @remove_variants() + @build_variants() + + build_variants: -> + _this = @ + option_types = [] + $('.js-tagit-option-values').each( -> + return unless $(this).hasClass('tagit') + option_values = $(this).tagit('assignedTags') + option_types.push(option_values) + ) + option_values_for_variants = _product(option_types) + console.log(option_values_for_variants) + option_values_for_variants.forEach((option_values_for_variant) -> + _this.build_variant(option_values_for_variant) + ) + + build_variant: (option_values) -> + $variant = @new_variant() + + $variant.find('[data-name="names"] > input').val(option_values) + option_values.forEach((option_value) -> + $variant.find('[data-name="names"]').append('' + option_value + ' ') + ) + + $table = $variant.siblings('table') + $table.find('tbody').append($variant) + + remove_variants: -> + $('.js-new-variants:not(.hidden)').remove() + + new_variant: -> + new_id = new Date().getTime() + $('.js-add-variant2').click() + $variant = $('.js-new-variants').last() + $variant.removeClass('hidden') + $variant.html($variant.html().replace(/new_variant/g, new_id)) + $variant + + # refs http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/ + _product = (arrays) -> + Array.prototype.reduce.call(arrays, (a, b) -> + ret = [] + a.forEach((a) -> + b.forEach((b) -> + ret.push(a.concat([b])) + ) + ) + ret + , [[]]) diff --git a/backend/app/assets/stylesheets/comable/admin/_variants.scss b/backend/app/assets/stylesheets/comable/admin/_variants.scss new file mode 100644 index 00000000..90a1df34 --- /dev/null +++ b/backend/app/assets/stylesheets/comable/admin/_variants.scss @@ -0,0 +1,29 @@ +$comable-colors: #ffa000 #5dd39e #d81159 #06aed5 #8f2d56; + +@for $i from 1 through length($comable-colors) { + $color: nth($comable-colors, $i); + + .comable-variant-name:nth-of-type(#{$i}) { + color: darken($color, 5%); + } + + .comable-option:nth-of-type(#{$i}) { + .comable-option-name { + color: darken($color, 5%); + } + + ul.comable-option-values li:not(.tagit-new) { + background-color: $color; + border-color: $color; + + & > span, & > a, & > a > span { + color: #fff; + } + + &.remove { + background-color: darken($color, 10%); + border-color: darken($color, 10%); + } + } + } +} diff --git a/backend/app/assets/stylesheets/comable/admin/application.scss b/backend/app/assets/stylesheets/comable/admin/application.scss index 220f025a..74880967 100644 --- a/backend/app/assets/stylesheets/comable/admin/application.scss +++ b/backend/app/assets/stylesheets/comable/admin/application.scss @@ -25,3 +25,4 @@ @import 'comable/admin/navigations'; @import 'comable/admin/user_sessions'; @import 'comable/admin/themes'; +@import 'comable/admin/variants'; diff --git a/backend/app/controllers/comable/admin/products_controller.rb b/backend/app/controllers/comable/admin/products_controller.rb index 867bacbd..7b736b19 100644 --- a/backend/app/controllers/comable/admin/products_controller.rb +++ b/backend/app/controllers/comable/admin/products_controller.rb @@ -15,6 +15,8 @@ def show end def new + @product.variants.build + # @product.option_types.build(name: Comable.t('admin.size')) @product.published_at = Date.today end @@ -66,21 +68,17 @@ def import private - # rubocop:disable Metrics/MethodLength def product_params params.require(:product).permit( :name, - :code, :caption, - :price, :published_at, - :sku_h_item_name, - :sku_v_item_name, category_path_names: [], - images_attributes: [:id, :file, :_destroy] + images_attributes: [:id, :file, :_destroy], + variants_attributes: [:id, :price, :sku, :names, :quantity], + option_types_attributes: [:id, :name, { values: [] }] ) end - # rubocop:enable Metrics/MethodLength end end end diff --git a/backend/app/helpers/comable/admin/application_helper.rb b/backend/app/helpers/comable/admin/application_helper.rb index 71ae49bb..fb9ca9c4 100644 --- a/backend/app/helpers/comable/admin/application_helper.rb +++ b/backend/app/helpers/comable/admin/application_helper.rb @@ -19,19 +19,23 @@ def link_to_add_fields(name, f, association, options = {}) end def button_to_remove_fields(name, options = {}) - content_tag(:button, name, options.merge(class: "ransack remove_fields #{options[:class]}")) + content_tag(:button, name, options.merge(class: "remove_fields #{options[:class]}")) end def button_to_add_fields(name, f, type, options = {}) new_fields = build_fields(f, type) - content_tag(:button, name, options.merge(class: "ransack add_fields #{options[:class]}", 'data-field-type' => type, 'data-content' => "#{new_fields}")) + content_tag(:button, name, options.merge(class: "add_fields #{options[:class]}", 'data-field-type' => type, 'data-content' => "#{new_fields}")) end def build_fields(f, type) - new_object = f.object.send("build_#{type}") - - f.send("#{type}_fields", new_object, child_index: "new_#{type}") do |builder| - render("comable/admin/shared/#{type}_fields", f: builder) + render_block = -> (builder) { render("comable/admin/shared/#{type}_fields", f: builder) } + + if singular? type + new_object = f.object.send("build_#{type}") + f.send("#{type}_fields", new_object, child_index: "new_#{type}", &render_block) + else + new_object = f.object.send(type).klass.new + f.send('fields_for', type, new_object, child_index: "new_#{type}", &render_block) end end @@ -48,6 +52,12 @@ def enable_advanced_search? def page_name [controller_name, action_name].join(':') end + + private + + def singular?(string) + string.to_s.try(:singularize) == string.to_s + end end end end diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 865ff2d7..2709a6a9 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -14,21 +14,13 @@ .help-block .col-md-9 - .form-group - = f.label :code - = f.text_field :code - .form-group = f.label :name = f.text_field :name .form-group = f.label :caption - = f.text_field :caption - - .form-group - = f.label :price - = f.text_field :price + = f.text_area :caption / TODO: Refactoring javascript: @@ -41,64 +33,6 @@ li = category.path.map(&:name).join(' > ') - / hr - - / fieldset - .col-md-3 - legend - = Comable.t('admin.stocks') - .help-block - - if @product.sku? || (@product.persisted? && @product.stocks.empty?) - = Comable.t('admin.link_to_add_new_stock') - = link_to Comable.t('admin.actions.new'), comable.new_admin_product_stock_path(@product) - - .col-md-9 - - if @product.stocks.any? - table.table.table-striped - thead - th - = @product.stocks.human_attribute_name(:code) - th - = @product.stocks.human_attribute_name(:quantity) - - if @product.sku? - - if @product.sku_h? - th - = @product.sku_h_item_name - - if @product.sku_v? - th - = @product.sku_v_item_name - th - = Comable.t('admin.operation') - tbody - - @product.stocks.each do |stock| - tr - td - = stock.code - td - strong class="#{(stock.quantity <= 0) ? 'text-danger' : (stock.quantity <= 10) ? 'text-warning' : 'text-success'}" - = number_with_delimiter stock.quantity - - if @product.sku? - - if @product.sku_h? - td - = stock.sku_h_choice_name - - if @product.sku_v? - td - = stock.sku_v_choice_name - td - = link_to comable.admin_stock_path(stock), class: 'btn btn-default' - i.fa.fa-edit> - = Comable.t('admin.actions.edit') - - .form-group - = f.label :sku_h_item_name - = f.text_field :sku_h_item_name - .help-block - = Comable.t('admin.please_fill_when_using_sku') - - .form-group - = f.label :sku_v_item_name - = f.text_field :sku_v_item_name - hr fieldset @@ -138,22 +72,30 @@ hr + - if @product.is_master? + = f.fields_for :variants do |ff| + = render 'comable/admin/shared/variant_form', f: ff + hr + fieldset .col-md-3 legend = Comable.t('admin.variants') .col-md-9 - - if @product.variants.any? + = f.fields_for :option_types do |ff| + = render 'comable/admin/shared/option_types_fields', f: ff + = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' + + - if @product.persisted? && @product.is_master? table.table.table-striped thead th = @product.variants.human_attribute_name(:sku) th = @product.variants.human_attribute_name(:quantity) - - @product.option_types.each do |option_type| - th - = option_type.name + th + = @product.variants.human_attribute_name(:variants) th = Comable.t('admin.operation') tbody @@ -165,23 +107,29 @@ - quantity = variant.quantity strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" = number_with_delimiter quantity - - variant.option_values.each do |option_value| - th - = option_value.name td - = link_to comable.admin_stock_path(variant.stock), class: 'btn btn-default' + - variant.names.each do |name| + span.comable-variant-name> + = name + td + = link_to comable.admin_product_variant_path(@product, variant), class: 'btn btn-default' i.fa.fa-edit> = Comable.t('admin.actions.edit') - .form-group - = f.label :sku_h_item_name - = f.text_field :sku_h_item_name - .help-block - = Comable.t('admin.please_fill_when_using_sku') - - .form-group - = f.label :sku_v_item_name - = f.text_field :sku_v_item_name + - if @product.new_record? + table.table.table-striped + thead + th + th + = @product.variants.human_attribute_name(:variants) + th + = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:quantity) + tbody + = f.fields_for :variants do |ff| + = render 'comable/admin/shared/variants_fields', f: ff + = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant2' hr diff --git a/backend/app/views/comable/admin/shared/_option_types_fields.slim b/backend/app/views/comable/admin/shared/_option_types_fields.slim new file mode 100644 index 00000000..6c3cd47e --- /dev/null +++ b/backend/app/views/comable/admin/shared/_option_types_fields.slim @@ -0,0 +1,11 @@ +.comable-option + .form-group + .row + .col-xs-4 + = f.text_field :name, placeholder: Comable.t('admin.size'), class: 'comable-option-name' + + .col-xs-8 + ul.js-tagit-option-values.comable-option-values data-index="#{f.index}" + - f.object.values.each do |value| + li + = value diff --git a/backend/app/views/comable/admin/shared/_variant_form.slim b/backend/app/views/comable/admin/shared/_variant_form.slim new file mode 100644 index 00000000..016656af --- /dev/null +++ b/backend/app/views/comable/admin/shared/_variant_form.slim @@ -0,0 +1,26 @@ +fieldset + .col-md-3 + legend + = Comable.t('admin.pricing') + + .col-md-9 + .form-group + = f.label :price + = f.text_field :price + +hr + +fieldset + .col-md-3 + legend + = Comable.t('admin.stocks') + + .col-md-9 + .form-group + = f.label :sku + = f.text_field :sku + + = f.fields_for :stock, f.object.stock || f.object.build_stock do |ff| + .form-group + = ff.label :quantity + = ff.text_field :quantity diff --git a/backend/app/views/comable/admin/shared/_variants_fields.slim b/backend/app/views/comable/admin/shared/_variants_fields.slim new file mode 100644 index 00000000..2217c240 --- /dev/null +++ b/backend/app/views/comable/admin/shared/_variants_fields.slim @@ -0,0 +1,12 @@ +tr.js-new-variants + td + = f.check_box :_destroy, { checked: true }, *({ checked_value: 0, unchecked_value: 1 }.values) + td data-name="names" + = f.hidden_field :names, value: f.object.names.join(',') + - f.object.names.each do |name| + span.comable-variant-name> + = name + td data-name="sku" + = f.text_field :sku + td data-name="quantity" + = f.text_field :quantity diff --git a/backend/app/views/comable/admin/variants/._form.slim.swo b/backend/app/views/comable/admin/variants/._form.slim.swo deleted file mode 100644 index 6e9e2b749d30b53845f5e6e7a5c10f052c25e9cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3UyLM09mi|+#6N%-h$P%4T6f1XySm-8>k*ZKxx08gXz;>mJR=y=)=byTG~3hN zcGt}8vb*9R;w40sCmx7!iEt2n@qt7Qcrp5*#sHDT2Tu^A!58tO@kxn6zt!EgxK{_Ow<%{jrnuL*|VH$GZr5W#Q%1w;nr4&i|Q^O+V^6 zHj_D1kHFz!=nK>J+sw87K6AW@<-d?BtVdzH-bT5W%j+%HUgw@&XF*UOI(!smIn3@m zUVX?yhj}8ZTOB{_o004ECn~QJD-Tp2*xdsI(J?-JB{}kewN+W@@S%hBnyXLmUcSny zJWzR{@<8Q*$^(@LDi2g1s60@4;Q!78B3K~zqE&B7TXu7LUMQX4oL+xCJxdvRsB+0w z^;vnK@<8Q*$^(@LDi2g1s60@4pz=WFfyx7w2PzNzH+q2CgnZ`hgnaHDunzzKSMmSv zuOj4k;3;qxJP5u4?gK;Mfc@ZwcM|dpI19c4MxX``fak9yy|@D$ht26*ubLVf{$ z3?2es2DgFtgZF_&a6PyVTn^s2oRACPSK#O1``|4268Iw60`~wPFz_j`3hLk>xC;CY z?Ry!#1il9z1NQ;h?%TmX;Dd|cMX-qL>(jIgxc({l3HTb=0H=Tf4uJjO)wiK7zy)`J zWpFik?X8473mykM;7;&f@bG1XoB=&>8r%x51($&fZ$TTuBjA3}1r1OG`@kRe5poVZ z1nva4ft$f~;P2R^c@6v#oB^K%`@mnZx$^?}5qKIr0nUL(!6V?Cpbyr-dw_P(84j1V zxNC;I?+-aCmp7=>p`DKDx9;ZcaweryWyg!ppK@sxOqD7$Ntgi-BPSBvK1OyiZw77S zk~0XUUY%+}lRACY<(6RMRg_Yu)`+ru<(JfrUrtp}@anj1DP?w5(mN$#TuznWGh6C& z=T=%2*u#SVL>0vf_iA&gbm?*ib4wD1%lW-EuS8+|%y>(5vzbYlgU}@flc_0sgJO!j zn(7GMh&e^ai_<3c1L63dHDvC9n;z?r&#!dPQm#XC-3C3LzEEfCx+9abc8}UV-S+ds z@>{`%V%GSTEL=`4D45WQdCk*2vQ1QF1NU(HKA|C4*AZDplXlFw$7rL?1n>Hx!=p6U zlrIz--OwKdBoj#0V7`PJiw&6raaYct-3ZP&ycMz$J(0Y-*l3(cbZ(l=byi3awu&?!f&s~qqx)A z&ihmr6FjdtS|4O}%Dl9~Jh@zx+4fzdZyPu1I@3X5iltiorm7j&9@Ump-7fFqz1H|b z$sfU0VlNj`RWH;P7L5!-r=8#KnwfgWr5P$UI9~TsEtO*=?%Uj>rKu^-PrWL$R*BykVD)r>QGSRCR2k*>y}8%f4j(FdV`AwC%DeY92bgRkNK)&Kh=2u6|RkwAsui z-5 z-b_WB-16<>OrqAk-CSjogeUz99XYn>HR#&vD!d{4twC$C7mYYl5w%Qz?#NlZR@Ub7 zj;LX*rs~q3qBzu;El&!`xQ_;{e*7FW@e01pJ%W1*kE?i|`)q2+3T?#WcO`2fzNPNI zE4e=~3pS~IJ3qAgJc1V7QkN+ zyZ;892j2!V&R+n}AwGWqY=Bds34V)M{5&`Vz6u2R1Xu>|1}`Bd|26m-I0tsXHW+~+ zSO<53Q{VvD53T^OBW8aU{1OOo3%CJX3%-k(eH%#sy&-*A9hCJ++W=tNG-aS_R7ZTmN4A4Fo-(2{FvtFQ``qcjiJCKSTIakY1hrFiMv5cT%McM8}wT|v__8TSvJSdM=`EK zI<~uz3t=wvy>1@OEvd5fmL^?YrpIVaPL+nuye_6O+MtCJYg&;J3q2WWqf85r53953UN~!ZiXXkSHZiW(|bWB8q1-0`|gx@1ig?lm9(B4Kji zmWJ$v9?yo#`2IlBjFOCIv9__G#m`!59z!Rpv{+NcsAg}_XC59KB6RRH;qmmdlT2tbJh~(Z6;5G*=BSlmvzyc zQ@C@_I~gJx|= z#+w;WF>xJBYs;}rES=V?4?i>XN0|Xrnknmw0_Gi^bX%#1fz9QNl|Ouyfyr5kW5+ID P`&cc1gElY|#zN#@?dR}) diff --git a/backend/app/views/comable/admin/variants/_form.slim b/backend/app/views/comable/admin/variants/_form.slim index d89dfc14..d9a919b6 100644 --- a/backend/app/views/comable/admin/variants/_form.slim +++ b/backend/app/views/comable/admin/variants/_form.slim @@ -31,29 +31,4 @@ hr - fieldset - .col-md-3 - legend - = Comable.t('admin.pricing') - - .col-md-9 - .form-group - = f.label :price - = f.text_field :price - - hr - - fieldset - .col-md-3 - legend - = Comable.t('admin.stocks') - - .col-md-9 - .form-group - = f.label :sku - = f.text_field :sku - - = f.fields_for :stock do |ff| - .form-group - = ff.label :quantity - = ff.text_field :quantity + = render 'comable/admin/shared/variant_form', f: f diff --git a/backend/app/views/comable/admin/variants/edit.slim b/backend/app/views/comable/admin/variants/edit.slim index 88588a94..4b5b7203 100644 --- a/backend/app/views/comable/admin/variants/edit.slim +++ b/backend/app/views/comable/admin/variants/edit.slim @@ -2,20 +2,10 @@ .comable-main-fixed-top .comable-page-heading ul.pull-right.list-inline - li.dropdown - = link_to '#', class: 'btn btn-default', 'data-toggle' => 'dropdown' do - i.fa.fa-bars - ul.dropdown-menu.dropdown-menu-right - li - = link_to comable.product_path(@product) do - span.fa.fa-external-link> - = Comable.t('admin.check_this_product_in_frontend') - - if @product.sku? || @product.stocks.empty? - li.divider - li.dropdown-header - = Comable.t('admin.nav.stock') - li - = link_to Comable.t('admin.actions.new'), comable.new_admin_product_stock_path(@product) + li + = link_to comable.product_path(@product), class: 'btn btn-default' do + span.fa.fa-external-link> + = Comable.t('admin.view') li = link_to_save diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index 0d6221ad..782f454e 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -6,5 +6,16 @@ class OptionType < ActiveRecord::Base validates :name, presence: true, length: { maximum: 255 } default_scope { order(:id) } + + def values + option_values.map(&:name) + end + + def values=(values) + values = values.split(' ') if values.is_a? String + self.option_values = values.map do |value| + value.is_a?(Comable::OptionValue) ? value : option_values.where(name: value).first_or_initialize + end + end end end diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index cf768589..00df49da 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -52,6 +52,10 @@ def category_path_names=(category_path_names, delimiter: Comable::Category::DEFA self.categories = Comable::Category.find_by_path_names(category_path_names, delimiter: delimiter) end + def is_master? + option_types.empty? + end + def sku_h_item_name option_types.first.try(:name) end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 5e135e34..c088991a 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -11,12 +11,33 @@ class Variant < ActiveRecord::Base validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } + def names=(names) + attributes = [] + names.split(',').each.with_index do |name, index| + option_type = product.option_types[index] if product && option_types.size > index + attributes << { option_type: option_type, name: name } + end + self.option_values_attributes = attributes + end + def name - option_values.map { |option_value| option_value.name }.join(' x ') + names.join(' x ') + end + + def names + option_values.map(&:name) end def quantity - stock.quantity + stock.try(:quantity) + end + + def quantity=(quantity) + if stock + stock.quantity = quantity + else + build_stock(quantity: quantity) + end end # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 96dc5232..6040e6ed 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -167,6 +167,9 @@ en: here_editor_will_be_displayed_and_you_can_edit_file: 'Here the editor will be displayed, you can edit file.' variants: 'Variants' pricing: 'Pricing' + view: 'View' + add_variants: 'Add variants' + size: 'Size' nav: dashboard: 'Dashboard' order: 'Orders' diff --git a/core/config/locales/ja.yml b/core/config/locales/ja.yml index 2dfc88f3..8946fb3f 100644 --- a/core/config/locales/ja.yml +++ b/core/config/locales/ja.yml @@ -167,6 +167,9 @@ ja: here_editor_will_be_displayed_and_you_can_edit_file: 'ここにエディタが表示されてファイルの内容を編集できます。' variants: 'バリエーション' pricing: '価格' + view: '確認する' + add_variants: 'バリエーションを追加する' + size: 'サイズ' nav: dashboard: ダッシュボード order: 注文管理 From 0ca86eb1bc9f9692b1af986a53c8867a501076e4 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 13:38:06 +0900 Subject: [PATCH 22/58] Fix the behavior to create a product with variants --- .../javascripts/comable/admin/variants.coffee | 2 ++ .../comable/admin/products_controller.rb | 2 +- .../comable/admin/application_helper.rb | 2 +- .../views/comable/admin/products/_form.slim | 11 +++++--- core/app/models/comable/option_type.rb | 10 +++---- core/app/models/comable/product.rb | 2 +- core/app/models/comable/variant.rb | 26 ++++++++++++------- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/backend/app/assets/javascripts/comable/admin/variants.coffee b/backend/app/assets/javascripts/comable/admin/variants.coffee index 7353c3a9..134d036e 100644 --- a/backend/app/assets/javascripts/comable/admin/variants.coffee +++ b/backend/app/assets/javascripts/comable/admin/variants.coffee @@ -26,6 +26,7 @@ class @Variant register_click_event_to_add_variant_button: -> $('.js-add-variats').click( => + $('#product_variants_attributes_0__destroy').val(1) setTimeout( => @initialize_tagits() , 1) @@ -34,6 +35,7 @@ class @Variant register_click_event_to_remove_variant_button: -> $(document).on('click', '.js-remove-variant', -> $(this).closest('.js-new-variants').remove() + $('#product_variants_attributes_0__destroy').val(0) if $('.js-new-variants').length == 0 ) rebuild_variants: (event, ui) => diff --git a/backend/app/controllers/comable/admin/products_controller.rb b/backend/app/controllers/comable/admin/products_controller.rb index 7b736b19..1583dbe4 100644 --- a/backend/app/controllers/comable/admin/products_controller.rb +++ b/backend/app/controllers/comable/admin/products_controller.rb @@ -75,7 +75,7 @@ def product_params :published_at, category_path_names: [], images_attributes: [:id, :file, :_destroy], - variants_attributes: [:id, :price, :sku, :names, :quantity], + variants_attributes: [:id, :price, :sku, :names, :quantity, :_destroy], option_types_attributes: [:id, :name, { values: [] }] ) end diff --git a/backend/app/helpers/comable/admin/application_helper.rb b/backend/app/helpers/comable/admin/application_helper.rb index fb9ca9c4..89f36088 100644 --- a/backend/app/helpers/comable/admin/application_helper.rb +++ b/backend/app/helpers/comable/admin/application_helper.rb @@ -34,7 +34,7 @@ def build_fields(f, type) new_object = f.object.send("build_#{type}") f.send("#{type}_fields", new_object, child_index: "new_#{type}", &render_block) else - new_object = f.object.send(type).klass.new + new_object = f.object.send(type).build f.send('fields_for', type, new_object, child_index: "new_#{type}", &render_block) end end diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 2709a6a9..f2ab2812 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -72,9 +72,10 @@ hr - - if @product.is_master? - = f.fields_for :variants do |ff| + - if @product.new_record? || @product.is_master? + = f.fields_for :variants, f.object.variants.first do |ff| = render 'comable/admin/shared/variant_form', f: ff + = ff.hidden_field :_destroy, value: !f.object.is_master? hr fieldset @@ -85,9 +86,11 @@ .col-md-9 = f.fields_for :option_types do |ff| = render 'comable/admin/shared/option_types_fields', f: ff - = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' - - if @product.persisted? && @product.is_master? + - if @product.new_record? + = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' + + - if @product.persisted? && !@product.is_master? table.table.table-striped thead th diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index 782f454e..f39fb9cb 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -1,21 +1,19 @@ module Comable class OptionType < ActiveRecord::Base has_many :option_values, class_name: Comable::OptionValue.name - belongs_to :product, class_name: Comable::Product.name + belongs_to :product, class_name: Comable::Product.name, inverse_of: :option_types, autosave: true + validates :product, presence: { message: Comable.t('admin.is_not_exists') } validates :name, presence: true, length: { maximum: 255 } default_scope { order(:id) } def values - option_values.map(&:name) + @values ? @values : option_values.map(&:name) end def values=(values) - values = values.split(' ') if values.is_a? String - self.option_values = values.map do |value| - value.is_a?(Comable::OptionValue) ? value : option_values.where(name: value).first_or_initialize - end + @values = values.is_a?(String) ? values.split(' ') : values end end end diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 00df49da..575025b6 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -9,7 +9,7 @@ class Product < ActiveRecord::Base has_many :variants, class_name: Comable::Variant.name, inverse_of: :product, dependent: :destroy has_many :images, class_name: Comable::Image.name, dependent: :destroy - has_many :option_types, class_name: Comable::OptionType.name, dependent: :destroy + has_many :option_types, class_name: Comable::OptionType.name, inverse_of: :product, dependent: :destroy has_and_belongs_to_many :categories, class_name: Comable::Category.name, join_table: :comable_products_categories accepts_nested_attributes_for :variants, allow_destroy: true diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index c088991a..68103dec 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -11,21 +11,16 @@ class Variant < ActiveRecord::Base validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } - def names=(names) - attributes = [] - names.split(',').each.with_index do |name, index| - option_type = product.option_types[index] if product && option_types.size > index - attributes << { option_type: option_type, name: name } - end - self.option_values_attributes = attributes - end + before_validation :set_option_values_from_names, if: :product + + attr_writer :names def name names.join(' x ') end def names - option_values.map(&:name) + @names ? @names : option_values.map(&:name) end def quantity @@ -53,5 +48,18 @@ def option_values_attributes=(attributes) end super end + + private + + def set_option_values_from_names + return unless @names + attributes = [] + @names.split(',').each.with_index do |name, index| + option_type = product.option_types[index] + attributes << { option_type: option_type, name: name } + end + self.names = nil + self.option_values_attributes = attributes + end end end From c4b754ec6f27a6fab0d3f63d0f9fa5f609a48f5e Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 14:17:56 +0900 Subject: [PATCH 23/58] Fix the behavior to update the product with variants --- .../javascripts/comable/admin/variants.coffee | 24 ++++++++++++++----- .../stylesheets/comable/admin/_common.scss | 9 +++++++ .../views/comable/admin/products/_form.slim | 4 ++-- .../admin/shared/_option_types_fields.slim | 6 +++-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/backend/app/assets/javascripts/comable/admin/variants.coffee b/backend/app/assets/javascripts/comable/admin/variants.coffee index 134d036e..0471f3a2 100644 --- a/backend/app/assets/javascripts/comable/admin/variants.coffee +++ b/backend/app/assets/javascripts/comable/admin/variants.coffee @@ -20,13 +20,14 @@ class @Variant fieldName: 'product[option_types_attributes][' + index + '][values][]', caseSensitive: false, removeConfirmation: true, + readOnly: $element.hasClass('tagit-readonly'), afterTagAdded: @rebuild_variants, afterTagRemoved: @rebuild_variants }) register_click_event_to_add_variant_button: -> $('.js-add-variats').click( => - $('#product_variants_attributes_0__destroy').val(1) + @change_from_master() setTimeout( => @initialize_tagits() , 1) @@ -35,7 +36,7 @@ class @Variant register_click_event_to_remove_variant_button: -> $(document).on('click', '.js-remove-variant', -> $(this).closest('.js-new-variants').remove() - $('#product_variants_attributes_0__destroy').val(0) if $('.js-new-variants').length == 0 + @change_to_master() ) rebuild_variants: (event, ui) => @@ -51,8 +52,7 @@ class @Variant option_values = $(this).tagit('assignedTags') option_types.push(option_values) ) - option_values_for_variants = _product(option_types) - console.log(option_values_for_variants) + option_values_for_variants = _product(_compact(option_types)) option_values_for_variants.forEach((option_values_for_variant) -> _this.build_variant(option_values_for_variant) ) @@ -65,8 +65,7 @@ class @Variant $variant.find('[data-name="names"]').append('' + option_value + ' ') ) - $table = $variant.siblings('table') - $table.find('tbody').append($variant) + $('.js-variants-table').find('tbody').append($variant) remove_variants: -> $('.js-new-variants:not(.hidden)').remove() @@ -79,6 +78,19 @@ class @Variant $variant.html($variant.html().replace(/new_variant/g, new_id)) $variant + change_from_master: -> + $('.js-variants-table').removeClass('hidden') + $('#product_variants_attributes_0__destroy').val(1) + + change_to_master: -> + $('.js-variants-table').removeClass('hidden') + $('.js-variants-table').addClass('hidden') + $('#product_variants_attributes_0__destroy').val(0) if $('.js-new-variants').length == 0 + + # refs http://stackoverflow.com/questions/281264/remove-empty-elements-from-an-array-in-javascript/2843625#2843625 + _compact = (arrays) -> + $.grep(arrays, (n) -> n if n && n.length != 0) + # refs http://cwestblog.com/2011/05/02/cartesian-product-of-multiple-arrays/ _product = (arrays) -> Array.prototype.reduce.call(arrays, (a, b) -> diff --git a/backend/app/assets/stylesheets/comable/admin/_common.scss b/backend/app/assets/stylesheets/comable/admin/_common.scss index f24bea66..b117b4a4 100644 --- a/backend/app/assets/stylesheets/comable/admin/_common.scss +++ b/backend/app/assets/stylesheets/comable/admin/_common.scss @@ -151,3 +151,12 @@ footer { cursor: inherit; display: block; } + +// for Tag-it +ul.tagit { + @include border-radius($input-border-radius); + + &.tagit-readonly { + background-color: #eee; + } +} diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index f2ab2812..d415e07a 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -85,7 +85,7 @@ .col-md-9 = f.fields_for :option_types do |ff| - = render 'comable/admin/shared/option_types_fields', f: ff + = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? - if @product.new_record? = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' @@ -120,7 +120,7 @@ = Comable.t('admin.actions.edit') - if @product.new_record? - table.table.table-striped + table.table.table-striped.hidden.js-variants-table thead th th diff --git a/backend/app/views/comable/admin/shared/_option_types_fields.slim b/backend/app/views/comable/admin/shared/_option_types_fields.slim index 6c3cd47e..9a7e1acd 100644 --- a/backend/app/views/comable/admin/shared/_option_types_fields.slim +++ b/backend/app/views/comable/admin/shared/_option_types_fields.slim @@ -1,11 +1,13 @@ +- readonly ||= false + .comable-option .form-group .row .col-xs-4 - = f.text_field :name, placeholder: Comable.t('admin.size'), class: 'comable-option-name' + = f.text_field :name, placeholder: Comable.t('admin.size'), class: 'comable-option-name', disabled: readonly .col-xs-8 - ul.js-tagit-option-values.comable-option-values data-index="#{f.index}" + ul.js-tagit-option-values.comable-option-values data-index="#{f.index}" class="#{'tagit-readonly' if readonly}" - f.object.values.each do |value| li = value From 16559be1382c9454684de428cfe81b830facd1c5 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 14:18:24 +0900 Subject: [PATCH 24/58] Improve the look and feel for the index of products --- .../stylesheets/comable/admin/_variants.scss | 11 ++++++ .../comable/admin/products_controller.rb | 3 +- .../views/comable/admin/products/_form.slim | 2 +- .../views/comable/admin/products/index.slim | 20 +++++------ .../admin/shared/_variants_fields.slim | 2 +- core/app/models/comable/option_type.rb | 4 +++ core/app/models/comable/option_value.rb | 4 +++ core/app/models/comable/product.rb | 2 +- core/app/models/comable/stock.rb | 2 +- core/app/models/comable/variant.rb | 4 +++ core/config/locales/en.yml | 34 +++++++++++++++---- core/config/locales/ja.yml | 34 +++++++++++++++---- 12 files changed, 94 insertions(+), 28 deletions(-) diff --git a/backend/app/assets/stylesheets/comable/admin/_variants.scss b/backend/app/assets/stylesheets/comable/admin/_variants.scss index 90a1df34..4686d3f4 100644 --- a/backend/app/assets/stylesheets/comable/admin/_variants.scss +++ b/backend/app/assets/stylesheets/comable/admin/_variants.scss @@ -5,6 +5,17 @@ $comable-colors: #ffa000 #5dd39e #d81159 #06aed5 #8f2d56; .comable-variant-name:nth-of-type(#{$i}) { color: darken($color, 5%); + + &:after { + display: inline; + margin: 0 5px; + color: #444; + content: '·'; + } + + &:last-child:after { + display: none; + } } .comable-option:nth-of-type(#{$i}) { diff --git a/backend/app/controllers/comable/admin/products_controller.rb b/backend/app/controllers/comable/admin/products_controller.rb index 1583dbe4..a19cfaca 100644 --- a/backend/app/controllers/comable/admin/products_controller.rb +++ b/backend/app/controllers/comable/admin/products_controller.rb @@ -7,7 +7,7 @@ class ProductsController < Comable::Admin::ApplicationController def index @q = Comable::Product.ransack(params[:q]) - @products = @q.result(distinct: true).includes(:stocks, :images).page(params[:page]).accessible_by(current_ability) + @products = @q.result(distinct: true).includes(:images, variants: [:option_values, :stock]).page(params[:page]).accessible_by(current_ability) end def show @@ -16,7 +16,6 @@ def show def new @product.variants.build - # @product.option_types.build(name: Comable.t('admin.size')) @product.published_at = Date.today end diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index d415e07a..80071707 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -112,7 +112,7 @@ = number_with_delimiter quantity td - variant.names.each do |name| - span.comable-variant-name> + span.comable-variant-name = name td = link_to comable.admin_product_variant_path(@product, variant), class: 'btn btn-default' diff --git a/backend/app/views/comable/admin/products/index.slim b/backend/app/views/comable/admin/products/index.slim index 42969f90..96e238d2 100644 --- a/backend/app/views/comable/admin/products/index.slim +++ b/backend/app/views/comable/admin/products/index.slim @@ -60,8 +60,6 @@ table.table.table-striped thead th - th - = sort_link [:comable, @q], :code th = sort_link [:comable, @q], :name th @@ -70,28 +68,30 @@ = Comable.t('admin.stocks') tbody - @products.each do |product| - - quantity = product.stocks.to_a.sum(&:quantity) tr td.comable-image = link_to comable.admin_product_path(product), class: 'thumbnail' do = image_tag product.image_url, width: '100%' td - = link_to product.code, comable.admin_product_path(product) + = link_to product.name, comable.admin_product_path(product) - unless product.published? span.fa.fa-eye-slash.text-muted< - td - = product.name td = number_to_currency product.price td ul.list-unstyled - - product.stocks.each do |stock| + - product.variants.each do |variant| li + - quantity = variant.quantity strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" - = number_with_delimiter stock.quantity - - if stock.sku? + = number_with_delimiter quantity + - if !product.is_master? span< - | (#{stock.sku_name}) + | ( + - variant.names.each do |name| + span.comable-variant-name + = name + | ) .text-center = paginate @products, theme: :comable_backend diff --git a/backend/app/views/comable/admin/shared/_variants_fields.slim b/backend/app/views/comable/admin/shared/_variants_fields.slim index 2217c240..0321dfd0 100644 --- a/backend/app/views/comable/admin/shared/_variants_fields.slim +++ b/backend/app/views/comable/admin/shared/_variants_fields.slim @@ -4,7 +4,7 @@ tr.js-new-variants td data-name="names" = f.hidden_field :names, value: f.object.names.join(',') - f.object.names.each do |name| - span.comable-variant-name> + span.comable-variant-name = name td data-name="sku" = f.text_field :sku diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index f39fb9cb..a6adadc8 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -1,5 +1,7 @@ module Comable class OptionType < ActiveRecord::Base + include Comable::Ransackable + has_many :option_values, class_name: Comable::OptionValue.name belongs_to :product, class_name: Comable::Product.name, inverse_of: :option_types, autosave: true @@ -8,6 +10,8 @@ class OptionType < ActiveRecord::Base default_scope { order(:id) } + ransack_options ransackable_attributes: { only: :name } + def values @values ? @values : option_values.map(&:name) end diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index 84bb841b..f9d6a684 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -1,10 +1,14 @@ module Comable class OptionValue < ActiveRecord::Base + include Comable::Ransackable + belongs_to :option_type, class_name: Comable::OptionType.name has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values validates :name, presence: true, length: { maximum: 255 } default_scope { order(:option_type_id) } + + ransack_options ransackable_attributes: { only: :name } end end diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 575025b6..684f0086 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -20,7 +20,7 @@ class Product < ActiveRecord::Base liquid_methods :id, :code, :name, :price, :images, :image_url - ransack_options attribute_select: { associations: :variants } + ransack_options attribute_select: { associations: [:variants, :stocks, :option_types] } linkable_columns_keys use_index: true diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 9a714cad..1f4094de 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -38,7 +38,7 @@ class Stock < ActiveRecord::Base delegate :sku_h_item_name, to: :product delegate :sku_v_item_name, to: :product - ransack_options attribute_select: { associations: :variant }, ransackable_attributes: { except: :variant_id } + ransack_options attribute_select: { associations: :variant }, ransackable_attributes: { only: :quantity } # 在庫の有無を取得する # diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 68103dec..2dcf735d 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -1,5 +1,7 @@ module Comable class Variant < ActiveRecord::Base + include Comable::Ransackable + belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values @@ -13,6 +15,8 @@ class Variant < ActiveRecord::Base before_validation :set_option_values_from_names, if: :product + ransack_options attribute_select: { associations: [:product, :stock, :option_values] }, ransackable_attributes: { except: :product_id } + attr_writer :names def name diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 6040e6ed..c5fc85a3 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -280,6 +280,9 @@ en: comable/order_items: 'Order items' attributes: + timestamps: ×tamps + created_at: 'Created at' + updated_at: 'Updated at' comable/user: id: 'ID' email: 'Email address' @@ -296,6 +299,7 @@ en: last_sign_in_at: 'Last sign in at' current_sign_in_ip: 'Current sign in ip' last_sign_in_ip: 'Last sign in ip' + <<: *timestamps comable/order: id: 'ID' code: 'Order number' @@ -319,6 +323,7 @@ en: guest_token: 'Guest token' payment_state: 'Payment state' shipment_state: 'Shipment state' + <<: *timestamps comable/order_item: &comable_order_item quantity: *quantity price: *price @@ -331,6 +336,7 @@ en: sku_h_choice_name: 'SKU Horizontal choice name' sku_v_choice_name: 'SKU Vertical choice name' order: 'Order' + <<: *timestamps comable/address: &comable_address full_name: 'Full name' family_name: 'Last name' @@ -340,6 +346,7 @@ en: city: 'City' detail: 'Detail' phone_number: 'Phone number' + <<: *timestamps comable/product: id: 'ID' code: 'Code' @@ -349,6 +356,8 @@ en: categories: 'Categories' sku_h_item_name: 'SKU Horizontal item name' sku_v_item_name: 'SKU Vertical item name' + published_at: 'Published at' + <<: *timestamps comable/stock: id: 'ID' code: 'SKU Code' @@ -357,27 +366,32 @@ en: sku_v_choice_name: 'SKU Vertical choice name' product: 'Product' product_code: 'Product code' + <<: *timestamps comable/store: name: 'Name' meta_keywords: 'Meta keywords' meta_description: 'Meta description' email: 'Email' + <<: *timestamps comable/shipment_method: name: 'Name' fee: 'Fee' activated_flag: 'Activated' traking_url: 'Tracking URL' + <<: *timestamps comable/payment_method: name: 'Name' payment_provider: 'Oayment provider' fee: 'Fee' enable_price: 'Enable price range' + <<: *timestamps comable/tracker: activated_flag: 'Activated' name: 'Name' tracker_id: 'Tracker ID' code: 'Code' place: 'Place' + <<: *timestamps comable/order/order_items: <<: *comable_order_item comable/order/bill_address: @@ -391,6 +405,7 @@ en: fee: 'Fee' state: 'Status' completed_at: 'Completed at' + <<: *timestamps comable/shipment: id: 'ID' order_id: 'Order ID' @@ -399,6 +414,7 @@ en: state: 'Status' tracking_number: 'Tracking number' completed_at: 'Completed at' + <<: *timestamps comable/page: id: 'ID' title: 'Title' @@ -409,26 +425,32 @@ en: meta_keywords: 'Meta Keywords' slug: 'Slug' published_at: 'Published at' - created_at: 'Created at' - updated_at: 'Updated at' + <<: *timestamps comable/navigation: id: 'ID' name: 'Name' - created_at: 'Created at' - updated_at: 'Updated at' + <<: *timestamps comable/navigation_item: id: 'ID' name: 'Name' linkable_type: 'Link to' linkable_id: '' url: 'Name' - created_at: 'Created at' - updated_at: 'Updated at' + <<: *timestamps comable/variant: id: 'ID' sku: 'SKU' price: 'Price' product: 'Product' + <<: *timestamps + comable/option_type: + id: 'ID' + name: 'Name' + <<: *timestamps + comable/option_value: + id: 'ID' + name: 'Name' + <<: *timestamps enumerize: comable/user: diff --git a/core/config/locales/ja.yml b/core/config/locales/ja.yml index 8946fb3f..6acdef3f 100644 --- a/core/config/locales/ja.yml +++ b/core/config/locales/ja.yml @@ -280,6 +280,9 @@ ja: comable/order_items: ご注文明細 attributes: + timestamps: ×tamps + created_at: '作成日' + updated_at: '更新日' comable/user: id: ID email: メールアドレス @@ -296,6 +299,7 @@ ja: last_sign_in_at: 最終ログイン日時 current_sign_in_ip: ログインIP last_sign_in_ip: 最終ログインIP + <<: *timestamps comable/order: id: ID code: 注文番号 @@ -319,6 +323,7 @@ ja: guest_token: ゲストトークン payment_state: '決済ステータス' shipment_state: '配送ステータス' + <<: *timestamps comable/order_item: &comable_order_item quantity: *quantity price: *price @@ -331,6 +336,7 @@ ja: sku_h_choice_name: SKU横軸選択肢名 sku_v_choice_name: SKU縦軸選択肢名 order: 注文 + <<: *timestamps comable/address: &comable_address full_name: お名前 family_name: 姓 @@ -340,6 +346,7 @@ ja: city: 市町村 detail: その他 phone_number: 電話番号 + <<: *timestamps comable/product: id: ID code: 商品コード @@ -349,6 +356,8 @@ ja: categories: カテゴリ sku_h_item_name: SKU横軸項目名 sku_v_item_name: SKU縦軸項目名 + published_at: '公開日' + <<: *timestamps comable/stock: id: ID code: SKU商品コード @@ -357,27 +366,32 @@ ja: sku_v_choice_name: SKU縦軸選択肢名 product: 商品 product_code: 商品コード + <<: *timestamps comable/store: name: ストア名 meta_keywords: メタキーワード meta_description: メタディスクリプション email: メールアドレス + <<: *timestamps comable/shipment_method: name: 発送方法名 fee: 手数料 activated_flag: この発送方法を有効にする traking_url: トラッキングURL + <<: *timestamps comable/payment_method: name: 決済方法名 payment_provider: 決済プロバイダ fee: '手数料' enable_price: 利用可能な価格範囲 + <<: *timestamps comable/tracker: activated_flag: 'このトラッキング情報を有効にする' name: 'トラッキング名' tracker_id: 'トラッキングID' code: 'トラッキングコード' place: '設置場所' + <<: *timestamps comable/order/order_items: <<: *comable_order_item comable/order/bill_address: @@ -391,6 +405,7 @@ ja: fee: '手数料' state: 'ステータス' completed_at: '決済日時' + <<: *timestamps comable/shipment: id: 'ID' order_id: '注文ID' @@ -399,6 +414,7 @@ ja: state: 'ステータス' tracking_number: 'トラッキング番号' completed_at: '発送日時' + <<: *timestamps comable/page: id: ID title: タイトル @@ -409,26 +425,32 @@ ja: meta_keywords: メタキーワード slug: スラッグ published_at: 公開日 - created_at: 作成日 - updated_at: 更新日 + <<: *timestamps comable/navigation: id: ID name: 名前 - created_at: 作成日 - updated_at: 更新日 + <<: *timestamps comable/navigation_item: id: ID name: 名前 linkable_type: リンク先 linkable_id: '' url: URL - created_at: 作成日 - updated_at: 更新日 + <<: *timestamps comable/variant: id: 'ID' sku: 'SKUコード' price: '価格' product: '商品' + <<: *timestamps + comable/option_type: + id: 'ID' + name: '名称' + <<: *timestamps + comable/option_value: + id: 'ID' + name: '名称' + <<: *timestamps enumerize: comable/user: From 86816568c470c50173042cbbc3d63d147ab17581 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 16:06:13 +0900 Subject: [PATCH 25/58] Improve the look and feel for the index of stocks --- .../comable/admin/stocks_controller.rb | 2 +- .../app/views/comable/admin/stocks/index.slim | 25 +++++++++++-------- core/config/locales/en.yml | 2 ++ core/config/locales/ja.yml | 2 ++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/backend/app/controllers/comable/admin/stocks_controller.rb b/backend/app/controllers/comable/admin/stocks_controller.rb index 89c33518..ff2999cf 100644 --- a/backend/app/controllers/comable/admin/stocks_controller.rb +++ b/backend/app/controllers/comable/admin/stocks_controller.rb @@ -10,7 +10,7 @@ class StocksController < Comable::Admin::ApplicationController def index @q = @stocks.ransack(params[:q]) - @stocks = @q.result.includes(variant: :product).page(params[:page]).accessible_by(current_ability) + @stocks = @q.result.includes(variant: [:product, :option_values]).page(params[:page]).accessible_by(current_ability) end def show diff --git a/backend/app/views/comable/admin/stocks/index.slim b/backend/app/views/comable/admin/stocks/index.slim index c054d3aa..a2961b03 100644 --- a/backend/app/views/comable/admin/stocks/index.slim +++ b/backend/app/views/comable/admin/stocks/index.slim @@ -57,25 +57,30 @@ table.table.table-striped thead th - = sort_link [:comable, @q], :code + = sort_link [:comable, @q], :name th - = sort_link [:comable, @q], :quantity - th - = sort_link [:comable, @q], :sku_h_choice_name + = sort_link [:comable, @q], :sku th - = sort_link [:comable, @q], :sku_v_choice_name + = sort_link [:comable, @q], :quantity tbody - @stocks.each do |stock| tr td - = link_to stock.code, comable.admin_stock_path(stock) + - if stock.variant.names.any? + p + = link_to stock.variant.product.name, comable.admin_product_variant_path(stock.variant.product, stock.variant) + p + - stock.variant.names.each do |name| + span.comable-variant-name + = name + - else + p + = link_to stock.variant.product.name, comable.admin_product_path(stock.variant.product) + td + = stock.variant.sku td strong class="#{(stock.quantity <= 0) ? 'text-danger' : (stock.quantity <= 10) ? 'text-warning' : 'text-success'}" = number_with_delimiter stock.quantity - td - = stock.sku_h_choice_name - td - = stock.sku_v_choice_name .text-center = paginate @stocks, theme: :comable_backend diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index c5fc85a3..c991b5b9 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -366,6 +366,8 @@ en: sku_v_choice_name: 'SKU Vertical choice name' product: 'Product' product_code: 'Product code' + name: 'Product name' + sku: 'SKU' <<: *timestamps comable/store: name: 'Name' diff --git a/core/config/locales/ja.yml b/core/config/locales/ja.yml index 6acdef3f..e98162af 100644 --- a/core/config/locales/ja.yml +++ b/core/config/locales/ja.yml @@ -366,6 +366,8 @@ ja: sku_v_choice_name: SKU縦軸選択肢名 product: 商品 product_code: 商品コード + name: '商品名' + sku: 'SKUコード' <<: *timestamps comable/store: name: ストア名 From ae24bc64136749bb15e9d5bfd1c75f8c63011716 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 16:11:25 +0900 Subject: [PATCH 26/58] Fix the indents --- .../views/comable/admin/products/_form.slim | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 80071707..052addb4 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -78,63 +78,63 @@ = ff.hidden_field :_destroy, value: !f.object.is_master? hr - fieldset - .col-md-3 - legend - = Comable.t('admin.variants') + - if @product.new_record? || @product.persisted? && !@product.is_master? + fieldset + .col-md-3 + legend + = Comable.t('admin.variants') + + .col-md-9 + = f.fields_for :option_types do |ff| + = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? + + - if @product.new_record? + = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' + + table.table.table-striped.hidden.js-variants-table + thead + th + th + = @product.variants.human_attribute_name(:variants) + th + = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:quantity) + tbody + = f.fields_for :variants do |ff| + = render 'comable/admin/shared/variants_fields', f: ff + = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant2' + + - if @product.persisted? + table.table.table-striped + thead + th + = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:quantity) + th + = @product.variants.human_attribute_name(:variants) + th + = Comable.t('admin.operation') + tbody + - @product.variants.each do |variant| + tr + td + = variant.sku + td + - quantity = variant.quantity + strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" + = number_with_delimiter quantity + td + - variant.names.each do |name| + span.comable-variant-name + = name + td + = link_to comable.admin_product_variant_path(@product, variant), class: 'btn btn-default' + i.fa.fa-edit> + = Comable.t('admin.actions.edit') - .col-md-9 - = f.fields_for :option_types do |ff| - = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? - - - if @product.new_record? - = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' - - - if @product.persisted? && !@product.is_master? - table.table.table-striped - thead - th - = @product.variants.human_attribute_name(:sku) - th - = @product.variants.human_attribute_name(:quantity) - th - = @product.variants.human_attribute_name(:variants) - th - = Comable.t('admin.operation') - tbody - - @product.variants.each do |variant| - tr - td - = variant.sku - td - - quantity = variant.quantity - strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" - = number_with_delimiter quantity - td - - variant.names.each do |name| - span.comable-variant-name - = name - td - = link_to comable.admin_product_variant_path(@product, variant), class: 'btn btn-default' - i.fa.fa-edit> - = Comable.t('admin.actions.edit') - - - if @product.new_record? - table.table.table-striped.hidden.js-variants-table - thead - th - th - = @product.variants.human_attribute_name(:variants) - th - = @product.variants.human_attribute_name(:sku) - th - = @product.variants.human_attribute_name(:quantity) - tbody - = f.fields_for :variants do |ff| - = render 'comable/admin/shared/variants_fields', f: ff - = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant2' - - hr + hr fieldset .col-md-3 From 4443903f277f8db2b477b1b14aa52b636929cff7 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 16:19:13 +0900 Subject: [PATCH 27/58] Improve the look and feel for the form of the product --- backend/app/views/comable/admin/products/_form.slim | 10 ++++++++-- .../views/comable/admin/shared/_variants_fields.slim | 2 ++ core/config/locales/en.yml | 1 + core/config/locales/ja.yml | 1 + ...73809_change_comable_products_and_comable_stocks.rb | 3 +++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 052addb4..7d1cb812 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -95,9 +95,11 @@ thead th th - = @product.variants.human_attribute_name(:variants) + = Comable.t('admin.variants') th = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:price) th = @product.variants.human_attribute_name(:quantity) tbody @@ -110,10 +112,12 @@ thead th = @product.variants.human_attribute_name(:sku) + th + = @product.variants.human_attribute_name(:price) th = @product.variants.human_attribute_name(:quantity) th - = @product.variants.human_attribute_name(:variants) + = Comable.t('admin.variants') th = Comable.t('admin.operation') tbody @@ -121,6 +125,8 @@ tr td = variant.sku + td + = number_to_currency variant.price td - quantity = variant.quantity strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" diff --git a/backend/app/views/comable/admin/shared/_variants_fields.slim b/backend/app/views/comable/admin/shared/_variants_fields.slim index 0321dfd0..e763140e 100644 --- a/backend/app/views/comable/admin/shared/_variants_fields.slim +++ b/backend/app/views/comable/admin/shared/_variants_fields.slim @@ -8,5 +8,7 @@ tr.js-new-variants = name td data-name="sku" = f.text_field :sku + td data-name="price" + = f.text_field :price td data-name="quantity" = f.text_field :quantity diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index c991b5b9..c5bfac03 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -444,6 +444,7 @@ en: sku: 'SKU' price: 'Price' product: 'Product' + quantity: 'Quantity' <<: *timestamps comable/option_type: id: 'ID' diff --git a/core/config/locales/ja.yml b/core/config/locales/ja.yml index e98162af..eb72bcfc 100644 --- a/core/config/locales/ja.yml +++ b/core/config/locales/ja.yml @@ -444,6 +444,7 @@ ja: sku: 'SKUコード' price: '価格' product: '商品' + quantity: '個数' <<: *timestamps comable/option_type: id: 'ID' diff --git a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb index 3a2554cc..268a31fe 100644 --- a/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb +++ b/core/db/migrate/20150823073809_change_comable_products_and_comable_stocks.rb @@ -42,6 +42,9 @@ def change dir.up { t.remove :sku_v_choice_name } dir.down { t.string :sku_v_choice_name } + dir.up { t.change :quantity, :integer, default: 0 } + dir.down { t.change :quantity, :integer, default: nil } + dir.up { t.change :variant_id, :integer, null: false } end end From 4356e130b6114b699a91f992fae093dc63bdde25 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 16:24:02 +0900 Subject: [PATCH 28/58] Rename CSS class names --- .../javascripts/comable/admin/variants.coffee | 14 +++++++------- .../app/views/comable/admin/products/_form.slim | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/app/assets/javascripts/comable/admin/variants.coffee b/backend/app/assets/javascripts/comable/admin/variants.coffee index 0471f3a2..88f5ddaf 100644 --- a/backend/app/assets/javascripts/comable/admin/variants.coffee +++ b/backend/app/assets/javascripts/comable/admin/variants.coffee @@ -3,8 +3,8 @@ class @Variant constructor: -> @initialize_tagits() - @register_click_event_to_add_variant_button() - @register_click_event_to_remove_variant_button() + @register_click_event_to_add_option_button() + @register_click_event_to_remove_option_button() @initialized = true initialize_tagits: -> @@ -25,16 +25,16 @@ class @Variant afterTagRemoved: @rebuild_variants }) - register_click_event_to_add_variant_button: -> - $('.js-add-variats').click( => + register_click_event_to_add_option_button: -> + $('.js-add-option').click( => @change_from_master() setTimeout( => @initialize_tagits() , 1) ) - register_click_event_to_remove_variant_button: -> - $(document).on('click', '.js-remove-variant', -> + register_click_event_to_remove_option_button: -> + $(document).on('click', '.js-remove-option', -> $(this).closest('.js-new-variants').remove() @change_to_master() ) @@ -72,7 +72,7 @@ class @Variant new_variant: -> new_id = new Date().getTime() - $('.js-add-variant2').click() + $('.js-add-variant').click() $variant = $('.js-new-variants').last() $variant.removeClass('hidden') $variant.html($variant.html().replace(/new_variant/g, new_id)) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 7d1cb812..4816960a 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -89,7 +89,7 @@ = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? - if @product.new_record? - = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-variats' + = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-option' table.table.table-striped.hidden.js-variants-table thead @@ -105,7 +105,7 @@ tbody = f.fields_for :variants do |ff| = render 'comable/admin/shared/variants_fields', f: ff - = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant2' + = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant' - if @product.persisted? table.table.table-striped From 5d639a76ee47896569d27c68f45f54f5a8178fba Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 27 Aug 2015 16:28:16 +0900 Subject: [PATCH 29/58] Change the name to save to the order item --- core/app/models/comable/order_item.rb | 2 +- core/app/models/comable/stock.rb | 9 ++++++++- core/spec/mailers/comable/order_mailer_spec.rb | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/app/models/comable/order_item.rb b/core/app/models/comable/order_item.rb index 8acf023f..eb90fadb 100644 --- a/core/app/models/comable/order_item.rb +++ b/core/app/models/comable/order_item.rb @@ -120,7 +120,7 @@ def increment_stock def current_attributes { - name: stock.name_with_sku, + name: stock.name, price: stock.price, sku: variant.sku } diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 1f4094de..6a043cd8 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -33,7 +33,6 @@ class Stock < ActiveRecord::Base # TODO: Remove the columns for compatible delegate :product, to: :variant - delegate :name, to: :product delegate :price, to: :variant delegate :sku_h_item_name, to: :product delegate :sku_v_item_name, to: :product @@ -66,6 +65,14 @@ def unstocked?(quantity: 1) !stocked?(quantity: quantity) end + def name + if variant.names.any? + "#{variant.product.name} (#{variant.names.join('/')}" + else + variant.product.name + end + end + def sku_h_choice_name variant.option_values.first.try(:name) end diff --git a/core/spec/mailers/comable/order_mailer_spec.rb b/core/spec/mailers/comable/order_mailer_spec.rb index dce531af..f8107bd1 100644 --- a/core/spec/mailers/comable/order_mailer_spec.rb +++ b/core/spec/mailers/comable/order_mailer_spec.rb @@ -24,7 +24,7 @@ end it 'renders the product name with sku' do - expect(mail.body.encoded).to include(order_item.stock.name_with_sku) + expect(mail.body.encoded).to include(order_item.name) end end end From 4d23995af686beb021317dd94c2980b740dcbf07 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 28 Aug 2015 11:27:03 +0900 Subject: [PATCH 30/58] Fix to pass the test --- core/app/models/comable/variant.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 2dcf735d..f0fc3e94 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -28,7 +28,7 @@ def names end def quantity - stock.try(:quantity) + stock.try(:quantity) || build_stock.quantity end def quantity=(quantity) From 4bdb3d7c9bf4c90cb20d0c735322c4a9be855c08 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 28 Aug 2015 13:23:55 +0900 Subject: [PATCH 31/58] Refactoring with rubocop: is_master? => master? --- backend/app/views/comable/admin/products/_form.slim | 6 +++--- backend/app/views/comable/admin/products/index.slim | 2 +- core/app/models/comable/product.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 4816960a..9113cf0a 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -72,13 +72,13 @@ hr - - if @product.new_record? || @product.is_master? + - if @product.new_record? || @product.master? = f.fields_for :variants, f.object.variants.first do |ff| = render 'comable/admin/shared/variant_form', f: ff - = ff.hidden_field :_destroy, value: !f.object.is_master? + = ff.hidden_field :_destroy, value: !f.object.master? hr - - if @product.new_record? || @product.persisted? && !@product.is_master? + - if @product.new_record? || @product.persisted? && !@product.master? fieldset .col-md-3 legend diff --git a/backend/app/views/comable/admin/products/index.slim b/backend/app/views/comable/admin/products/index.slim index 96e238d2..b5ab3880 100644 --- a/backend/app/views/comable/admin/products/index.slim +++ b/backend/app/views/comable/admin/products/index.slim @@ -85,7 +85,7 @@ - quantity = variant.quantity strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" = number_with_delimiter quantity - - if !product.is_master? + - if !product.master? span< | ( - variant.names.each do |name| diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 684f0086..27b7edcf 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -52,7 +52,7 @@ def category_path_names=(category_path_names, delimiter: Comable::Category::DEFA self.categories = Comable::Category.find_by_path_names(category_path_names, delimiter: delimiter) end - def is_master? + def master? option_types.empty? end From ed60c1ba52b00fc15971d823fa5a43c01642ea6a Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 28 Aug 2015 16:47:01 +0900 Subject: [PATCH 32/58] Fix the problem that cannot create a new product --- core/app/models/comable/variant.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index f0fc3e94..3ae62212 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -13,7 +13,7 @@ class Variant < ActiveRecord::Base validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } - before_validation :set_option_values_from_names, if: :product + before_save :set_option_values_from_names, if: :product ransack_options attribute_select: { associations: [:product, :stock, :option_values] }, ransackable_attributes: { except: :product_id } From 3c94fa46f133a0e4ca6aa92c6ace9383e88bc974 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 28 Aug 2015 16:47:16 +0900 Subject: [PATCH 33/58] Rename the wrong variable --- core/app/models/comable/variant.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 3ae62212..cbae9f94 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -43,10 +43,10 @@ def quantity=(quantity) def option_values_attributes=(attributes) if attributes.is_a? Array existed_option_values = attributes.map do |attr| - variant = Comable::OptionValue.find_by(option_type: attr[:option_type], name: attr[:name]) unless attr[:id] - next unless variant - attr[:id] = variant.id - variant + option_value = Comable::OptionValue.find_by(option_type: attr[:option_type], name: attr[:name]) unless attr[:id] + next unless option_value + attr[:id] = option_value.id + option_value end.compact self.attributes = { option_values: existed_option_values } end From 3727eaa6c895abd96d257207cb06c3bf784b7e9a Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 28 Aug 2015 16:55:10 +0900 Subject: [PATCH 34/58] Fix to pass the test in Rails 4.0 --- sample/db/samples/variants.rb | 10 +++++----- spec/factories/comable/order_items.rb | 3 ++- spec/factories/comable/products.rb | 8 +++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/sample/db/samples/variants.rb b/sample/db/samples/variants.rb index e0212ba0..0e58650b 100644 --- a/sample/db/samples/variants.rb +++ b/sample/db/samples/variants.rb @@ -5,13 +5,13 @@ fur_gloves = Comable::Product.where(name: Comable::Sample.t(:fur_gloves)).first! leather_boots = Comable::Product.where(name: Comable::Sample.t(:leather_boots)).first! -suede_dress_size = suede_dress.option_types.create!(name: Comable::Sample.t(:size)) -suede_dress_color = suede_dress.option_types.create!(name: Comable::Sample.t(:color)) +suede_dress_size = Comable::OptionType.create!(product: suede_dress, name: Comable::Sample.t(:size)) +suede_dress_color = Comable::OptionType.create!(product: suede_dress, name: Comable::Sample.t(:color)) -girly_coat_size = girly_coat.option_types.create!(name: Comable::Sample.t(:size)) -girly_coat_color = girly_coat.option_types.create!(name: Comable::Sample.t(:color)) +girly_coat_size = Comable::OptionType.create(product: girly_coat, name: Comable::Sample.t(:size)) +girly_coat_color = Comable::OptionType.create(product: girly_coat, name: Comable::Sample.t(:color)) -leather_boots_size = leather_boots.option_types.create!(name: Comable::Sample.t(:size)) +leather_boots_size = Comable::OptionType.create(product: leather_boots, name: Comable::Sample.t(:size)) default_stock_attributes = { quantity: 10 diff --git a/spec/factories/comable/order_items.rb b/spec/factories/comable/order_items.rb index 71d9e60f..490f2c25 100644 --- a/spec/factories/comable/order_items.rb +++ b/spec/factories/comable/order_items.rb @@ -10,7 +10,8 @@ trait :sku do after(:build) do |order_item| product = order_item.variant.try(:product) || build(:product) - product.option_types_attributes = [name: 'Color'] + [name: 'Size'] + create(:option_type, product: product, name: 'Color') + create(:option_type, product: product, name: 'Size') variant = order_item.variant || build(:variant) variant.product = product diff --git a/spec/factories/comable/products.rb b/spec/factories/comable/products.rb index 2c6113c0..34fe2b20 100644 --- a/spec/factories/comable/products.rb +++ b/spec/factories/comable/products.rb @@ -13,9 +13,8 @@ after(:build) do |product| product.save! - color = build(:option_type, name: 'Color') - size = build(:option_type, name: 'Size') - product.option_types = [color, size] + color = create(:option_type, product: product, name: 'Color') + size = create(:option_type, product: product, name: 'Size') color_red = color.option_values.build(name: 'Red') size_s = size.option_values.build(name: 'S') @@ -33,8 +32,7 @@ after(:build) do |product| product.save! - color = build(:option_type, name: 'Color') - product.option_types = [color] + color = create(:option_type, product: product, name: 'Color') color_red = color.option_values.build(name: 'Red') product.variants = [build(:variant, option_values: [color_red])] From 62d4bab57e53bb647e0e85fbf68cd8f3524819b9 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 31 Aug 2015 11:39:17 +0900 Subject: [PATCH 35/58] Add the tests for VariantsController --- .../comable/admin/variants_controller_spec.rb | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 backend/spec/controllers/comable/admin/variants_controller_spec.rb diff --git a/backend/spec/controllers/comable/admin/variants_controller_spec.rb b/backend/spec/controllers/comable/admin/variants_controller_spec.rb new file mode 100644 index 00000000..6a2d0a0c --- /dev/null +++ b/backend/spec/controllers/comable/admin/variants_controller_spec.rb @@ -0,0 +1,121 @@ +describe Comable::Admin::VariantsController do + sign_in_admin + + let(:comable) { controller.comable } + + let(:valid_attributes) { attributes_for(:variant) } + let(:invalid_attributes) { valid_attributes.merge(sku: 'x' * 1024) } + + let(:product) { create(:product, variants: []) } + + describe 'GET index' do + it 'assigns all variants as @variants' do + variant = create(:variant, product: product) + get :index, product_id: product.to_param + expect(assigns(:variants)).to eq([variant]) + end + end + + describe 'GET show' do + it 'assigns the requested variant as @variant' do + variant = create(:variant, product: product) + get :show, product_id: product.to_param, id: variant.to_param + expect(assigns(:variant)).to eq(variant) + end + end + + describe 'GET new' do + it 'assigns a new variant as @variant' do + get :new, product_id: product.to_param + expect(assigns(:variant)).to be_a_new(Comable::Variant) + end + end + + describe 'GET edit' do + it 'assigns the requested variant as @variant' do + variant = create(:variant, product: product) + get :edit, product_id: product.to_param, id: variant.to_param + expect(assigns(:variant)).to eq(variant) + end + end + + describe 'POST create' do + describe 'with valid params' do + it 'creates a new Comable::Variant' do + expect { post :create, product_id: product.to_param, variant: valid_attributes }.to change(Comable::Variant, :count).by(1) + end + + it 'assigns a newly created variant as @variant' do + post :create, product_id: product.to_param, variant: valid_attributes + expect(assigns(:variant)).to be_a(Comable::Variant) + expect(assigns(:variant)).to be_persisted + end + + it 'redirects to the created variant' do + post :create, product_id: product.to_param, variant: valid_attributes + expect(response).to redirect_to([comable, :admin, product, Comable::Variant.last]) + end + end + + describe 'with invalid params' do + it 'assigns a newly created but unsaved variant as @variant' do + post :create, product_id: product.to_param, variant: invalid_attributes + expect(assigns(:variant)).to be_a_new(Comable::Variant) + end + + it "re-renders the 'new' template" do + post :create, product_id: product.to_param, variant: invalid_attributes + expect(response).to render_template(:new) + end + end + end + + describe 'PUT update' do + let!(:variant) { create(:variant, product: product) } + + describe 'with valid params' do + let(:new_attributes) { { sku: "NEW: #{variant.sku}" } } + + it 'updates the requested variant' do + put :update, product_id: product.to_param, id: variant.to_param, variant: new_attributes + variant.reload + expect(variant).to have_attributes(new_attributes) + end + + it 'assigns the requested variant as @variant' do + put :update, product_id: product.to_param, id: variant.to_param, variant: valid_attributes + expect(assigns(:variant)).to eq(variant) + end + + it 'redirects to the variant' do + put :update, product_id: product.to_param, id: variant.to_param, variant: valid_attributes + expect(response).to redirect_to([comable, :admin, product, variant]) + end + end + + describe 'with invalid params' do + it 'assigns the variant as @variant' do + put :update, product_id: product.to_param, id: variant.to_param, variant: invalid_attributes + expect(assigns(:variant)).to eq(variant) + end + + it "re-renders the 'edit' template" do + put :update, product_id: product.to_param, id: variant.to_param, variant: invalid_attributes + expect(response).to render_template(:edit) + end + end + end + + describe 'DELETE destroy' do + it 'destroys the requested variant' do + variant = create(:variant, product: product) + expect { delete :destroy, product_id: product.to_param, id: variant.to_param }.to change(Comable::Variant, :count).by(-1) + end + + it 'redirects to the variants list' do + variant = create(:variant, product: product) + delete :destroy, product_id: product.to_param, id: variant.to_param + expect(response).to redirect_to([comable, :admin, product, :variants]) + end + end +end From e21c49eeebc6c07b0ef4db61e65e6606ced1ba3f Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 31 Aug 2015 17:03:33 +0900 Subject: [PATCH 36/58] Commonize `link_to_add_fields` helper method --- .../app/helpers/comable/admin/application_helper.rb | 9 +++------ backend/app/views/comable/admin/products/_form.slim | 4 ++-- .../shared/{_image_fields.slim => _images_fields.slim} | 10 +++++----- 3 files changed, 10 insertions(+), 13 deletions(-) rename backend/app/views/comable/admin/shared/{_image_fields.slim => _images_fields.slim} (58%) diff --git a/backend/app/helpers/comable/admin/application_helper.rb b/backend/app/helpers/comable/admin/application_helper.rb index 89f36088..643027a5 100644 --- a/backend/app/helpers/comable/admin/application_helper.rb +++ b/backend/app/helpers/comable/admin/application_helper.rb @@ -10,12 +10,9 @@ def gravatar_tag(email, options = {}) image_tag "//www.gravatar.com/avatar/#{hash}?default=mm", options end - def link_to_add_fields(name, f, association, options = {}) - new_object = f.object.class.reflect_on_association(association).klass.new - fields = f.fields_for(association, new_object, child_index: "new_#{association}") do |builder| - render("comable/admin/shared/#{association.to_s.singularize}_fields", ff: builder) - end - link_to(name, 'javascript:void(0)', options.merge(onclick: "add_fields(this, '#{association}', '#{escape_javascript(fields)}')")) + def link_to_add_fields(name, f, type, options = {}) + new_fields = build_fields(f, type) + link_to(name, 'javascript:void(0)', options.merge(class: "add_fields #{options[:class]}", 'data-field-type' => type, 'data-content' => "#{new_fields}")) end def button_to_remove_fields(name, options = {}) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 9113cf0a..2978a7c1 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -89,7 +89,7 @@ = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? - if @product.new_record? - = button_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-option' + = link_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-option' table.table.table-striped.hidden.js-variants-table thead @@ -105,7 +105,7 @@ tbody = f.fields_for :variants do |ff| = render 'comable/admin/shared/variants_fields', f: ff - = button_to_add_fields Comable.t('admin.add_variants'), f, :variants, class: 'hidden js-add-variant' + = link_to_add_fields nil, f, :variants, class: 'hidden js-add-variant' - if @product.persisted? table.table.table-striped diff --git a/backend/app/views/comable/admin/shared/_image_fields.slim b/backend/app/views/comable/admin/shared/_images_fields.slim similarity index 58% rename from backend/app/views/comable/admin/shared/_image_fields.slim rename to backend/app/views/comable/admin/shared/_images_fields.slim index 5d66b949..a2106ead 100644 --- a/backend/app/views/comable/admin/shared/_image_fields.slim +++ b/backend/app/views/comable/admin/shared/_images_fields.slim @@ -5,11 +5,11 @@ span< | ##{index} p - = ff.file_field :file - - if ff.object.url + = f.file_field :file + - if f.object.url .thumbnail - = image_tag ff.object.url - - if ff.object.persisted? + = image_tag f.object.url + - if f.object.persisted? label.remove - span> = ff.check_box :_destroy + span> = f.check_box :_destroy span> = Comable.t('admin.actions.destroy') From e24d5f099f59a7bfcadc0e6780fe5f8e0aa0066e Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 31 Aug 2015 17:07:56 +0900 Subject: [PATCH 37/58] Add the feature spec for Variants --- Gemfile.common | 7 +- .../comable/admin/products/variants_spec.rb | 70 +++++++++++++++++++ spec/rails_helper.rb | 35 +++++++++- spec/support/authorization_helpers.rb | 28 ++++++++ spec/support/controllers/auth_helpers.rb | 11 --- spec/support/share_db_connection.rb | 13 ++++ 6 files changed, 151 insertions(+), 13 deletions(-) create mode 100644 backend/spec/features/comable/admin/products/variants_spec.rb create mode 100644 spec/support/authorization_helpers.rb delete mode 100644 spec/support/controllers/auth_helpers.rb create mode 100644 spec/support/share_db_connection.rb diff --git a/Gemfile.common b/Gemfile.common index 952c2fa4..f6154cba 100644 --- a/Gemfile.common +++ b/Gemfile.common @@ -7,9 +7,14 @@ gem 'rspec-its', require: false gem 'rspec-example_steps', require: false gem 'shoulda-matchers', require: false gem 'factory_girl_rails', require: false -gem 'capybara', require: false gem 'generator_spec', require: false +# for feature test with capybara +gem 'capybara', require: false +gem 'phantomjs', require: false +gem 'poltergeist', require: false +gem 'database_cleaner', require: false + gem 'coveralls', require: false gem 'rubocop', require: false diff --git a/backend/spec/features/comable/admin/products/variants_spec.rb b/backend/spec/features/comable/admin/products/variants_spec.rb new file mode 100644 index 00000000..f9a24839 --- /dev/null +++ b/backend/spec/features/comable/admin/products/variants_spec.rb @@ -0,0 +1,70 @@ +feature 'Variants', js: true do + given(:option_type_color) { build(:option_type, name: 'Color') } + given(:option_type_size) { build(:option_type, name: 'Size') } + given(:option_value_red) { build(:option_value, name: 'Red') } + given(:option_value_s) { build(:option_value, name: 'S') } + given(:option_value_m) { build(:option_value, name: 'M') } + + authorization! :admin_user + + scenario 'Create a new variant' do + visit comable.new_admin_product_path + + fill_in :product_name, with: build(:product).name + + # Input a type and value for a new variant + click_link Comable.t('admin.add_variants') + fill_in_option :first, name: option_type_color.name, values: option_value_red.name + + click_on Comable.t('admin.actions.save') + + expect(page).to have_content Comable.t('successful') + expect(find_option_type(:first)).to eq(option_type_color.name) + expect(find_option_values(:first)).to include(option_value_red.name) + end + + scenario 'Create a new variants' do + visit comable.new_admin_product_path + + fill_in :product_name, with: build(:product).name + + # Input types and values for new variants + click_link Comable.t('admin.add_variants') + click_link Comable.t('admin.add_variants') + fill_in_option :first, name: option_type_color.name, values: option_value_red.name + fill_in_option :last, name: option_type_size.name, values: [option_value_s, option_value_m].map(&:name) + + click_on Comable.t('admin.actions.save') + + expect(page).to have_content Comable.t('successful') + expect(find_option_type(:first)).to eq(option_type_color.name) + expect(find_option_values(:first)).to include(option_value_red.name) + expect(find_option_type(:last)).to eq(option_type_size.name) + expect(find_option_values(:last)).to include(option_value_s.name) + expect(find_option_values(:last)).to include(option_value_m.name) + end + + private + + def fill_in_option(offset, name:, values:) + option = all('.comable-option').send(offset) + option_type = option.all('input').first + option_type.set(name) + + values = [values] unless values.is_a? Array + return if values.empty? + + script = "var option = $('.comable-option').#{offset}();" + script << values.map { |value| "option.find('.js-tagit-option-values').tagit('createTag', '#{value}');" }.join + page.execute_script script + end + + def find_option_type(offset, max = 2) + index = max.times.to_a.send(offset) + find("input#product_option_types_attributes_#{index}_name").value + end + + def find_option_values(offset) + page.evaluate_script "$('.comable-option').#{offset}().find('.js-tagit-option-values').tagit('assignedTags');" + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 986a98c6..272ccf94 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -12,6 +12,15 @@ require 'shoulda/matchers' require 'generator_spec' +# for Feature test +require 'capybara' +require 'phantomjs' +require 'phantomjs/poltergeist' +require 'database_cleaner' + +# Change Capybara javascript driver to Poltergeist (PhantomJS) +Capybara.javascript_driver = :poltergeist + # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } @@ -32,7 +41,8 @@ config.include Comable::EngineControllerTestMonkeyPatch, type: :controller config.include Devise::TestHelpers, type: :view config.include Devise::TestHelpers, type: :controller - config.extend Comable::AuthHelpers, type: :controller + config.extend Comable::AuthorizationHelpers::Controller, type: :controller + config.extend Comable::AuthorizationHelpers::Feature, type: :feature config.extend Comable::RequestHelpers, type: :request # for Rspec 3 @@ -41,4 +51,27 @@ # Omit the prefix FactoryGirl config.include FactoryGirl::Syntax::Methods + + # Support Capybara DSL for the feature test + config.include Capybara::DSL, type: :feature + + # + # DatabaeeCleaner for the asynchronous test (with :js option). + # + + # # Clean up database before test + # config.before(:suite) do + # DatabaseCleaner.clean_with(:truncation) + # end + + # Start the transaction + config.before(:example) do |example| + DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction + DatabaseCleaner.start + end + + # Clean up database by the strategy + config.after(:example) do + DatabaseCleaner.clean + end end diff --git a/spec/support/authorization_helpers.rb b/spec/support/authorization_helpers.rb new file mode 100644 index 00000000..92f33102 --- /dev/null +++ b/spec/support/authorization_helpers.rb @@ -0,0 +1,28 @@ +module Comable + module AuthorizationHelpers + module Controller + def sign_in_admin(options = {}) + before do + @request.env['devise.mapping'] = Devise.mappings[:admin_user] + admin = create(:user, :admin, options) + sign_in :admin_user, admin + end + end + end + + module Feature + class << self + def extended(base) + base.send(:include, Warden::Test::Helpers) + end + end + + def authorization!(scope = :user) + before do + user = (scope.to_sym == :admin_user) ? create(:user, :admin) : create(:user) + login_as(user, scope: scope) + end + end + end + end +end diff --git a/spec/support/controllers/auth_helpers.rb b/spec/support/controllers/auth_helpers.rb deleted file mode 100644 index f6ecb938..00000000 --- a/spec/support/controllers/auth_helpers.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Comable - module AuthHelpers - def sign_in_admin(options = {}) - before do - @request.env['devise.mapping'] = Devise.mappings[:user] - admin = create(:user, :admin, options) - sign_in :user, admin - end - end - end -end diff --git a/spec/support/share_db_connection.rb b/spec/support/share_db_connection.rb new file mode 100644 index 00000000..c115036e --- /dev/null +++ b/spec/support/share_db_connection.rb @@ -0,0 +1,13 @@ +# from https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara#capybara-and-poltergeist +class ActiveRecord::Base + mattr_accessor :shared_connection + @@shared_connection = nil + + def self.connection + @@shared_connection || retrieve_connection + end +end + +# Forces all threads to share the same connection. This works on +# Capybara because it starts the web server in a thread. +ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection From 94b276018832bc498c37f897faad41199362d84f Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 31 Aug 2015 17:24:50 +0900 Subject: [PATCH 38/58] Ignore `share_db_connection.rb` for rubocop --- .rubocop.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.rubocop.yml b/.rubocop.yml index 946ba8ac..b825b772 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -54,4 +54,5 @@ AllCops: - vendor/**/* - spec/dummy/db/schema.rb - spec/dummy/tmp/**/* + - spec/support/share_db_connection.rb - gemfiles/**/* From b20d68e0e20bee2fe3a287282ed54c615270d830 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 3 Sep 2015 11:02:04 +0900 Subject: [PATCH 39/58] Fix the transaction seetting for test --- spec/rails_helper.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 272ccf94..b91cb0ec 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -33,11 +33,7 @@ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" - # If you're not using ActiveRecord, or you'd prefer not to run each of your - # examples within a transaction, remove the following line or assign false - # instead of true. - config.use_transactional_fixtures = true - + # Modules config.include Comable::EngineControllerTestMonkeyPatch, type: :controller config.include Devise::TestHelpers, type: :view config.include Devise::TestHelpers, type: :controller @@ -58,6 +54,7 @@ # # DatabaeeCleaner for the asynchronous test (with :js option). # + config.use_transactional_fixtures = false # # Clean up database before test # config.before(:suite) do From 8c936ad9e60ae21297e36030a207ad3673fe6bf3 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Thu, 3 Sep 2015 14:02:56 +0900 Subject: [PATCH 40/58] Add `Variant#options=` method --- core/app/models/comable/variant.rb | 8 ++++++++ core/spec/models/comable/variant_spec.rb | 26 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index cbae9f94..de6011e2 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -39,6 +39,14 @@ def quantity=(quantity) end end + def options=(options) + options = JSON.parse(options) if options.is_a? String + self.option_values_attributes = options.map do |option| + hash = option.symbolize_keys + { name: hash[:value], option_type_attributes: { name: hash[:name] } } + end + end + # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 def option_values_attributes=(attributes) if attributes.is_a? Array diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb index ee5d5cd7..2d83b08a 100644 --- a/core/spec/models/comable/variant_spec.rb +++ b/core/spec/models/comable/variant_spec.rb @@ -7,4 +7,30 @@ it { is_expected.to validate_presence_of(:price) } it { is_expected.to validate_numericality_of(:price).is_greater_than_or_equal_to(0) } it { is_expected.to validate_length_of(:sku).is_at_most(255) } + + describe '#options=' do + it 'should sets OptionValue' do + option_value_name = 'Red' + subject.options = [value: option_value_name] + expect(subject.option_values.first.name).to eq(option_value_name) + end + + it 'should sets OptionValue by JSON' do + option_value_name = 'Red' + subject.options = [value: option_value_name].to_json + expect(subject.option_values.first.name).to eq(option_value_name) + end + + it 'should sets OptionType' do + option_type_name = 'Color' + subject.options = [name: option_type_name, value: 'Red'] + expect(subject.option_values.first.option_type.name).to eq(option_type_name) + end + + it 'should sets OptionType by JSON' do + option_type_name = 'Color' + subject.options = [name: option_type_name, value: 'Red'].to_json + expect(subject.option_values.first.option_type.name).to eq(option_type_name) + end + end end From 420d3b95091d146002c486e18195418e2db68cbb Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 4 Sep 2015 10:05:13 +0900 Subject: [PATCH 41/58] Temporary improvement --- .../javascripts/comable/admin/variants.coffee | 22 ++++++++++--------- .../comable/admin/products_controller.rb | 2 +- .../views/comable/admin/products/_form.slim | 4 ++-- .../admin/shared/_option_type_fields.slim | 13 +++++++++++ .../admin/shared/_variants_fields.slim | 8 +++---- .../comable/admin/products/variants_spec.rb | 5 +++-- core/app/models/comable/option_type.rb | 4 ---- core/app/models/comable/option_value.rb | 4 ++-- core/app/models/comable/product.rb | 22 +++++++++++++++++-- core/app/models/comable/variant.rb | 21 +++++++++--------- ...50823072622_create_comable_option_types.rb | 3 +-- core/spec/models/comable/variant_spec.rb | 15 +++++++++++++ 12 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 backend/app/views/comable/admin/shared/_option_type_fields.slim diff --git a/backend/app/assets/javascripts/comable/admin/variants.coffee b/backend/app/assets/javascripts/comable/admin/variants.coffee index 88f5ddaf..34261ce7 100644 --- a/backend/app/assets/javascripts/comable/admin/variants.coffee +++ b/backend/app/assets/javascripts/comable/admin/variants.coffee @@ -46,23 +46,25 @@ class @Variant build_variants: -> _this = @ - option_types = [] + options = [] $('.js-tagit-option-values').each( -> return unless $(this).hasClass('tagit') - option_values = $(this).tagit('assignedTags') - option_types.push(option_values) + name = $(this).closest('.comable-option').find('input:first-child').val() + values = $(this).tagit('assignedTags') + option = jQuery.map(values, (value) -> { name: name, value: value }) + options.push(option) ) - option_values_for_variants = _product(_compact(option_types)) - option_values_for_variants.forEach((option_values_for_variant) -> - _this.build_variant(option_values_for_variant) + options_for_variants = _product(_compact(options)) + options_for_variants.forEach((options_for_variant) -> + _this.build_variant(options_for_variant) ) - build_variant: (option_values) -> + build_variant: (options) -> $variant = @new_variant() - $variant.find('[data-name="names"] > input').val(option_values) - option_values.forEach((option_value) -> - $variant.find('[data-name="names"]').append('' + option_value + ' ') + $variant.find('[data-name="options"] > input').val(JSON.stringify(options)) + options.forEach((option) -> + $variant.find('[data-name="options"]').append('' + option.value + ' ') ) $('.js-variants-table').find('tbody').append($variant) diff --git a/backend/app/controllers/comable/admin/products_controller.rb b/backend/app/controllers/comable/admin/products_controller.rb index a19cfaca..0f91c122 100644 --- a/backend/app/controllers/comable/admin/products_controller.rb +++ b/backend/app/controllers/comable/admin/products_controller.rb @@ -74,7 +74,7 @@ def product_params :published_at, category_path_names: [], images_attributes: [:id, :file, :_destroy], - variants_attributes: [:id, :price, :sku, :names, :quantity, :_destroy], + variants_attributes: [:id, :price, :sku, :options, :quantity, :_destroy], option_types_attributes: [:id, :name, { values: [] }] ) end diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 2978a7c1..19a21e44 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -85,13 +85,13 @@ = Comable.t('admin.variants') .col-md-9 - = f.fields_for :option_types do |ff| + = f.fields_for :option_types, f.object.option_types do |ff| = render 'comable/admin/shared/option_types_fields', f: ff, readonly: @product.persisted? - if @product.new_record? = link_to_add_fields Comable.t('admin.add_variants'), f, :option_types, class: 'btn btn-link js-add-option' - table.table.table-striped.hidden.js-variants-table + table.table.table-striped.js-variants-table class="#{'hidden' if @product.master?}" thead th th diff --git a/backend/app/views/comable/admin/shared/_option_type_fields.slim b/backend/app/views/comable/admin/shared/_option_type_fields.slim new file mode 100644 index 00000000..02cfd3f0 --- /dev/null +++ b/backend/app/views/comable/admin/shared/_option_type_fields.slim @@ -0,0 +1,13 @@ +- readonly ||= false + +.comable-option + .form-group + .row + .col-xs-4 + = text_field_tag :option_type, nil, placeholder: Comable.t('admin.size'), class: 'comable-option-name', disabled: readonly + + .col-xs-8 + ul.js-tagit-option-values.comable-option-values data-index="#{f.index}" class="#{'tagit-readonly' if readonly}" + - f.object.values.each do |value| + li + = value diff --git a/backend/app/views/comable/admin/shared/_variants_fields.slim b/backend/app/views/comable/admin/shared/_variants_fields.slim index e763140e..2ed93574 100644 --- a/backend/app/views/comable/admin/shared/_variants_fields.slim +++ b/backend/app/views/comable/admin/shared/_variants_fields.slim @@ -1,11 +1,11 @@ tr.js-new-variants td = f.check_box :_destroy, { checked: true }, *({ checked_value: 0, unchecked_value: 1 }.values) - td data-name="names" - = f.hidden_field :names, value: f.object.names.join(',') - - f.object.names.each do |name| + td data-name="options" + = f.hidden_field :options, value: f.object.options.to_json + - f.object.options.each do |option| span.comable-variant-name - = name + = option[:value] td data-name="sku" = f.text_field :sku td data-name="price" diff --git a/backend/spec/features/comable/admin/products/variants_spec.rb b/backend/spec/features/comable/admin/products/variants_spec.rb index f9a24839..43c241c5 100644 --- a/backend/spec/features/comable/admin/products/variants_spec.rb +++ b/backend/spec/features/comable/admin/products/variants_spec.rb @@ -19,6 +19,7 @@ click_on Comable.t('admin.actions.save') expect(page).to have_content Comable.t('successful') + expect(find_option_type(:first)).to eq(option_type_color.name) expect(find_option_values(:first)).to include(option_value_red.name) end @@ -59,8 +60,8 @@ def fill_in_option(offset, name:, values:) page.execute_script script end - def find_option_type(offset, max = 2) - index = max.times.to_a.send(offset) + def find_option_type(offset) + index = all('.comable-option-values').send(offset)['data-index'] find("input#product_option_types_attributes_#{index}_name").value end diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index a6adadc8..ef9deb0f 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -3,13 +3,9 @@ class OptionType < ActiveRecord::Base include Comable::Ransackable has_many :option_values, class_name: Comable::OptionValue.name - belongs_to :product, class_name: Comable::Product.name, inverse_of: :option_types, autosave: true - validates :product, presence: { message: Comable.t('admin.is_not_exists') } validates :name, presence: true, length: { maximum: 255 } - default_scope { order(:id) } - ransack_options ransackable_attributes: { only: :name } def values diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index f9d6a684..58d99ceb 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -5,9 +5,9 @@ class OptionValue < ActiveRecord::Base belongs_to :option_type, class_name: Comable::OptionType.name has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values - validates :name, presence: true, length: { maximum: 255 } + accepts_nested_attributes_for :option_type - default_scope { order(:option_type_id) } + validates :name, presence: true, length: { maximum: 255 } ransack_options ransackable_attributes: { only: :name } end diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 27b7edcf..9e10c3f4 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -9,12 +9,10 @@ class Product < ActiveRecord::Base has_many :variants, class_name: Comable::Variant.name, inverse_of: :product, dependent: :destroy has_many :images, class_name: Comable::Image.name, dependent: :destroy - has_many :option_types, class_name: Comable::OptionType.name, inverse_of: :product, dependent: :destroy has_and_belongs_to_many :categories, class_name: Comable::Category.name, join_table: :comable_products_categories accepts_nested_attributes_for :variants, allow_destroy: true accepts_nested_attributes_for :images, allow_destroy: true - accepts_nested_attributes_for :option_types, allow_destroy: true validates :name, presence: true, length: { maximum: 255 } @@ -86,6 +84,25 @@ def stocks=(stocks) stocks.map { |stock| variants.build(stock: stock) } end + def build_option_type + Comable::OptionType.new + end + + def option_types + option_types = variants.map {|variant| variant.option_values.map(&:option_type) }.flatten.uniq + if option_types.any? + option_types.singleton_class.send(:define_method, :build, -> { Comable::OptionType.new }) + option_types + else + option_values = Comable::OptionValue.joins(:variants).merge(variants) + Comable::OptionType.joins(:option_values).merge(option_values).uniq + end + end + + def option_types_attributes=(option_types_attributes) + @option_types_attributes = option_types_attributes + end + # # Deprecated methods # @@ -94,5 +111,6 @@ def stocks=(stocks) deprecate :sku_v_item_name, deprecator: Comable::Deprecator.instance deprecate :code, deprecator: Comable::Deprecator.instance deprecate :code=, deprecator: Comable::Deprecator.instance + deprecate :option_types, deprecator: Comable::Deprecator.instance end end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index de6011e2..7e587650 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -4,7 +4,7 @@ class Variant < ActiveRecord::Base belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true - has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values + has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values, primary_key: :name accepts_nested_attributes_for :option_values, allow_destroy: true accepts_nested_attributes_for :stock @@ -39,24 +39,25 @@ def quantity=(quantity) end end + def options + option_values.map do |option_value| + { name: option_value.option_type.try(:name), value: option_value.name } + end + end + def options=(options) options = JSON.parse(options) if options.is_a? String self.option_values_attributes = options.map do |option| hash = option.symbolize_keys - { name: hash[:value], option_type_attributes: { name: hash[:name] } } + option_type = Comable::OptionType.where(name: hash[:name]).first_or_initialize(&:save!) + { name: hash[:value], option_type: option_type } end end # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 def option_values_attributes=(attributes) - if attributes.is_a? Array - existed_option_values = attributes.map do |attr| - option_value = Comable::OptionValue.find_by(option_type: attr[:option_type], name: attr[:name]) unless attr[:id] - next unless option_value - attr[:id] = option_value.id - option_value - end.compact - self.attributes = { option_values: existed_option_values } + attributes.each do |attr| + option_values.where(option_type: attr[:option_type], name: attr[:name]).first_or_initialize(&:save!) end super end diff --git a/core/db/migrate/20150823072622_create_comable_option_types.rb b/core/db/migrate/20150823072622_create_comable_option_types.rb index 208d8aa4..20b2395c 100644 --- a/core/db/migrate/20150823072622_create_comable_option_types.rb +++ b/core/db/migrate/20150823072622_create_comable_option_types.rb @@ -1,11 +1,10 @@ class CreateComableOptionTypes < ActiveRecord::Migration def change create_table :comable_option_types do |t| - t.references :product, null: false t.string :name, null: false t.timestamps null: false end - add_index :comable_option_types, [:product_id, :name], unique: true + add_index :comable_option_types, :name, unique: true end end diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb index 2d83b08a..ef895745 100644 --- a/core/spec/models/comable/variant_spec.rb +++ b/core/spec/models/comable/variant_spec.rb @@ -8,6 +8,21 @@ it { is_expected.to validate_numericality_of(:price).is_greater_than_or_equal_to(0) } it { is_expected.to validate_length_of(:sku).is_at_most(255) } + describe '#options' do + it 'should returns name of OptionValue' do + option_value = build(:option_value) + subject.option_values = [option_value] + expect(subject.options.first[:value]).to eq(option_value.name) + end + + it 'should returns name of OptionType' do + option_type = build(:option_type) + option_value = build(:option_value, option_type: option_type) + subject.option_values = [option_value] + expect(subject.options.first[:name]).to eq(option_type.name) + end + end + describe '#options=' do it 'should sets OptionValue' do option_value_name = 'Red' From 200f04e7522a56638dc150c2c12fc4575420af74 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 4 Sep 2015 11:19:07 +0900 Subject: [PATCH 42/58] Change the primary keys of OptionType and OptionValue --- core/app/models/comable/option_type.rb | 4 +++- core/app/models/comable/option_value.rb | 6 ++++-- core/app/models/comable/variant.rb | 15 ++++----------- .../20150823072622_create_comable_option_types.rb | 2 +- ...20150823072955_create_comable_option_values.rb | 6 +++--- ...73250_create_comable_variants_option_values.rb | 2 +- core/spec/models/comable/variant_spec.rb | 4 ++-- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index ef9deb0f..55b9d8fb 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -1,8 +1,10 @@ module Comable class OptionType < ActiveRecord::Base + self.primary_key = :name + include Comable::Ransackable - has_many :option_values, class_name: Comable::OptionValue.name + has_many :option_values, class_name: Comable::OptionValue.name, foreign_key: :option_type_name validates :name, presence: true, length: { maximum: 255 } diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index 58d99ceb..49482cf1 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -1,9 +1,11 @@ module Comable class OptionValue < ActiveRecord::Base + self.primary_key = :name + include Comable::Ransackable - belongs_to :option_type, class_name: Comable::OptionType.name - has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values + belongs_to :option_type, class_name: Comable::OptionType.name, foreign_key: :option_type_name + has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values, foreign_key: :option_value_name accepts_nested_attributes_for :option_type diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 7e587650..0af49842 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -4,7 +4,8 @@ class Variant < ActiveRecord::Base belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true - has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values, primary_key: :name + + has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values, association_foreign_key: :option_value_name accepts_nested_attributes_for :option_values, allow_destroy: true accepts_nested_attributes_for :stock @@ -47,19 +48,11 @@ def options def options=(options) options = JSON.parse(options) if options.is_a? String - self.option_values_attributes = options.map do |option| + self.option_values = options.map do |option| hash = option.symbolize_keys option_type = Comable::OptionType.where(name: hash[:name]).first_or_initialize(&:save!) - { name: hash[:value], option_type: option_type } - end - end - - # refs http://stackoverflow.com/questions/8776724/how-do-i-create-a-new-object-referencing-an-existing-nested-attribute/21215218#21215218 - def option_values_attributes=(attributes) - attributes.each do |attr| - option_values.where(option_type: attr[:option_type], name: attr[:name]).first_or_initialize(&:save!) + Comable::OptionValue.where(name: hash[:value], option_type: option_type).first_or_initialize(&:save!) end - super end private diff --git a/core/db/migrate/20150823072622_create_comable_option_types.rb b/core/db/migrate/20150823072622_create_comable_option_types.rb index 20b2395c..ce85712d 100644 --- a/core/db/migrate/20150823072622_create_comable_option_types.rb +++ b/core/db/migrate/20150823072622_create_comable_option_types.rb @@ -1,6 +1,6 @@ class CreateComableOptionTypes < ActiveRecord::Migration def change - create_table :comable_option_types do |t| + create_table :comable_option_types, id: false do |t| t.string :name, null: false t.timestamps null: false end diff --git a/core/db/migrate/20150823072955_create_comable_option_values.rb b/core/db/migrate/20150823072955_create_comable_option_values.rb index ca628d1b..9d4182d7 100644 --- a/core/db/migrate/20150823072955_create_comable_option_values.rb +++ b/core/db/migrate/20150823072955_create_comable_option_values.rb @@ -1,11 +1,11 @@ class CreateComableOptionValues < ActiveRecord::Migration def change - create_table :comable_option_values do |t| - t.references :option_type, null: false + create_table :comable_option_values, id: false do |t| + t.string :option_type_name, null: false t.string :name, null: false t.timestamps null: false end - add_index :comable_option_values, [:option_type_id, :name], unique: true + add_index :comable_option_values, [:option_type_name, :name], unique: true end end diff --git a/core/db/migrate/20150823073250_create_comable_variants_option_values.rb b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb index cfcf9df6..ecdf0b7e 100644 --- a/core/db/migrate/20150823073250_create_comable_variants_option_values.rb +++ b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb @@ -2,7 +2,7 @@ class CreateComableVariantsOptionValues < ActiveRecord::Migration def change create_table :comable_variants_option_values do |t| t.references :variant, null: false, index: true - t.references :option_value, null: false, index: true + t.string :option_value_name, null: false, index: true end end end diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb index ef895745..e3452852 100644 --- a/core/spec/models/comable/variant_spec.rb +++ b/core/spec/models/comable/variant_spec.rb @@ -26,13 +26,13 @@ describe '#options=' do it 'should sets OptionValue' do option_value_name = 'Red' - subject.options = [value: option_value_name] + subject.options = [name: 'Color', value: option_value_name] expect(subject.option_values.first.name).to eq(option_value_name) end it 'should sets OptionValue by JSON' do option_value_name = 'Red' - subject.options = [value: option_value_name].to_json + subject.options = [name: 'Color', value: option_value_name].to_json expect(subject.option_values.first.name).to eq(option_value_name) end From 4578c0cc9858eb6a336714330e20509de5d14310 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 4 Sep 2015 20:34:06 +0900 Subject: [PATCH 43/58] Fix to pass the tests --- core/spec/models/comable/option_type_spec.rb | 1 - sample/db/samples/variants.rb | 26 +++++++------------- spec/factories/comable/order_items.rb | 5 +--- spec/factories/comable/products.rb | 22 +++-------------- 4 files changed, 14 insertions(+), 40 deletions(-) diff --git a/core/spec/models/comable/option_type_spec.rb b/core/spec/models/comable/option_type_spec.rb index 3b55e8d3..feeb9d1d 100644 --- a/core/spec/models/comable/option_type_spec.rb +++ b/core/spec/models/comable/option_type_spec.rb @@ -1,6 +1,5 @@ describe Comable::OptionType do it { is_expected.to have_many(:option_values).class_name(Comable::OptionValue.name) } - it { is_expected.to belong_to(:product).class_name(Comable::Product.name) } it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_length_of(:name).is_at_most(255) } diff --git a/sample/db/samples/variants.rb b/sample/db/samples/variants.rb index 0e58650b..7a963c38 100644 --- a/sample/db/samples/variants.rb +++ b/sample/db/samples/variants.rb @@ -5,14 +5,6 @@ fur_gloves = Comable::Product.where(name: Comable::Sample.t(:fur_gloves)).first! leather_boots = Comable::Product.where(name: Comable::Sample.t(:leather_boots)).first! -suede_dress_size = Comable::OptionType.create!(product: suede_dress, name: Comable::Sample.t(:size)) -suede_dress_color = Comable::OptionType.create!(product: suede_dress, name: Comable::Sample.t(:color)) - -girly_coat_size = Comable::OptionType.create(product: girly_coat, name: Comable::Sample.t(:size)) -girly_coat_color = Comable::OptionType.create(product: girly_coat, name: Comable::Sample.t(:color)) - -leather_boots_size = Comable::OptionType.create(product: leather_boots, name: Comable::Sample.t(:size)) - default_stock_attributes = { quantity: 10 } @@ -20,37 +12,37 @@ variants_attributes = [ { product: suede_dress, - option_values_attributes: [option_type: suede_dress_size, name: 'S'] + [option_type: suede_dress_color, name: Comable::Sample.t(:navy)], + options: [name: Comable::Sample.t(:size), value: 'S'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:navy)], stock_attributes: default_stock_attributes, sku: 'SUEDE-DRESS-SN' }, { product: suede_dress, - option_values_attributes: [option_type: suede_dress_size, name: 'M'] + [option_type: suede_dress_color, name: Comable::Sample.t(:navy)], + options: [name: Comable::Sample.t(:size), value: 'M'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:navy)], stock_attributes: default_stock_attributes, sku: 'SUEDE-DRESS-MN' }, { product: girly_coat, - option_values_attributes: [option_type: girly_coat_size, name: 'S'] + [option_type: girly_coat_color, name: Comable::Sample.t(:beige)], + options: [name: Comable::Sample.t(:size), value: 'S'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:beige)], stock_attributes: default_stock_attributes, sku: 'GIRLY-COAT-SB' }, { product: girly_coat, - option_values_attributes: [option_type: girly_coat_size, name: 'M'] + [option_type: girly_coat_color, name: Comable::Sample.t(:beige)], + options: [name: Comable::Sample.t(:size), value: 'M'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:beige)], stock_attributes: default_stock_attributes, sku: 'GIRLY-COAT-MB' }, { product: girly_coat, - option_values_attributes: [option_type: girly_coat_size, name: 'S'] + [option_type: girly_coat_color, name: Comable::Sample.t(:navy)], + options: [name: Comable::Sample.t(:size), value: 'S'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:navy)], stock_attributes: default_stock_attributes, sku: 'GIRLY-COAT-SN' }, { product: girly_coat, - option_values_attributes: [option_type: girly_coat_size, name: 'M'] + [option_type: girly_coat_color, name: Comable::Sample.t(:navy)], + options: [name: Comable::Sample.t(:size), value: 'M'] + [name: Comable::Sample.t(:color), value: Comable::Sample.t(:navy)], stock_attributes: default_stock_attributes, sku: 'GIRLY-COAT-MN' }, @@ -61,19 +53,19 @@ }, { product: leather_boots, - option_values_attributes: [option_type: leather_boots_size, name: 'S'], + options: [name: Comable::Sample.t(:size), value: 'S'], stock_attributes: { quantity: 0 }, sku: 'LEATHER-BOOTS-S' }, { product: leather_boots, - option_values_attributes: [option_type: leather_boots_size, name: 'M'], + options: [name: Comable::Sample.t(:size), value: 'M'], stock_attributes: { quantity: 1 }, sku: 'LEATHER-BOOTS-M' }, { product: leather_boots, - option_values_attributes: [option_type: leather_boots_size, name: 'L'], + options: [name: Comable::Sample.t(:size), value: 'L'], stock_attributes: default_stock_attributes, sku: 'LEATHER-BOOTS-L' } diff --git a/spec/factories/comable/order_items.rb b/spec/factories/comable/order_items.rb index 490f2c25..c4f3474f 100644 --- a/spec/factories/comable/order_items.rb +++ b/spec/factories/comable/order_items.rb @@ -10,13 +10,10 @@ trait :sku do after(:build) do |order_item| product = order_item.variant.try(:product) || build(:product) - create(:option_type, product: product, name: 'Color') - create(:option_type, product: product, name: 'Size') - variant = order_item.variant || build(:variant) variant.product = product variant.stock = build(:stock, quantity: order_item.quantity) if variant.stock - variant.option_values_attributes = [option_type: product.option_types.first, name: 'Red'] + [option_type: product.option_types.first, name: 'S'] + variant.options = [name: 'Color', value: 'Red'] + [name: 'Size', value: 'S'] order_item.variant = variant end diff --git a/spec/factories/comable/products.rb b/spec/factories/comable/products.rb index 34fe2b20..8af4ab95 100644 --- a/spec/factories/comable/products.rb +++ b/spec/factories/comable/products.rb @@ -11,31 +11,17 @@ trait :sku do after(:build) do |product| - product.save! - - color = create(:option_type, product: product, name: 'Color') - size = create(:option_type, product: product, name: 'Size') - - color_red = color.option_values.build(name: 'Red') - size_s = size.option_values.build(name: 'S') - size_m = size.option_values.build(name: 'M') - size_l = size.option_values.build(name: 'L') product.variants = [ - build(:variant, option_values: [color_red, size_s], stock: build(:stock, :stocked)), - build(:variant, option_values: [color_red, size_m], stock: build(:stock, :stocked)), - build(:variant, option_values: [color_red, size_l], stock: build(:stock, :stocked)) + build(:variant, stock: build(:stock, :stocked), options: [name: 'Color', value: 'Red'] + [name: 'Size', value: 'S']), + build(:variant, stock: build(:stock, :stocked), options: [name: 'Color', value: 'Red'] + [name: 'Size', value: 'M']), + build(:variant, stock: build(:stock, :stocked), options: [name: 'Color', value: 'Red'] + [name: 'Size', value: 'L']) ] end end trait :sku_h do after(:build) do |product| - product.save! - - color = create(:option_type, product: product, name: 'Color') - - color_red = color.option_values.build(name: 'Red') - product.variants = [build(:variant, option_values: [color_red])] + product.variants = [build(:variant, options: [name: 'Color', value: 'Red'])] end end end From 1011edcd2733d4e4a671c5e198447005b0450de2 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 4 Sep 2015 21:04:50 +0900 Subject: [PATCH 44/58] Refactoring with rubocop --- core/app/models/comable/product.rb | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 9e10c3f4..20d0d17b 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -89,18 +89,12 @@ def build_option_type end def option_types - option_types = variants.map {|variant| variant.option_values.map(&:option_type) }.flatten.uniq - if option_types.any? - option_types.singleton_class.send(:define_method, :build, -> { Comable::OptionType.new }) - option_types - else - option_values = Comable::OptionValue.joins(:variants).merge(variants) - Comable::OptionType.joins(:option_values).merge(option_values).uniq - end + option_types = variants.map { |variant| variant.option_values.map(&:option_type) }.flatten.uniq + option_types.singleton_class.send(:define_method, :build, -> { Comable::OptionType.new }) + option_types end - def option_types_attributes=(option_types_attributes) - @option_types_attributes = option_types_attributes + def option_types_attributes=(_option_types_attributes) end # @@ -112,5 +106,6 @@ def option_types_attributes=(option_types_attributes) deprecate :code, deprecator: Comable::Deprecator.instance deprecate :code=, deprecator: Comable::Deprecator.instance deprecate :option_types, deprecator: Comable::Deprecator.instance + deprecate :option_types_attributes=, deprecator: Comable::Deprecator.instance end end From 7d9a348d08ea1450f2107ae5d8d67ece3e63d612 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 15:46:44 +0900 Subject: [PATCH 45/58] Change the settigns for DatabaseCleaner --- spec/rails_helper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index b91cb0ec..eb952eb0 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -56,19 +56,19 @@ # config.use_transactional_fixtures = false - # # Clean up database before test - # config.before(:suite) do - # DatabaseCleaner.clean_with(:truncation) - # end + # Clean up database before test + config.before(:suite) do + DatabaseCleaner.clean_with(:truncation) + end # Start the transaction - config.before(:example) do |example| + config.before(:each) do |example| DatabaseCleaner.strategy = example.metadata[:js] ? :truncation : :transaction DatabaseCleaner.start end # Clean up database by the strategy - config.after(:example) do + config.after(:each) do DatabaseCleaner.clean end end From 6a5dd286153d17e1ca35958bc38e45ee61f60838 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 15:47:29 +0900 Subject: [PATCH 46/58] Support the variants in frontend --- core/app/models/comable/product.rb | 1 - core/app/models/comable/stock.rb | 2 +- core/app/models/comable/variant.rb | 4 ---- .../app/models/concerns/comable/cart_owner.rb | 2 ++ .../app/models/concerns/comable/sku_choice.rb | 10 +------- .../controllers/comable/carts_controller.rb | 5 ++++ frontend/app/views/comable/products/show.slim | 7 ++++-- frontend/spec/features/cart_spec.rb | 23 +++++++++++++------ 8 files changed, 30 insertions(+), 24 deletions(-) diff --git a/core/app/models/comable/product.rb b/core/app/models/comable/product.rb index 20d0d17b..e7ab2c04 100644 --- a/core/app/models/comable/product.rb +++ b/core/app/models/comable/product.rb @@ -105,7 +105,6 @@ def option_types_attributes=(_option_types_attributes) deprecate :sku_v_item_name, deprecator: Comable::Deprecator.instance deprecate :code, deprecator: Comable::Deprecator.instance deprecate :code=, deprecator: Comable::Deprecator.instance - deprecate :option_types, deprecator: Comable::Deprecator.instance deprecate :option_types_attributes=, deprecator: Comable::Deprecator.instance end end diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 6a043cd8..879bcb80 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -67,7 +67,7 @@ def unstocked?(quantity: 1) def name if variant.names.any? - "#{variant.product.name} (#{variant.names.join('/')}" + "#{variant.product.name} (#{variant.names.join('/')})" else variant.product.name end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 0af49842..2d0d187f 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -20,10 +20,6 @@ class Variant < ActiveRecord::Base attr_writer :names - def name - names.join(' x ') - end - def names @names ? @names : option_values.map(&:name) end diff --git a/core/app/models/concerns/comable/cart_owner.rb b/core/app/models/concerns/comable/cart_owner.rb index 1287a35b..4518bbe5 100644 --- a/core/app/models/concerns/comable/cart_owner.rb +++ b/core/app/models/concerns/comable/cart_owner.rb @@ -57,6 +57,8 @@ def process_cart_item(obj) yield obj.stocks.first when Comable::Stock yield obj + when Comable::Variant + yield obj.stock when Array obj.map { |item| yield item } else diff --git a/core/app/models/concerns/comable/sku_choice.rb b/core/app/models/concerns/comable/sku_choice.rb index a11b6b7a..33230d56 100644 --- a/core/app/models/concerns/comable/sku_choice.rb +++ b/core/app/models/concerns/comable/sku_choice.rb @@ -5,15 +5,7 @@ module Comable # module SkuChoice def name_with_sku - return name unless sku? - name + "(#{sku_name})" - end - - def sku_name - return unless sku? - sku_name = sku_h_choice_name - sku_name += '/' + sku_v_choice_name if sku_v_choice_name.present? - sku_name + name end end end diff --git a/frontend/app/controllers/comable/carts_controller.rb b/frontend/app/controllers/comable/carts_controller.rb index ec2cdbcd..1e857580 100644 --- a/frontend/app/controllers/comable/carts_controller.rb +++ b/frontend/app/controllers/comable/carts_controller.rb @@ -51,12 +51,17 @@ def ensure_found_cart_item def find_cart_item cart_item = Comable::Stock.where(id: params[:stock_id]).first + cart_item ||= Comable::Variant.joins(:option_values).where(where_option_value).first cart_item ||= Comable::Product.where(id: params[:product_id]).first return unless cart_item return if cart_item.is_a?(Comable::Product) && cart_item.sku? cart_item end + def where_option_value + { Comable::OptionValue.table_name => { name: params[:option_value_names] } } + end + def cart_item_options options = {} options.update(quantity: params[:quantity].to_i) if params[:quantity] diff --git a/frontend/app/views/comable/products/show.slim b/frontend/app/views/comable/products/show.slim index 671a2c6b..c1da18c1 100644 --- a/frontend/app/views/comable/products/show.slim +++ b/frontend/app/views/comable/products/show.slim @@ -33,8 +33,11 @@ = form_tag comable.add_cart_path do - if @product.sku? - .sku - = sku_table @product + - @product.variants.map(&:option_values).flatten.uniq.group_by(&:option_type_name).each do |key, option_values| + .sku + label + = key + = select_tag 'option_value_names[]', options_for_select(option_values.map(&:name)) - if @product.stocked? .add_cart.form-inline.form-group diff --git a/frontend/spec/features/cart_spec.rb b/frontend/spec/features/cart_spec.rb index 30bdb69b..0b38157a 100644 --- a/frontend/spec/features/cart_spec.rb +++ b/frontend/spec/features/cart_spec.rb @@ -20,8 +20,7 @@ When '商品をカートに投入して' do visit comable.product_path(subject) - choose subject.stocks.first.code if subject.sku? - click_button Comable.t('add_to_cart') + add_to_cart subject expect(page).to have_content Comable.t('carts.added') end @@ -101,9 +100,7 @@ When '商品をカートに投入して' do visit comable.product_path(subject) - choose subject.stocks.first.code if subject.sku? - select quantity, from: 'quantity' - click_button Comable.t('add_to_cart') + add_to_cart subject, quantity: quantity expect(page).to have_content Comable.t('carts.added') end @@ -126,8 +123,7 @@ When '商品をカートに投入して' do visit comable.product_path(subject) - choose subject.stocks.first.code if subject.sku? - click_button Comable.t('add_to_cart') + add_to_cart subject expect(page).to have_content Comable.t('carts.added') end @@ -158,4 +154,17 @@ it_behaves_like '商品を数量指定でカート投入できること' it_behaves_like '商品の数量変更ができること' end + + private + + def add_to_cart(product, quantity: nil) + if product.sku? + variant = product.variants.first + variant.option_values.each do |option_value| + select option_value.name + end + end + select quantity, from: 'quantity' if quantity + click_button Comable.t('add_to_cart') + end end From 0bd5fa878cae4e4477ed75d761d23ccc1b580ffd Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 16:05:31 +0900 Subject: [PATCH 47/58] Refactoring with rubocop --- core/app/models/concerns/comable/cart_owner.rb | 2 ++ frontend/app/controllers/comable/carts_controller.rb | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/app/models/concerns/comable/cart_owner.rb b/core/app/models/concerns/comable/cart_owner.rb index 4518bbe5..7bfed4dc 100644 --- a/core/app/models/concerns/comable/cart_owner.rb +++ b/core/app/models/concerns/comable/cart_owner.rb @@ -51,6 +51,7 @@ def errors private + # rubocop:disable Metrics/MethodLength def process_cart_item(obj) case obj when Comable::Product @@ -65,6 +66,7 @@ def process_cart_item(obj) fail end end + # rubocop:enable Metrics/MethodLength def add_stock_to_cart(stock, quantity) cart_item = find_cart_item_by(stock) diff --git a/frontend/app/controllers/comable/carts_controller.rb b/frontend/app/controllers/comable/carts_controller.rb index 1e857580..494ffa97 100644 --- a/frontend/app/controllers/comable/carts_controller.rb +++ b/frontend/app/controllers/comable/carts_controller.rb @@ -51,15 +51,17 @@ def ensure_found_cart_item def find_cart_item cart_item = Comable::Stock.where(id: params[:stock_id]).first - cart_item ||= Comable::Variant.joins(:option_values).where(where_option_value).first + cart_item ||= find_variant cart_item ||= Comable::Product.where(id: params[:product_id]).first return unless cart_item return if cart_item.is_a?(Comable::Product) && cart_item.sku? cart_item end - def where_option_value - { Comable::OptionValue.table_name => { name: params[:option_value_names] } } + def find_variant + Comable::Variant.joins(:option_values).where( + Comable::OptionValue.table_name => { name: params[:option_value_names] } + ).first end def cart_item_options From 06454d9470781ec5192eff7b4e020bd97ea5d596 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 16:51:36 +0900 Subject: [PATCH 48/58] Remove the unused code --- core/app/helpers/comable/products_helper.rb | 63 --------------------- core/app/models/comable/stock.rb | 8 --- 2 files changed, 71 deletions(-) diff --git a/core/app/helpers/comable/products_helper.rb b/core/app/helpers/comable/products_helper.rb index e6f84366..ee0ca8a7 100644 --- a/core/app/helpers/comable/products_helper.rb +++ b/core/app/helpers/comable/products_helper.rb @@ -15,68 +15,5 @@ def link_to_category(category, force_link: false) category.name end end - - def sku_table(product, options = nil) - stocks = product.stocks - content_tag(:table, nil, options) do - html = '' - html << build_sku_v_table_header(product, stocks) - html << build_sku_table_rows(product, stocks) - html.html_safe - end - end - - private - - def build_sku_v_table_header(product, stocks) - sku_item_name = product.sku_h_item_name - sku_item_name += '/' + product.sku_v_item_name if product.sku_v? - - html = '' - html << content_tag(:th, sku_item_name) - stocks.group_by(&:sku_h_choice_name).keys.each do |sku_h_choice_name| - html << content_tag(:th, sku_h_choice_name) - end - html.html_safe - end - - def build_sku_table_rows(product, stocks) - return content_tag(:tr, build_sku_table_row(stocks)) unless product.sku_v? - - html = '' - stocks_groups_by_sku_v(stocks).each_pair do |sku_v_choice_name, sku_v_stocks| - html << content_tag(:tr, build_sku_table_row(sku_v_stocks, sku_v_choice_name)) - end - html.html_safe - end - - def build_sku_table_row(stocks, sku_v_choice_name = nil) - html = '' - html << content_tag(:th, sku_v_choice_name) - html << stocks.map { |stock| content_tag(:td, build_sku_product_label(stock)) }.join - html.html_safe - end - - def build_sku_product_label(stock) - return unless stock - content_tag(:div, class: 'radio') do - content_tag(:label) do - html = '' - html << radio_button_tag(:stock_id, stock.id, false, disabled: stock.unstocked?) - html << stock.code - html.html_safe - end - end - end - - def stocks_groups_by_sku_v(stocks) - sku_h_choice_names = stocks.group_by(&:sku_h_choice_name).keys - - stocks.group_by(&:sku_v_choice_name).each_with_object({}) do |(sku_v_choice_name, sku_v_stocks), group| - group[sku_v_choice_name] = sku_h_choice_names.map do |sku_h_choice_name| - sku_v_stocks.find { |s| s.sku_h_choice_name == sku_h_choice_name } - end - end - end end end diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index 879bcb80..a2346615 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -85,14 +85,6 @@ def code variant.sku end - def code=(code) - if variant - variant.sku = code - else - build_variant(sku: code) - end - end - def product=(product) if variant variant.product = product From b4c9a3a406f8bf6e8efe60eb36fa7a6033c34bc5 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 16:59:54 +0900 Subject: [PATCH 49/58] Add the test for deprecated methods --- core/spec/models/comable/order_item_spec.rb | 14 +++++++++++ core/spec/models/comable/stock_spec.rb | 28 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/core/spec/models/comable/order_item_spec.rb b/core/spec/models/comable/order_item_spec.rb index 98e1f2de..c3c0a12e 100644 --- a/core/spec/models/comable/order_item_spec.rb +++ b/core/spec/models/comable/order_item_spec.rb @@ -21,4 +21,18 @@ end end end + + describe '#stock=' do + it 'should sets product with vatiant' do + subject.variant = build(:variant) + subject.stock = build(:stock) + expect(subject.stock).to eq(subject.variant.stock) + end + + it 'should sets product without vatiant' do + subject.variant = nil + subject.stock = build(:stock) + expect(subject.variant).to be_new_record + end + end end diff --git a/core/spec/models/comable/stock_spec.rb b/core/spec/models/comable/stock_spec.rb index a191ff0d..7303a3ad 100644 --- a/core/spec/models/comable/stock_spec.rb +++ b/core/spec/models/comable/stock_spec.rb @@ -1,3 +1,31 @@ describe Comable::Stock do it { is_expected.to validate_numericality_of(:quantity).is_greater_than_or_equal_to(0) } + + describe '#product=' do + it 'should sets product with vatiant' do + subject.variant = build(:variant) + subject.product = build(:product) + expect(subject.product).to eq(subject.variant.product) + end + + it 'should sets product without vatiant' do + subject.variant = nil + subject.product = build(:product) + expect(subject.variant).to be_new_record + end + end + + describe '#sku_v?' do + it 'should returns false when it has only option of variant' do + subject.variant = build(:variant, options: [name: 'Size', value: 'M']) + subject.product = build(:product) + expect(subject.sku_v?).to be false + end + + it 'should returns true when it has two options of variant' do + subject.variant = build(:variant, options: [name: 'Size', value: 'M'] + [name: 'Color', value: 'Red']) + subject.product = build(:product) + expect(subject.sku_v?).to be false + end + end end From e242083e656e9855b43cbd26fc8b4959f9670d83 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 18:33:22 +0900 Subject: [PATCH 50/58] Fix the problem that cannot destroy the cart item --- .../app/models/concerns/comable/cart_owner.rb | 2 +- .../controllers/comable/carts_controller.rb | 9 ++---- .../comable/carts_controller_spec.rb | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/core/app/models/concerns/comable/cart_owner.rb b/core/app/models/concerns/comable/cart_owner.rb index 7bfed4dc..d32d7935 100644 --- a/core/app/models/concerns/comable/cart_owner.rb +++ b/core/app/models/concerns/comable/cart_owner.rb @@ -92,7 +92,7 @@ def reset_stock_from_cart(stock, quantity) def find_cart_item_by(stock) # TODO: Refactoring fail unless stock.is_a?(Comable::Stock) - cart_items.find { |cart_item| cart_item.stock.id == stock.id } + cart_items.find { |cart_item| cart_item.stock == stock } end end end diff --git a/frontend/app/controllers/comable/carts_controller.rb b/frontend/app/controllers/comable/carts_controller.rb index 494ffa97..52ef8fd2 100644 --- a/frontend/app/controllers/comable/carts_controller.rb +++ b/frontend/app/controllers/comable/carts_controller.rb @@ -1,7 +1,7 @@ module Comable class CartsController < Comable::ApplicationController - before_filter :set_cart_item, only: [:add, :update] - before_filter :ensure_found_cart_item, only: [:add, :update] + before_filter :set_cart_item, only: [:add, :update, :destroy] + before_filter :ensure_found_cart_item, only: [:add, :update, :destroy] def add if current_comable_user.add_cart_item(@cart_item, cart_item_options) @@ -22,10 +22,7 @@ def update end def destroy - cart_item = find_cart_item - return redirect_by_product_not_found unless cart_item - - if current_comable_user.reset_cart_item(cart_item) + if current_comable_user.reset_cart_item(@cart_item) redirect_to comable.cart_path, notice: Comable.t('carts.updated') else flash.now[:alert] = Comable.t('carts.invalid') diff --git a/frontend/spec/controllers/comable/carts_controller_spec.rb b/frontend/spec/controllers/comable/carts_controller_spec.rb index f48c97ea..6fedeab3 100644 --- a/frontend/spec/controllers/comable/carts_controller_spec.rb +++ b/frontend/spec/controllers/comable/carts_controller_spec.rb @@ -149,6 +149,35 @@ end end + describe 'DELETE destroy' do + let(:product) { create(:product, :with_stock) } + + describe 'with valid params' do + it 'destroys the requested cart_item' do + current_comable_user.add_cart_item(product) + expect { delete :destroy, product_id: product.to_param }.to change(Comable::OrderItem, :count).by(-1) + end + + it 'redirects to the cart' do + current_comable_user.add_cart_item(product) + delete :destroy, product_id: product.to_param + expect(response).to redirect_to([controller.comable, :cart]) + end + end + + describe 'with invalid params' do + it "re-renders the 'show' template" do + delete :destroy, product_id: product.to_param + expect(response).to render_template(:show) + end + + it 'assigns the message as flash.now[:alert]' do + delete :destroy, product_id: product.to_param + expect(flash.now[:alert]).to eq(Comable.t('carts.invalid')) + end + end + end + private # TODO: Move to the support directory. From 0937e40cf2a2c6b4f945e15d7e6dd531c2ec5281 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Mon, 7 Sep 2015 18:58:04 +0900 Subject: [PATCH 51/58] Add the test about updating the cart item --- .../comable/carts_controller_spec.rb | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/frontend/spec/controllers/comable/carts_controller_spec.rb b/frontend/spec/controllers/comable/carts_controller_spec.rb index 6fedeab3..e972617e 100644 --- a/frontend/spec/controllers/comable/carts_controller_spec.rb +++ b/frontend/spec/controllers/comable/carts_controller_spec.rb @@ -149,6 +149,37 @@ end end + describe 'PUT update' do + let(:product) { create(:product, :with_stock) } + + describe 'with valid params' do + it 'updates the requested cart item' do + current_comable_user.add_cart_item(product) + cart_item = current_comable_user.cart.first + expect { put :update, product_id: product.to_param, quantity: cart_item.quantity + 1 }.to change { cart_item.reload.quantity }.by(1) + end + + it 'redirects to the cart' do + current_comable_user.add_cart_item(product) + cart_item = current_comable_user.cart.first + put :update, product_id: product.to_param, quantity: cart_item.quantity + 1 + expect(response).to redirect_to([controller.comable, :cart]) + end + end + + describe 'with invalid params' do + it "re-renders the 'show' template" do + put :update, product_id: product.to_param + expect(response).to render_template(:show) + end + + it 'assigns the message as flash.now[:alert]' do + put :update, product_id: product.to_param + expect(flash.now[:alert]).to eq(Comable.t('carts.invalid')) + end + end + end + describe 'DELETE destroy' do let(:product) { create(:product, :with_stock) } From 9e99559cd1ad3e7052fbf52888f32962e206f3bd Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 15:01:57 +0900 Subject: [PATCH 52/58] Revert "Change the primary keys of OptionType and OptionValue" This partial reverts commit 200f04e7522a56638dc150c2c12fc4575420af74. --- core/app/models/comable/option_type.rb | 4 +--- core/app/models/comable/option_value.rb | 6 ++---- core/app/models/comable/variant.rb | 2 +- .../migrate/20150823072622_create_comable_option_types.rb | 2 +- .../migrate/20150823072955_create_comable_option_values.rb | 6 +++--- .../20150823073250_create_comable_variants_option_values.rb | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/core/app/models/comable/option_type.rb b/core/app/models/comable/option_type.rb index 55b9d8fb..ef9deb0f 100644 --- a/core/app/models/comable/option_type.rb +++ b/core/app/models/comable/option_type.rb @@ -1,10 +1,8 @@ module Comable class OptionType < ActiveRecord::Base - self.primary_key = :name - include Comable::Ransackable - has_many :option_values, class_name: Comable::OptionValue.name, foreign_key: :option_type_name + has_many :option_values, class_name: Comable::OptionValue.name validates :name, presence: true, length: { maximum: 255 } diff --git a/core/app/models/comable/option_value.rb b/core/app/models/comable/option_value.rb index 49482cf1..58d99ceb 100644 --- a/core/app/models/comable/option_value.rb +++ b/core/app/models/comable/option_value.rb @@ -1,11 +1,9 @@ module Comable class OptionValue < ActiveRecord::Base - self.primary_key = :name - include Comable::Ransackable - belongs_to :option_type, class_name: Comable::OptionType.name, foreign_key: :option_type_name - has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values, foreign_key: :option_value_name + belongs_to :option_type, class_name: Comable::OptionType.name + has_and_belongs_to_many :variants, class_name: Comable::Variant.name, join_table: :comable_variants_option_values accepts_nested_attributes_for :option_type diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 2d0d187f..5421df26 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -5,7 +5,7 @@ class Variant < ActiveRecord::Base belongs_to :product, class_name: Comable::Product.name, inverse_of: :variants has_one :stock, class_name: Comable::Stock.name, inverse_of: :variant, dependent: :destroy, autosave: true - has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values, association_foreign_key: :option_value_name + has_and_belongs_to_many :option_values, class_name: Comable::OptionValue.name, join_table: :comable_variants_option_values accepts_nested_attributes_for :option_values, allow_destroy: true accepts_nested_attributes_for :stock diff --git a/core/db/migrate/20150823072622_create_comable_option_types.rb b/core/db/migrate/20150823072622_create_comable_option_types.rb index ce85712d..20b2395c 100644 --- a/core/db/migrate/20150823072622_create_comable_option_types.rb +++ b/core/db/migrate/20150823072622_create_comable_option_types.rb @@ -1,6 +1,6 @@ class CreateComableOptionTypes < ActiveRecord::Migration def change - create_table :comable_option_types, id: false do |t| + create_table :comable_option_types do |t| t.string :name, null: false t.timestamps null: false end diff --git a/core/db/migrate/20150823072955_create_comable_option_values.rb b/core/db/migrate/20150823072955_create_comable_option_values.rb index 9d4182d7..ca628d1b 100644 --- a/core/db/migrate/20150823072955_create_comable_option_values.rb +++ b/core/db/migrate/20150823072955_create_comable_option_values.rb @@ -1,11 +1,11 @@ class CreateComableOptionValues < ActiveRecord::Migration def change - create_table :comable_option_values, id: false do |t| - t.string :option_type_name, null: false + create_table :comable_option_values do |t| + t.references :option_type, null: false t.string :name, null: false t.timestamps null: false end - add_index :comable_option_values, [:option_type_name, :name], unique: true + add_index :comable_option_values, [:option_type_id, :name], unique: true end end diff --git a/core/db/migrate/20150823073250_create_comable_variants_option_values.rb b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb index ecdf0b7e..cfcf9df6 100644 --- a/core/db/migrate/20150823073250_create_comable_variants_option_values.rb +++ b/core/db/migrate/20150823073250_create_comable_variants_option_values.rb @@ -2,7 +2,7 @@ class CreateComableVariantsOptionValues < ActiveRecord::Migration def change create_table :comable_variants_option_values do |t| t.references :variant, null: false, index: true - t.string :option_value_name, null: false, index: true + t.references :option_value, null: false, index: true end end end From abc266e26ac103280e8c68c9e3e8b444599b5136 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 15:25:40 +0900 Subject: [PATCH 53/58] Change the method to index options of Variant --- backend/app/views/comable/admin/products/_form.slim | 4 ++-- backend/app/views/comable/admin/products/index.slim | 4 ++-- backend/app/views/comable/admin/stocks/index.slim | 6 +++--- core/app/models/comable/stock.rb | 4 ++-- core/app/models/comable/variant.rb | 2 +- core/spec/models/comable/variant_spec.rb | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/app/views/comable/admin/products/_form.slim b/backend/app/views/comable/admin/products/_form.slim index 19a21e44..0cc041a2 100644 --- a/backend/app/views/comable/admin/products/_form.slim +++ b/backend/app/views/comable/admin/products/_form.slim @@ -132,9 +132,9 @@ strong class="#{(quantity <= 0) ? 'text-danger' : (quantity <= 10) ? 'text-warning' : 'text-success'}" = number_with_delimiter quantity td - - variant.names.each do |name| + - variant.options.each do |option| span.comable-variant-name - = name + = option.value td = link_to comable.admin_product_variant_path(@product, variant), class: 'btn btn-default' i.fa.fa-edit> diff --git a/backend/app/views/comable/admin/products/index.slim b/backend/app/views/comable/admin/products/index.slim index b5ab3880..76f1d9d8 100644 --- a/backend/app/views/comable/admin/products/index.slim +++ b/backend/app/views/comable/admin/products/index.slim @@ -88,9 +88,9 @@ - if !product.master? span< | ( - - variant.names.each do |name| + - variant.options.each do |option| span.comable-variant-name - = name + = option.value | ) .text-center diff --git a/backend/app/views/comable/admin/stocks/index.slim b/backend/app/views/comable/admin/stocks/index.slim index a2961b03..ddad5470 100644 --- a/backend/app/views/comable/admin/stocks/index.slim +++ b/backend/app/views/comable/admin/stocks/index.slim @@ -66,13 +66,13 @@ - @stocks.each do |stock| tr td - - if stock.variant.names.any? + - if stock.variant.options.any? p = link_to stock.variant.product.name, comable.admin_product_variant_path(stock.variant.product, stock.variant) p - - stock.variant.names.each do |name| + - stock.variant.options.each do |option| span.comable-variant-name - = name + = option.value - else p = link_to stock.variant.product.name, comable.admin_product_path(stock.variant.product) diff --git a/core/app/models/comable/stock.rb b/core/app/models/comable/stock.rb index a2346615..b2be6367 100644 --- a/core/app/models/comable/stock.rb +++ b/core/app/models/comable/stock.rb @@ -66,8 +66,8 @@ def unstocked?(quantity: 1) end def name - if variant.names.any? - "#{variant.product.name} (#{variant.names.join('/')})" + if variant.options.any? + "#{variant.product.name} (#{variant.options.map(&:value).join('/')})" else variant.product.name end diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 5421df26..101435c2 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -38,7 +38,7 @@ def quantity=(quantity) def options option_values.map do |option_value| - { name: option_value.option_type.try(:name), value: option_value.name } + OpenStruct.new(type: option_value.option_type.try(:name), value: option_value.name) end end diff --git a/core/spec/models/comable/variant_spec.rb b/core/spec/models/comable/variant_spec.rb index e3452852..d8aab0a8 100644 --- a/core/spec/models/comable/variant_spec.rb +++ b/core/spec/models/comable/variant_spec.rb @@ -19,7 +19,7 @@ option_type = build(:option_type) option_value = build(:option_value, option_type: option_type) subject.option_values = [option_value] - expect(subject.options.first[:name]).to eq(option_type.name) + expect(subject.options.first[:type]).to eq(option_type.name) end end From 39491f24f5a54349bce791db1aff182bc697ea9e Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 15:26:00 +0900 Subject: [PATCH 54/58] Remove the unused methods --- core/app/models/comable/variant.rb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/core/app/models/comable/variant.rb b/core/app/models/comable/variant.rb index 101435c2..8b5aa6b4 100644 --- a/core/app/models/comable/variant.rb +++ b/core/app/models/comable/variant.rb @@ -14,16 +14,8 @@ class Variant < ActiveRecord::Base validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :sku, length: { maximum: 255 } - before_save :set_option_values_from_names, if: :product - ransack_options attribute_select: { associations: [:product, :stock, :option_values] }, ransackable_attributes: { except: :product_id } - attr_writer :names - - def names - @names ? @names : option_values.map(&:name) - end - def quantity stock.try(:quantity) || build_stock.quantity end @@ -50,18 +42,5 @@ def options=(options) Comable::OptionValue.where(name: hash[:value], option_type: option_type).first_or_initialize(&:save!) end end - - private - - def set_option_values_from_names - return unless @names - attributes = [] - @names.split(',').each.with_index do |name, index| - option_type = product.option_types[index] - attributes << { option_type: option_type, name: name } - end - self.names = nil - self.option_values_attributes = attributes - end end end From cb31b548cdf43a9902cba42802a8d8faddf2808e Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 16:02:43 +0900 Subject: [PATCH 55/58] Fix the problem that cannot add to cart --- frontend/app/controllers/comable/carts_controller.rb | 2 +- frontend/app/views/comable/products/show.slim | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/app/controllers/comable/carts_controller.rb b/frontend/app/controllers/comable/carts_controller.rb index 52ef8fd2..665760db 100644 --- a/frontend/app/controllers/comable/carts_controller.rb +++ b/frontend/app/controllers/comable/carts_controller.rb @@ -57,7 +57,7 @@ def find_cart_item def find_variant Comable::Variant.joins(:option_values).where( - Comable::OptionValue.table_name => { name: params[:option_value_names] } + Comable::OptionValue.table_name => { id: params[:option_values] } ).first end diff --git a/frontend/app/views/comable/products/show.slim b/frontend/app/views/comable/products/show.slim index c1da18c1..b6ef45ef 100644 --- a/frontend/app/views/comable/products/show.slim +++ b/frontend/app/views/comable/products/show.slim @@ -33,11 +33,11 @@ = form_tag comable.add_cart_path do - if @product.sku? - - @product.variants.map(&:option_values).flatten.uniq.group_by(&:option_type_name).each do |key, option_values| + - @product.variants.map(&:option_values).flatten.uniq.group_by(&:option_type).each_pair.with_index do |(option_type, option_values), index| .sku - label - = key - = select_tag 'option_value_names[]', options_for_select(option_values.map(&:name)) + label for="option_values_#{index}" + = option_type.name + = select_tag "option_values[]", options_for_select(option_values.map { |o| [o.name, o.id] }), id: "option_values_#{index}" - if @product.stocked? .add_cart.form-inline.form-group From aaadd0258e831bb0f360d67b9a3dbd9cf568e541 Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 16:10:30 +0900 Subject: [PATCH 56/58] Remove the unnecessary code --- .../app/controllers/comable/admin/variants_controller.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/app/controllers/comable/admin/variants_controller.rb b/backend/app/controllers/comable/admin/variants_controller.rb index afccfd53..ca4a6e1e 100644 --- a/backend/app/controllers/comable/admin/variants_controller.rb +++ b/backend/app/controllers/comable/admin/variants_controller.rb @@ -40,12 +40,8 @@ def update end def destroy - if @variant.destroy - redirect_to comable.admin_product_variants_path(@product), notice: Comable.t('successful') - else - flash.now[:alert] = Comable.t('failure') - render :edit - end + @variant.destroy + redirect_to comable.admin_product_variants_path(@product), notice: Comable.t('successful') end private From 9db187e50edbfc3d85d4a400fcc859783cd9b0bc Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 17:22:10 +0900 Subject: [PATCH 57/58] Fix the method name --- backend/app/views/comable/admin/orders/show.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app/views/comable/admin/orders/show.slim b/backend/app/views/comable/admin/orders/show.slim index d6b19c01..4b7ceeec 100644 --- a/backend/app/views/comable/admin/orders/show.slim +++ b/backend/app/views/comable/admin/orders/show.slim @@ -64,7 +64,7 @@ td = order_item.name_with_sku .text-muted - small = order_item.code + small = order_item.sku td = number_to_currency order_item.price td From 2032d1a56ef1beaae65a8ba725f7b039e9b10d1b Mon Sep 17 00:00:00 2001 From: YOSHIDA Hiroki Date: Fri, 11 Sep 2015 17:22:54 +0900 Subject: [PATCH 58/58] Update the version of `deprecation_horizon` --- core/lib/comable/deprecator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/comable/deprecator.rb b/core/lib/comable/deprecator.rb index 979fa9aa..b9c3e779 100644 --- a/core/lib/comable/deprecator.rb +++ b/core/lib/comable/deprecator.rb @@ -1,7 +1,7 @@ if Rails::VERSION::MAJOR > 3 module Comable class Deprecator < ActiveSupport::Deprecation - def initialize(deprecation_horizon = '0.4.0', gem_name = 'Comable') + def initialize(deprecation_horizon = '0.8.0', gem_name = 'Comable') super end end