comable_core 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/comable/application_helper.rb +17 -10
  3. data/app/models/comable/ability.rb +1 -1
  4. data/app/models/comable/address.rb +2 -2
  5. data/app/models/comable/order/associations.rb +22 -0
  6. data/app/models/comable/order/callbacks.rb +43 -0
  7. data/app/models/comable/order/scopes.rb +17 -0
  8. data/app/models/comable/order/validations.rb +39 -0
  9. data/app/models/comable/order.rb +63 -72
  10. data/app/models/comable/order_item/csvable.rb +32 -0
  11. data/app/models/comable/{order_detail.rb → order_item.rb} +19 -10
  12. data/app/models/comable/payment.rb +82 -0
  13. data/app/models/comable/payment_method.rb +1 -0
  14. data/app/models/comable/product/csvable.rb +20 -0
  15. data/app/models/comable/product.rb +2 -7
  16. data/app/models/comable/shipment.rb +79 -0
  17. data/app/models/comable/stock/csvable.rb +26 -0
  18. data/app/models/comable/stock.rb +2 -17
  19. data/app/models/comable/tracker.rb +17 -0
  20. data/app/models/comable/{customer.rb → user.rb} +11 -23
  21. data/app/models/concerns/comable/checkout.rb +35 -36
  22. data/app/models/concerns/comable/importable.rb +67 -0
  23. data/app/views/comable/order_mailer/complete.text.erb +9 -8
  24. data/config/initializers/comma.rb +8 -0
  25. data/config/locales/en.yml +105 -14
  26. data/config/locales/ja.yml +92 -19
  27. data/db/migrate/{20140120032559_create_comable_customers.rb → 20140120032559_create_comable_users.rb} +6 -6
  28. data/db/migrate/20140723175431_create_comable_orders.rb +2 -4
  29. data/db/migrate/{20140723175810_create_comable_order_details.rb → 20140723175810_create_comable_order_items.rb} +3 -3
  30. data/db/migrate/20140817194104_create_comable_payment_methods.rb +1 -0
  31. data/db/migrate/20141024025526_create_comable_addresses.rb +1 -1
  32. data/db/migrate/20150423095210_create_comable_shipments.rb +12 -0
  33. data/db/migrate/20150511171940_create_comable_payments.rb +11 -0
  34. data/db/migrate/20150513185230_create_comable_trackers.rb +12 -0
  35. data/db/seeds/comable/{customers.rb → users.rb} +3 -3
  36. data/lib/comable/core/configuration.rb +7 -1
  37. data/lib/comable/core/engine.rb +0 -25
  38. data/lib/comable/payment_provider/base.rb +16 -0
  39. data/lib/comable_core.rb +6 -1
  40. data/lib/comma_extractor_extentions.rb +31 -0
  41. data/lib/generators/comable/install/templates/config/initializers/comable.rb +16 -1
  42. metadata +79 -8
  43. data/lib/comable/errors.rb +0 -4
@@ -0,0 +1,79 @@
1
+ module Comable
2
+ class Shipment < ActiveRecord::Base
3
+ include Comable::Ransackable
4
+
5
+ belongs_to :order, class_name: Comable::Order.name, inverse_of: :shipment
6
+ belongs_to :shipment_method, class_name: Comable::ShipmentMethod.name
7
+
8
+ before_validation :copy_attributes_from_shipment_method, unless: :order_completed?
9
+
10
+ validates :order, presence: true
11
+ validates :shipment_method, presence: true
12
+ validates :fee, presence: true, numericality: { greater_than_or_equal_to: 0 }
13
+ validates :tracking_number, length: { maximum: 255 }
14
+
15
+ delegate :name, to: :shipment_method
16
+
17
+ ransack_options ransackable_attributes: { except: [:order_id, :shipment_method_id] }
18
+
19
+ # The #state attribute assigns the following values:
20
+ #
21
+ # pending when Order is not able to ship (default)
22
+ # ready when Order is able to ship
23
+ # completed when Order is already shipped
24
+ # canceled when Order is canceled
25
+ # resumed when Order is resumed from the "canceled" state
26
+ state_machine initial: :pending do
27
+ state :pending
28
+ state :ready
29
+ state :completed
30
+ state :canceled
31
+ state :resumed
32
+
33
+ event :next_state do
34
+ transition :pending => :ready
35
+ transition :ready => :completed
36
+ end
37
+
38
+ event :ship do
39
+ transition :ready => :completed
40
+ end
41
+
42
+ event :cancel do
43
+ transition [:completed, :resumed] => :canceled
44
+ end
45
+
46
+ event :resume do
47
+ transition :canceled => :resumed
48
+ end
49
+ end
50
+
51
+ class << self
52
+ def state_names
53
+ state_machine.states.keys
54
+ end
55
+ end
56
+
57
+ def stated?(target_state)
58
+ target_state_index = self.class.state_names.index(target_state.to_sym)
59
+ current_state_index = self.class.state_names.index(state_name)
60
+ target_state_index < current_state_index
61
+ end
62
+
63
+ def completed?
64
+ state?(:completed) || state?(:resumed)
65
+ end
66
+
67
+ private
68
+
69
+ def order_completed?
70
+ order.completed?
71
+ end
72
+
73
+ def copy_attributes_from_shipment_method
74
+ self.attributes = {
75
+ fee: shipment_method.fee
76
+ }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,26 @@
1
+ module Comable
2
+ class Stock < ActiveRecord::Base
3
+ module Csvable
4
+ extend ActiveSupport::Concern
5
+
6
+ include Comable::Importable
7
+
8
+ included do
9
+ comma do
10
+ product_code
11
+ code
12
+ quantity
13
+ sku_h_choice_name
14
+ sku_v_choice_name
15
+ end
16
+ end
17
+
18
+ delegate :code, to: :product, prefix: true, allow_nil: true
19
+
20
+ def product_code=(code)
21
+ return if product_code == code
22
+ self.product = Comable::Product.find_by(code: code)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,6 +7,7 @@ module Comable
7
7
  include Comable::SkuItem
8
8
  include Comable::SkuChoice
9
9
  include Comable::Ransackable
10
+ include Comable::Stock::Csvable
10
11
 
11
12
  belongs_to :product, class_name: Comable::Product.name
12
13
 
@@ -18,25 +19,15 @@ module Comable
18
19
  # 品切れでない在庫インスタンスを返す
19
20
  scope :stocked, -> { where('quantity > ?', 0) }
20
21
 
21
- class << self
22
- alias_method :unsold, :stocked
23
- deprecate :unsold, deprecator: Comable::Deprecator.instance
24
- end
25
-
26
22
  # @!scope class
27
23
  # 品切れの在庫インスタンスを返す
28
24
  scope :unstocked, -> { where('quantity <= ?', 0) }
29
25
 
30
- class << self
31
- alias_method :soldout, :unstocked
32
- deprecate :soldout, deprecator: Comable::Deprecator.instance
33
- end
34
-
35
26
  #
36
27
  # @!endgroup
37
28
  #
38
29
 
39
- validates :product, presence: true
30
+ validates :product, presence: { message: Comable.t('admin.is_not_exists') }
40
31
  validates :code, presence: true, length: { maximum: 255 }
41
32
  validates :sku_h_choice_name, length: { maximum: 255 }
42
33
  validates :sku_v_choice_name, length: { maximum: 255 }
@@ -63,9 +54,6 @@ module Comable
63
54
  (self.quantity - quantity) >= 0
64
55
  end
65
56
 
66
- alias_method :unsold?, :stocked?
67
- deprecate :unsold?, deprecator: Comable::Deprecator.instance
68
-
69
57
  # 在庫の有無を取得する
70
58
  #
71
59
  # @example
@@ -78,8 +66,5 @@ module Comable
78
66
  def unstocked?(quantity: 1)
79
67
  !stocked?(quantity: quantity)
80
68
  end
81
-
82
- alias_method :soldout?, :unstocked?
83
- deprecate :soldout?, deprecator: Comable::Deprecator.instance
84
69
  end
85
70
  end
@@ -0,0 +1,17 @@
1
+ module Comable
2
+ class Tracker < ActiveRecord::Base
3
+ extend Enumerize
4
+
5
+ validates :name, presence: true, length: { maximum: 255 }
6
+ validates :tracker_id, length: { maximum: 255 }
7
+ validates :code, presence: true
8
+ validates :place, presence: true, length: { maximum: 255 }
9
+
10
+ scope :activated, -> { where(activate_flag: true) }
11
+
12
+ enumerize :place, in: %i(
13
+ everywhere
14
+ checkout
15
+ ), scope: true
16
+ end
17
+ end
@@ -1,5 +1,5 @@
1
1
  module Comable
2
- class Customer < ActiveRecord::Base
2
+ class User < ActiveRecord::Base
3
3
  include Comable::CartOwner
4
4
  include Comable::RoleOwner
5
5
  include Comable::Ransackable
@@ -15,7 +15,7 @@ module Comable
15
15
 
16
16
  validates :email, presence: true, length: { maximum: 255 }
17
17
 
18
- devise(*Comable::Config.devise_strategies[:customer])
18
+ devise(*Comable::Config.devise_strategies[:user])
19
19
 
20
20
  ransack_options ransackable_attributes: { except: [:encrypted_password, :reset_password_token, :reset_password_sent_at, :remember_created_at, :bill_address_id, :ship_address_id] }
21
21
 
@@ -62,28 +62,21 @@ module Comable
62
62
  end
63
63
 
64
64
  def cart_items
65
- @cart_items ||= incomplete_order.order_details
65
+ incomplete_order.order_items
66
66
  end
67
67
 
68
68
  def incomplete_order
69
+ @incomplete_order = nil if @incomplete_order.try(:completed?)
69
70
  @incomplete_order ||= find_incomplete_order || initialize_incomplete_order
70
71
  end
71
72
 
72
- def order(order_params = {})
73
- Rails.logger.debug '[DEPRECATED] Comable::Customer#order is deprecated. Please use Comable::Order#next_state method.'
74
- incomplete_order.attributes = order_params
75
- incomplete_order.state = 'complete'
76
- incomplete_order.complete!
77
- incomplete_order.tap { reload }
78
- end
79
-
80
73
  def after_set_user
81
74
  return unless current_guest_token
82
75
 
83
- guest_order = Comable::Order.incomplete.preload(:order_details).where(guest_token: current_guest_token).first
76
+ guest_order = Comable::Order.incomplete.preload(:order_items).where(guest_token: current_guest_token).first
84
77
  return unless guest_order
85
78
 
86
- inherit_order_state(guest_order)
79
+ incomplete_order.inherit!(guest_order)
87
80
  inherit_cart_items(guest_order)
88
81
  end
89
82
 
@@ -106,7 +99,7 @@ module Comable
106
99
 
107
100
  def incomplete_order_attributes
108
101
  {
109
- customer_id: id,
102
+ user_id: id,
110
103
  email: email
111
104
  }
112
105
  end
@@ -115,20 +108,15 @@ module Comable
115
108
  guest_token ||= current_guest_token unless signed_in?
116
109
  Comable::Order
117
110
  .incomplete
118
- .preload(:order_details)
111
+ .preload(:order_items)
119
112
  .where(guest_token: guest_token)
120
- .by_customer(self)
113
+ .by_user(self)
121
114
  .first
122
115
  end
123
116
 
124
- def inherit_order_state(guest_order)
125
- return if incomplete_order.stated?(guest_order.state)
126
- incomplete_order.next_state
127
- end
128
-
129
117
  def inherit_cart_items(guest_order)
130
- guest_order.order_details.each do |order_detail|
131
- move_cart_item(order_detail)
118
+ guest_order.order_items.each do |order_item|
119
+ move_cart_item(order_item)
132
120
  end
133
121
  end
134
122
  end
@@ -10,52 +10,35 @@ module Comable
10
10
  state :shipment
11
11
  state :payment
12
12
  state :confirm
13
- state :complete
13
+ state :completed
14
+ state :canceled
15
+ state :returned
16
+ state :resumed
14
17
 
15
18
  event :next_state do
16
19
  transition :cart => :orderer, if: :orderer_required?
17
20
  transition [:cart, :orderer] => :delivery, if: :delivery_required?
18
21
  transition [:cart, :orderer, :delivery] => :shipment, if: :shipment_required?
19
22
  transition [:cart, :orderer, :delivery, :shipment] => :payment, if: :payment_required?
20
- transition all - [:confirm, :complete] => :confirm
21
- transition :confirm => :complete
23
+ transition all - [:confirm, :completed] => :confirm
24
+ transition :confirm => :completed
22
25
  end
23
26
 
24
- before_transition to: :complete do |order, _transition|
25
- order.complete
27
+ event :cancel do
28
+ transition to: :canceled, from: [:completed, :resumed], if: :allow_cancel?
26
29
  end
27
- end
28
-
29
- # TODO: Remove with_options
30
- with_options if: -> { state?(:cart) } do |context|
31
- context.validates :customer_id, presence: true, uniqueness: { scope: [:customer_id, :completed_at] }, unless: :guest_token
32
- context.validates :guest_token, presence: true, uniqueness: { scope: [:guest_token, :completed_at] }, unless: :customer
33
- end
34
-
35
- with_options if: -> { stated?(:cart) } do |context|
36
- context.validates :email, presence: true
37
- end
38
30
 
39
- with_options if: -> { stated?(:orderer) } do |context|
40
- context.validates :bill_address, presence: true
41
- end
42
-
43
- with_options if: -> { stated?(:delivery) } do |context|
44
- context.validates :ship_address, presence: true
45
- end
46
-
47
- with_options if: -> { stated?(:payment) && payment_required? } do |context|
48
- context.validates :payment_method, presence: true
49
- end
31
+ event :return do
32
+ transition to: :returned, from: [:completed, :resumed], if: :allow_return?
33
+ end
50
34
 
51
- with_options if: -> { stated?(:shipment) && shipment_required? } do |context|
52
- context.validates :shipment_method, presence: true
53
- end
35
+ event :resume do
36
+ transition to: :resumed, from: :canceled
37
+ end
54
38
 
55
- with_options if: -> { stated?(:complete) } do |context|
56
- context.validates :code, presence: true
57
- context.validates :shipment_fee, presence: true
58
- context.validates :total_price, presence: true
39
+ before_transition to: :completed, do: :complete!
40
+ after_transition to: :canceled, do: :restock!
41
+ after_transition to: :resumed, do: :unstock!
59
42
  end
60
43
  end
61
44
 
@@ -71,6 +54,10 @@ module Comable
71
54
  target_state_index < current_state_index
72
55
  end
73
56
 
57
+ def completed?
58
+ state?(:completed) || state?(:resumed)
59
+ end
60
+
74
61
  def orderer_required?
75
62
  bill_address.nil? || bill_address.new_record?
76
63
  end
@@ -80,11 +67,23 @@ module Comable
80
67
  end
81
68
 
82
69
  def payment_required?
83
- Comable::PaymentMethod.exists?
70
+ Comable::PaymentMethod.exists? && payment.nil?
84
71
  end
85
72
 
86
73
  def shipment_required?
87
- Comable::ShipmentMethod.activated.exists?
74
+ Comable::ShipmentMethod.activated.exists? && shipment.nil?
75
+ end
76
+
77
+ def allow_cancel?
78
+ # TODO: Implement shipments
79
+ # !shipments.exists?
80
+ true
81
+ end
82
+
83
+ def allow_return?
84
+ # TODO: Implement shipments
85
+ # shipments.exists?
86
+ false
88
87
  end
89
88
  end
90
89
  end
@@ -0,0 +1,67 @@
1
+ module Comable
2
+ module Importable
3
+ extend ActiveSupport::Concern
4
+
5
+ class Exception < StandardError
6
+ end
7
+
8
+ class UnknownFileType < Comable::Importable::Exception
9
+ end
10
+
11
+ class RecordInvalid < Comable::Importable::Exception
12
+ end
13
+
14
+ # from http://railscasts.com/episodes/396-importing-csv-and-excel
15
+ module ClassMethods
16
+ def import_from(file, primary_key: :code)
17
+ spreadsheet = open_spreadsheet(file)
18
+ read_spreadsheet(spreadsheet) do |header, row|
19
+ attributes = attributes_from_header_and_row(header, row)
20
+ record = find_by(primary_key => attributes[primary_key]) || new
21
+ begin
22
+ record.update_attributes!(attributes)
23
+ rescue ActiveRecord::RecordInvalid => e
24
+ raise RecordInvalid, "#{record.class.human_attribute_name(primary_key)} \"#{record.send(primary_key)}\": #{e.message}"
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def open_spreadsheet(file)
32
+ case File.extname(file.original_filename)
33
+ when '.csv' then Roo::CSV.new(file.path)
34
+ when '.xls' then Roo::Excel.new(file.path, nil, :ignore)
35
+ when '.xlsx' then Roo::Excelx.new(file.path, nil, :ignore)
36
+ else fail UnknownFileType, Comable.t('admin.unknown_file_type', filename: file.original_filename)
37
+ end
38
+ end
39
+
40
+ def read_spreadsheet(spreadsheet)
41
+ header = spreadsheet.row(1)
42
+ (2..spreadsheet.last_row).each do |i|
43
+ yield header, spreadsheet.row(i)
44
+ end
45
+ end
46
+
47
+ def attributes_from_header_and_row(header, row)
48
+ human_attributes = Hash[[header, row].transpose].to_hash
49
+ human_attributes_to_attributes(human_attributes)
50
+ end
51
+
52
+ def human_attributes_to_attributes(human_attributes)
53
+ comma_column_names.each.with_object({}) do |(key, _value), result|
54
+ human_key = Comma::HeaderExtractor.value_humanizer.call(key.to_sym, self)
55
+ result[key.to_sym] = human_attributes[human_key] if human_attributes[human_key]
56
+ result
57
+ end
58
+ end
59
+
60
+ def comma_column_names(style = :default)
61
+ header_extractor_class = Comma::HeaderExtractor.dup
62
+ header_extractor_class.value_humanizer = -> (value, _model_class) { value.to_s }
63
+ extract_with(header_extractor_class, style)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -6,10 +6,10 @@
6
6
 
7
7
  <%= Comable.t('order_mailer.complete.introductions', store_name: current_store.name) %>
8
8
 
9
- <%- shipment_method = @order.shipment_method %>
10
- <%- if shipment_method %>
11
- <%= shipment_method.class.model_name.human %>:
12
- <%= shipment_method.name %>
9
+ <%- shipment = @order.shipment %>
10
+ <%- if shipment %>
11
+ <%= @order.class.human_attribute_name(:shipment_method) %>:
12
+ <%= shipment.name %>
13
13
  <%- end %>
14
14
 
15
15
  <%= @order.class.human_attribute_name(:ship_address) %>:
@@ -22,19 +22,20 @@
22
22
  <%= "#{@order.class.human_attribute_name(:code)}: #{@order.code}" %>
23
23
  <%= "#{@order.class.human_attribute_name(:completed_at)}: #{I18n.l @order.completed_at.to_date}" %>
24
24
 
25
- <%- @order.order_details.each do |order_detail| %>
26
- <%= name_with_quantity order_detail.name_with_sku, order_detail.quantity %>
27
- <%= number_to_currency order_detail.subtotal_price %>
25
+ <%- @order.order_items.each do |order_item| %>
26
+ <%= name_with_quantity order_item.name_with_sku, order_item.quantity %>
27
+ <%= number_to_currency order_item.subtotal_price %>
28
28
 
29
29
  <%- end %>
30
30
  ----------------------------------------------------------------------
31
31
 
32
32
  <%= "#{@order.class.human_attribute_name(:item_total_price)}: #{number_to_currency @order.item_total_price}" %>
33
+ <%= "#{@order.class.human_attribute_name(:payment_fee)}: #{number_to_currency @order.payment_fee}" %>
33
34
  <%= "#{@order.class.human_attribute_name(:shipment_fee)}: #{number_to_currency @order.shipment_fee}" %>
34
35
 
35
36
  <%= "#{@order.class.human_attribute_name(:total_price)}: #{number_to_currency @order.total_price}" %>
36
37
 
37
- <%= "#{@order.class.human_attribute_name(:payment_method)}: #{@order.payment_method.name}\n" if @order.payment_method -%>
38
+ <%= "#{@order.class.human_attribute_name(:payment_method)}: #{@order.payment.name}\n" if @order.payment -%>
38
39
 
39
40
  ======================================================================
40
41
 
@@ -0,0 +1,8 @@
1
+ # from https://github.com/comma-csv/comma/pull/50#issuecomment-22819269
2
+ Comma::HeaderExtractor.value_humanizer = lambda do |value, model_class|
3
+ if value.is_a?(String) || !model_class.respond_to?(:human_attribute_name)
4
+ Comma::HeaderExtractor::DEFAULT_VALUE_HUMANIZER.call(value, model_class)
5
+ else
6
+ model_class.human_attribute_name(value)
7
+ end
8
+ end