stall 0.2.0 → 0.3.1

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 (140) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -23
  3. data/app/assets/javascripts/para/stall.coffee +1 -0
  4. data/app/assets/javascripts/para/stall/inputs/variant-select.coffee +62 -0
  5. data/app/assets/javascripts/para/stall/inputs/variants-matrix.coffee +12 -0
  6. data/app/assets/javascripts/para/stall/inputs/variants-matrix/helpers.coffee +40 -0
  7. data/app/assets/javascripts/para/stall/inputs/variants-matrix/input.coffee +133 -0
  8. data/app/assets/javascripts/para/stall/inputs/variants-matrix/nested-fields.coffee +38 -0
  9. data/app/assets/javascripts/para/stall/inputs/variants-matrix/properties_select.coffee +45 -0
  10. data/app/assets/javascripts/para/stall/inputs/variants-matrix/variant.coffee +59 -0
  11. data/app/assets/javascripts/stall.coffee +1 -0
  12. data/app/assets/javascripts/stall/add-to-cart-form.coffee +53 -28
  13. data/app/assets/javascripts/stall/cart-form.coffee +7 -2
  14. data/app/assets/stylesheets/para/stall.sass +28 -0
  15. data/app/controllers/para/stall/admin/carts_controller.rb +27 -0
  16. data/app/controllers/stall/cart_credits_controller.rb +27 -0
  17. data/app/controllers/stall/carts_controller.rb +1 -1
  18. data/app/controllers/stall/checkout/steps_controller.rb +22 -12
  19. data/app/controllers/stall/checkouts_controller.rb +1 -0
  20. data/app/controllers/stall/line_items_controller.rb +1 -0
  21. data/app/controllers/stall/payments_controller.rb +16 -1
  22. data/app/helpers/stall/credit_notes_helper.rb +25 -0
  23. data/app/helpers/stall/customers_helper.rb +3 -1
  24. data/app/models/billing_address.rb +2 -0
  25. data/app/models/cart_credit_note_adjustment.rb +3 -0
  26. data/app/models/credit_note.rb +3 -0
  27. data/app/models/credit_note_adjustment.rb +3 -0
  28. data/app/models/credit_note_usage.rb +3 -0
  29. data/app/models/product.rb +3 -0
  30. data/app/models/product_category.rb +3 -0
  31. data/app/models/product_detail.rb +3 -0
  32. data/app/models/property.rb +3 -0
  33. data/app/models/property_value.rb +3 -0
  34. data/app/models/shipping_address.rb +2 -0
  35. data/app/models/stall/models/address.rb +2 -2
  36. data/app/models/stall/models/cart.rb +2 -15
  37. data/app/models/stall/models/cart_credit_note_adjustment.rb +11 -0
  38. data/app/models/stall/models/credit_note.rb +50 -0
  39. data/app/models/stall/models/credit_note_adjustment.rb +13 -0
  40. data/app/models/stall/models/credit_note_usage.rb +16 -0
  41. data/app/models/stall/models/customer.rb +20 -8
  42. data/app/models/stall/models/line_item.rb +9 -0
  43. data/app/models/stall/models/product.rb +45 -0
  44. data/app/models/stall/models/product_category.rb +31 -0
  45. data/app/models/stall/models/product_detail.rb +17 -0
  46. data/app/models/stall/models/product_list.rb +9 -45
  47. data/app/models/stall/models/property.rb +20 -0
  48. data/app/models/stall/models/property_value.rb +23 -0
  49. data/app/models/stall/models/variant.rb +34 -0
  50. data/app/models/stall/models/variant_property_value.rb +18 -0
  51. data/app/models/variant.rb +3 -0
  52. data/app/models/variant_property_value.rb +3 -0
  53. data/app/services/stall/cart_credit_note_creation_service.rb +40 -0
  54. data/app/services/stall/cart_payment_validation_service.rb +28 -0
  55. data/app/services/stall/cart_update_service.rb +17 -6
  56. data/app/services/stall/credit_usage_service.rb +102 -0
  57. data/app/services/stall/payment_notification_service.rb +4 -8
  58. data/app/services/stall/product_list_staleness_handling_service.rb +33 -0
  59. data/app/views/admin/addresses/_fields.html.haml +9 -0
  60. data/app/views/admin/carts/_filters.html.haml +17 -0
  61. data/app/views/admin/carts/_form.html.haml +43 -0
  62. data/app/views/admin/carts/_table.html.haml +17 -0
  63. data/app/views/admin/customers/_fields.html.haml +1 -0
  64. data/app/views/admin/line_items/_fields.html.haml +15 -0
  65. data/app/views/admin/products/_table.html.haml +12 -0
  66. data/app/views/admin/properties/_form.html.haml +6 -0
  67. data/app/views/admin/properties/_table.html.haml +8 -0
  68. data/app/views/admin/property_values/_fields.html.haml +1 -0
  69. data/app/views/admin/shipments/_fields.html.haml +7 -0
  70. data/app/views/checkout/steps/_informations.html.haml +3 -1
  71. data/app/views/checkout/steps/_payment.html.haml +2 -0
  72. data/app/views/checkout/steps/_payment_return.html.haml +0 -1
  73. data/app/views/para/admin/resources/_variant_row.html.haml +24 -0
  74. data/app/views/para/stall/inputs/_variant_select.html.haml +14 -0
  75. data/app/views/para/stall/inputs/_variants_matrix.html.haml +41 -0
  76. data/app/views/stall/addresses/_fields.html.haml +6 -12
  77. data/app/views/stall/carts/_cart.html.haml +45 -37
  78. data/app/views/stall/carts/_widget.html.haml +28 -0
  79. data/app/views/stall/carts/show.html.haml +2 -0
  80. data/app/views/stall/checkout/steps/_navigation.html.haml +13 -0
  81. data/app/views/stall/credit_note_adjustments/_form.html.haml +28 -0
  82. data/app/views/stall/line_items/_added.html.haml +2 -2
  83. data/app/views/stall/line_items/_form.html.haml +1 -1
  84. data/app/views/stall/payments/manual_payment_gateway/_form.html.haml +10 -0
  85. data/app/views/stall/shared/mailers/_cart.html.haml +1 -1
  86. data/config/locales/stall.fr.yml +82 -2
  87. data/db/migrate/20161129101956_add_type_to_stall_address_ownerships.rb +52 -0
  88. data/db/migrate/20161202080218_add_reference_to_product_lists.rb +17 -0
  89. data/db/migrate/20170118103916_create_credit_notes.rb +17 -0
  90. data/db/migrate/20170118144047_create_credit_note_adjustments.rb +13 -0
  91. data/db/migrate/20170123123115_create_stall_product_categories.rb +12 -0
  92. data/db/migrate/20170123123326_create_stall_products.rb +17 -0
  93. data/db/migrate/20170123125030_create_stall_variants.rb +13 -0
  94. data/db/migrate/20170123131748_create_stall_product_category_hierarchies.rb +16 -0
  95. data/db/migrate/20170123143704_create_stall_product_details.rb +14 -0
  96. data/db/migrate/20170125152622_convert_all_money_fields_to_decimal_to_use_infinite_precision.rb +27 -0
  97. data/db/migrate/20170131162537_add_data_to_stall_adjustments.rb +5 -0
  98. data/db/migrate/20170202165514_create_stall_properties.rb +9 -0
  99. data/db/migrate/20170202165516_create_stall_property_values.rb +13 -0
  100. data/db/migrate/20170202165518_create_stall_variant_property_values.rb +13 -0
  101. data/lib/generators/stall/install/templates/initializer.rb +21 -0
  102. data/lib/generators/stall/view/view_generator.rb +41 -19
  103. data/lib/para/stall.rb +32 -0
  104. data/lib/para/stall/inputs.rb +13 -0
  105. data/lib/para/stall/inputs/variant_input_helper.rb +34 -0
  106. data/lib/para/stall/inputs/variant_select_input.rb +79 -0
  107. data/lib/para/stall/inputs/variants_matrix_input.rb +72 -0
  108. data/lib/para/stall/routes.rb +11 -0
  109. data/lib/para/stall/variants_property_config.rb +78 -0
  110. data/lib/stall.rb +10 -0
  111. data/lib/stall/addressable.rb +11 -59
  112. data/lib/stall/addresses.rb +1 -0
  113. data/lib/stall/addresses/copier_base.rb +3 -1
  114. data/lib/stall/addresses/copy.rb +10 -0
  115. data/lib/stall/addresses/copy_source_to_target.rb +10 -24
  116. data/lib/stall/addresses/prefill_target_from_source.rb +8 -16
  117. data/lib/stall/adjustable.rb +20 -0
  118. data/lib/stall/archived_paid_cart_helper.rb +36 -0
  119. data/lib/stall/cart_helper.rb +15 -7
  120. data/lib/stall/checkout/informations_checkout_step.rb +47 -50
  121. data/lib/stall/checkout/payment_return_checkout_step.rb +4 -1
  122. data/lib/stall/checkout/step.rb +24 -3
  123. data/lib/stall/checkout/step_form.rb +11 -5
  124. data/lib/stall/checkout/wizard.rb +7 -6
  125. data/lib/stall/config.rb +9 -0
  126. data/lib/stall/default_currency_manager.rb +27 -0
  127. data/lib/stall/engine.rb +14 -3
  128. data/lib/stall/payments.rb +2 -0
  129. data/lib/stall/payments/gateway_request.rb +15 -0
  130. data/lib/stall/payments/gateway_response.rb +33 -0
  131. data/lib/stall/payments/manual_payment_gateway.rb +86 -0
  132. data/lib/stall/priceable.rb +4 -0
  133. data/lib/stall/reference_manager.rb +17 -0
  134. data/lib/stall/routes.rb +1 -0
  135. data/lib/stall/shippable.rb +18 -0
  136. data/lib/stall/total_prices_manager.rb +40 -0
  137. data/lib/stall/version.rb +1 -1
  138. metadata +120 -5
  139. data/app/models/address_ownership.rb +0 -3
  140. data/app/models/stall/models/address_ownership.rb +0 -26
@@ -0,0 +1,3 @@
1
+ class CreditNote < ActiveRecord::Base
2
+ include Stall::Models::CreditNote
3
+ end
@@ -0,0 +1,3 @@
1
+ class CreditNoteAdjustment < Adjustment
2
+ include Stall::Models::CreditNoteAdjustment
3
+ end
@@ -0,0 +1,3 @@
1
+ class CreditNoteUsage < ActiveRecord::Base
2
+ include Stall::Models::CreditNoteUsage
3
+ end
@@ -0,0 +1,3 @@
1
+ class Product < ActiveRecord::Base
2
+ include Stall::Models::Product
3
+ end
@@ -0,0 +1,3 @@
1
+ class ProductCategory < ActiveRecord::Base
2
+ include Stall::Models::ProductCategory
3
+ end
@@ -0,0 +1,3 @@
1
+ class ProductDetail < ActiveRecord::Base
2
+ include Stall::Models::ProductDetail
3
+ end
@@ -0,0 +1,3 @@
1
+ class Property < ActiveRecord::Base
2
+ include Stall::Models::Property
3
+ end
@@ -0,0 +1,3 @@
1
+ class PropertyValue < ActiveRecord::Base
2
+ include Stall::Models::PropertyValue
3
+ end
@@ -0,0 +1,2 @@
1
+ class ShippingAddress < Address
2
+ end
@@ -6,7 +6,7 @@ module Stall
6
6
  included do
7
7
  self.table_name = 'stall_addresses'
8
8
 
9
- has_one :address_ownership, dependent: :destroy
9
+ belongs_to :addressable, polymorphic: true
10
10
 
11
11
  enum civility: { :m => 1, :mme => 2 }
12
12
 
@@ -17,7 +17,7 @@ module Stall
17
17
  end
18
18
 
19
19
  def civility_name
20
- I18n.t("stall.addresses.civilities.#{ civility }")
20
+ I18n.t("stall.addresses.civilities.#{ civility }") if civility.present?
21
21
  end
22
22
 
23
23
  def country_name
@@ -6,12 +6,8 @@ module Stall
6
6
  included do
7
7
  include Stall::Addressable
8
8
  include Stall::Payable
9
-
10
- has_one :shipment, dependent: :destroy, inverse_of: :cart
11
- accepts_nested_attributes_for :shipment
12
-
13
- has_many :adjustments, dependent: :destroy, inverse_of: :cart
14
- accepts_nested_attributes_for :adjustments
9
+ include Stall::Shippable
10
+ include Stall::Adjustable
15
11
 
16
12
  attr_accessor :terms
17
13
  end
@@ -25,15 +21,6 @@ module Stall
25
21
  def active?
26
22
  !paid?
27
23
  end
28
-
29
- private
30
-
31
- def items
32
- items = line_items.to_a
33
- items << shipment if shipment
34
- items += adjustments.to_a
35
- items
36
- end
37
24
  end
38
25
  end
39
26
  end
@@ -0,0 +1,11 @@
1
+ module Stall
2
+ module Models
3
+ module CartCreditNoteAdjustment
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_one :credit_note, as: :source, dependent: :nullify
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ module Stall
2
+ module Models
3
+ module CreditNote
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ self.table_name = 'stall_credit_notes'
8
+
9
+ include Stall::Priceable
10
+ include Stall::DefaultCurrencyManager
11
+ include Stall::ReferenceManager
12
+
13
+ monetize :eot_amount_cents, :amount_cents,
14
+ with_model_currency: :currency, allow_nil: true
15
+
16
+ belongs_to :customer
17
+ belongs_to :source, polymorphic: true
18
+
19
+ has_many :credit_note_usages, dependent: :destroy
20
+ has_many :adjustments, through: :credit_note_usages
21
+
22
+ validates :amount, :customer, presence: true
23
+
24
+ def amount_with_eot_management=(value)
25
+ (self.amount_without_eot_management = value).tap do
26
+ self.eot_amount = amount / vat_coefficient
27
+ end
28
+ end
29
+
30
+ # TODO : Check if we can use Module#prepend here without getting too
31
+ # complex
32
+ #
33
+ alias_method :amount_without_eot_management=, :amount=
34
+ alias_method :amount=, :amount_with_eot_management=
35
+ end
36
+
37
+ def remaining_amount
38
+ amount - adjustments.map(&:price).sum.abs
39
+ end
40
+
41
+ def vat_rate
42
+ read_attribute(:vat_rate) || write_attribute(:vat_rate, Stall.config.vat_rate)
43
+ end
44
+
45
+ def with_remaining_money?
46
+ remaining_amount.to_d > 0
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ module Stall
2
+ module Models
3
+ module CreditNoteAdjustment
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_one :credit_note_usage, foreign_key: :adjustment_id,
8
+ dependent: :destroy
9
+ has_one :credit_note, through: :credit_note_usage
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # Joint model between credit notes and adjustments
2
+ #
3
+ module Stall
4
+ module Models
5
+ module CreditNoteUsage
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ self.table_name = 'stall_credit_note_usages'
10
+
11
+ belongs_to :credit_note
12
+ belongs_to :adjustment, class_name: 'CreditNoteAdjustment'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -8,23 +8,35 @@ module Stall
8
8
 
9
9
  include Stall::Addressable
10
10
 
11
- belongs_to :user, polymorphic: true, inverse_of: :customer
12
- accepts_nested_attributes_for :user
11
+ if Stall.config.default_user_model
12
+ belongs_to :user, polymorphic: true, inverse_of: :customer
13
+ accepts_nested_attributes_for :user
13
14
 
14
- has_many :product_lists, dependent: :destroy
15
+ before_validation :ensure_user_email
16
+ end
15
17
 
16
- validates :email, format: { with: /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/ },
17
- allow_blank: true
18
+ has_many :product_lists, dependent: :destroy
19
+ has_many :credit_notes, dependent: :destroy
18
20
 
19
- before_validation :ensure_user_email
21
+ validates :email, presence: true,
22
+ format: { with: /\A[^@\s]+@([^@\s]+\.)+[^@\W]+\z/ }
20
23
 
21
24
  def user_or_default
22
25
  user || build_user
23
26
  end
24
27
 
25
28
  def build_user(attributes = {})
26
- attributes.reverse_merge!(customer: self)
27
- self.user = Stall.config.default_user_model.new(attributes)
29
+ (self.user = Stall.config.default_user_model.new(attributes)).tap do
30
+ user.customer = self if user.respond_to?(:customer)
31
+ end if Stall.config.default_user_model
32
+ end
33
+
34
+ def credit(currency = Stall.config.default_currency)
35
+ credit_notes.for_currency(currency).map(&:remaining_amount).sum
36
+ end
37
+
38
+ def credit?(currency = Stall.config.default_currency)
39
+ credit(currency).to_d > 0
28
40
  end
29
41
 
30
42
  private
@@ -27,6 +27,7 @@ module Stall
27
27
 
28
28
  validate :stock_availability
29
29
 
30
+ before_validation :restore_valid_quantity
30
31
  before_validation :refresh_total_prices
31
32
 
32
33
  scope :ordered, -> { order(created_at: :asc) }
@@ -52,6 +53,14 @@ module Stall
52
53
  self.eot_price = unit_eot_price * quantity if unit_eot_price && quantity
53
54
  self.price = unit_price * quantity if unit_price && quantity
54
55
  end
56
+
57
+ # Ensures that a quantity set to 0 to an existing line item doesn't return
58
+ # an error.
59
+ def restore_valid_quantity
60
+ if persisted? && quantity && (quantity < 1) && quantity_changed?
61
+ restore_quantity!
62
+ end
63
+ end
55
64
  end
56
65
  end
57
66
  end
@@ -0,0 +1,45 @@
1
+ module Stall
2
+ module Models
3
+ module Product
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ self.table_name = 'stall_products'
8
+
9
+ acts_as_orderable
10
+ extend FriendlyId
11
+ friendly_id :name, use: [:slugged, :finders]
12
+
13
+ belongs_to :product_category
14
+
15
+ has_many :variants, dependent: :destroy, inverse_of: :product
16
+ accepts_nested_attributes_for :variants, allow_destroy: true
17
+
18
+ has_many :product_details, dependent: :destroy, inverse_of: :product
19
+ accepts_nested_attributes_for :product_details, allow_destroy: true
20
+
21
+ has_attached_file :image, styles: {
22
+ thumb: '100x100#',
23
+ show: '555x'
24
+ }
25
+
26
+ validates :name, :image, presence: true
27
+ validates_attachment :image, content_type: { content_type: /\Aimage\/.*\z/ }
28
+
29
+ scope :visible, -> { where(visible: true) }
30
+
31
+ def should_generate_new_friendly_id?
32
+ slug.blank?
33
+ end
34
+
35
+ def vat_rate
36
+ Stall.config.vat_rate
37
+ end
38
+
39
+ def price
40
+ variants.map(&:price).min
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ module Stall
2
+ module Models
3
+ module ProductCategory
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ self.table_name = 'stall_product_categories'
8
+
9
+ acts_as_tree order: 'position'
10
+ class_attribute :max_depth
11
+
12
+ extend FriendlyId
13
+ friendly_id :name, use: [:slugged, :finders]
14
+
15
+ has_many :products, dependent: :nullify
16
+
17
+ validates :name, presence: true
18
+
19
+ scope :ordered, -> { order(position: 'asc') }
20
+
21
+ def self.max_depth
22
+ 2
23
+ end
24
+
25
+ def should_generate_new_friendly_id?
26
+ slug.blank?
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module Stall
2
+ module Models
3
+ module ProductDetail
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ self.table_name = 'stall_product_details'
8
+
9
+ acts_as_orderable
10
+
11
+ belongs_to :product
12
+
13
+ validates :name, presence: true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -6,7 +6,9 @@ module Stall
6
6
  included do
7
7
  self.table_name = 'stall_product_lists'
8
8
 
9
- store_accessor :data, :reference
9
+ include Stall::DefaultCurrencyManager
10
+ include Stall::ReferenceManager
11
+ include Stall::TotalPricesManager
10
12
 
11
13
  has_secure_token
12
14
 
@@ -16,13 +18,15 @@ module Stall
16
18
  belongs_to :customer
17
19
  accepts_nested_attributes_for :customer
18
20
 
21
+ has_many :generated_credit_notes, as: :source,
22
+ class_name: 'CreditNote',
23
+ dependent: :nullify
24
+
19
25
  validates :type, presence: true
20
26
 
21
- after_initialize :ensure_currency
22
27
  after_initialize :ensure_state
23
28
 
24
29
  before_save :save_customer_if_changed
25
- after_save :ensure_reference, on: :create
26
30
 
27
31
  scope :empty, -> {
28
32
  joins(
@@ -52,27 +56,11 @@ module Stall
52
56
  end
53
57
 
54
58
  def subtotal
55
- price = line_items.map(&:price).sum
56
- price = Money.new(price, currency) unless Money === price
57
- price
59
+ ensure_money(line_items.map(&:price).sum)
58
60
  end
59
61
 
60
62
  def eot_subtotal
61
- line_items.map(&:eot_price).sum
62
- end
63
-
64
- def total_price
65
- price = items.map(&:price).sum
66
- price = Money.new(price, currency) unless Money === price
67
- price
68
- end
69
-
70
- def total_eot_price
71
- items.map(&:eot_price).sum
72
- end
73
-
74
- def total_vat
75
- items.map(&:vat).sum
63
+ ensure_money(line_items.map(&:eot_price).sum)
76
64
  end
77
65
 
78
66
  def total_quantity
@@ -91,20 +79,8 @@ module Stall
91
79
  true
92
80
  end
93
81
 
94
- def currency
95
- @currency ||= if (currency = read_attribute(:currency).presence)
96
- Money::Currency.new(currency)
97
- else
98
- self.currency = Money.default_currency
99
- end
100
- end
101
-
102
82
  private
103
83
 
104
- def ensure_currency
105
- self.currency ||= Money.default_currency
106
- end
107
-
108
84
  def ensure_state
109
85
  self.state ||= (wizard.try(:steps).try(:first) || 'pending')
110
86
  end
@@ -113,23 +89,11 @@ module Stall
113
89
  line_items.to_a
114
90
  end
115
91
 
116
- def ensure_reference
117
- unless reference.present?
118
- reference = [Time.now.strftime('%Y%m%d'), ('%05d' % id)].join('-')
119
- self.reference = reference
120
- save(validate: false)
121
- end
122
- end
123
-
124
92
  def save_customer_if_changed
125
93
  customer.save if customer && customer.changed?
126
94
  end
127
95
 
128
96
  module ClassMethods
129
- def find_by_reference(reference)
130
- where("data->>'reference' = ?", reference).first
131
- end
132
-
133
97
  # The .aborted and .finalized scopes cannot be declared as actual rails
134
98
  # scopes since subclasses that override the .wizard method wouldn't
135
99
  # be taken into account, scopes being executed in the context of the