comable_core 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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