shoppe 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/app/assets/stylesheets/shoppe/application.scss +15 -4
- data/app/controllers/shoppe/application_controller.rb +2 -2
- data/app/controllers/shoppe/customers_controller.rb +10 -12
- data/app/models/shoppe/address.rb +14 -14
- data/app/models/shoppe/attachment.rb +9 -9
- data/app/models/shoppe/country.rb +14 -14
- data/app/models/shoppe/customer.rb +11 -8
- data/app/models/shoppe/delivery_service.rb +10 -10
- data/app/models/shoppe/delivery_service_price.rb +15 -15
- data/app/models/shoppe/order.rb +12 -9
- data/app/models/shoppe/order/actions.rb +15 -15
- data/app/models/shoppe/order/billing.rb +10 -10
- data/app/models/shoppe/order/delivery.rb +13 -13
- data/app/models/shoppe/order/states.rb +20 -20
- data/app/models/shoppe/order_item.rb +10 -10
- data/app/models/shoppe/payment.rb +7 -7
- data/app/models/shoppe/product.rb +24 -24
- data/app/models/shoppe/product/product_attributes.rb +5 -5
- data/app/models/shoppe/product/variants.rb +3 -3
- data/app/models/shoppe/product_attribute.rb +20 -20
- data/app/models/shoppe/product_category.rb +6 -6
- data/app/models/shoppe/setting.rb +13 -13
- data/app/models/shoppe/stock_level_adjustment.rb +5 -5
- data/app/models/shoppe/tax_rate.rb +16 -16
- data/app/models/shoppe/user.rb +13 -13
- data/app/uploaders/shoppe/attachment_uploader.rb +0 -1
- data/app/views/shoppe/customers/_addresses.html.haml +5 -5
- data/app/views/shoppe/customers/_form.html.haml +17 -10
- data/app/views/shoppe/customers/_search_form.html.haml +5 -5
- data/app/views/shoppe/customers/edit.html.haml +4 -4
- data/app/views/shoppe/customers/index.html.haml +11 -11
- data/app/views/shoppe/customers/new.html.haml +3 -3
- data/app/views/shoppe/customers/show.html.haml +11 -12
- data/app/views/shoppe/product_categories/index.html.haml +3 -2
- data/app/views/shoppe/variants/form.html.haml +0 -1
- data/config/locales/de.yml +58 -7
- data/config/locales/en.yml +38 -4
- data/config/locales/es-US.yml +656 -0
- data/config/locales/es.yml +372 -281
- data/config/locales/ru.yml +781 -0
- data/db/seeds.rb +113 -98
- data/lib/shoppe.rb +10 -10
- data/lib/shoppe/version.rb +1 -1
- metadata +22 -6
@@ -13,25 +13,25 @@ module Shoppe
|
|
13
13
|
self.table_name = 'shoppe_product_categories'
|
14
14
|
|
15
15
|
# Attachments for this product category
|
16
|
-
has_many :attachments, :
|
16
|
+
has_many :attachments, as: :parent, dependent: :destroy, class_name: "Shoppe::Attachment"
|
17
17
|
|
18
18
|
# All products within this category
|
19
19
|
has_many :product_categorizations, dependent: :restrict_with_exception, class_name: 'Shoppe::ProductCategorization', inverse_of: :product_category
|
20
20
|
has_many :products, class_name: 'Shoppe::Product', through: :product_categorizations
|
21
21
|
|
22
22
|
# Validations
|
23
|
-
validates :name, :
|
24
|
-
validates :permalink, :
|
23
|
+
validates :name, presence: true
|
24
|
+
validates :permalink, presence: true, uniqueness: { scope: :parent_id }, permalink: true
|
25
25
|
|
26
26
|
# Root (no parent) product categories only
|
27
27
|
scope :without_parent, -> { where(parent_id: nil) }
|
28
28
|
|
29
|
-
# No
|
29
|
+
# No descendants
|
30
30
|
scope :except_descendants, ->(record) { where.not(id: (Array.new(record.descendants) << record).flatten) }
|
31
31
|
|
32
32
|
translates :name, :permalink, :description
|
33
33
|
scope :ordered, -> { includes(:translations).order(:name) }
|
34
|
-
|
34
|
+
|
35
35
|
# Set the permalink on callback
|
36
36
|
before_validation :set_permalink, :set_ancestral_permalink
|
37
37
|
after_save :set_child_permalinks
|
@@ -56,7 +56,7 @@ module Shoppe
|
|
56
56
|
end
|
57
57
|
|
58
58
|
# Attachment with the role image
|
59
|
-
#
|
59
|
+
#
|
60
60
|
# @return [String]
|
61
61
|
def image
|
62
62
|
self.attachments.for("image")
|
@@ -2,17 +2,17 @@ require 'ostruct'
|
|
2
2
|
|
3
3
|
module Shoppe
|
4
4
|
class Setting < ActiveRecord::Base
|
5
|
-
|
5
|
+
|
6
6
|
# Validations
|
7
|
-
validates :key, :
|
8
|
-
validates :value, :
|
9
|
-
validates :value_type, :
|
10
|
-
|
7
|
+
validates :key, presence: true, uniqueness: true
|
8
|
+
validates :value, presence: true
|
9
|
+
validates :value_type, presence: true
|
10
|
+
|
11
11
|
before_validation do
|
12
12
|
self.value_type = I18n.t("shoppe.settings.types")[self.key.to_sym].try(:capitalize) || self.value.class.to_s
|
13
13
|
self.value = encoded_value
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
# The encoded value for saving in the backend (as a string)
|
17
17
|
#
|
18
18
|
# @return [String]
|
@@ -23,7 +23,7 @@ module Shoppe
|
|
23
23
|
else value.to_s
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# The decoded value for the setting attribute (in it's native type)
|
28
28
|
#
|
29
29
|
# @return [Object]
|
@@ -36,7 +36,7 @@ module Shoppe
|
|
36
36
|
else value.to_s
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
# A full hash of all settings available in the current scope
|
41
41
|
#
|
42
42
|
# @return [Hash]
|
@@ -46,8 +46,8 @@ module Shoppe
|
|
46
46
|
h
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
50
|
-
# Update settings from a given hash and persist them. Accepts a
|
49
|
+
|
50
|
+
# Update settings from a given hash and persist them. Accepts a
|
51
51
|
# hash of keys (which should be strings).
|
52
52
|
#
|
53
53
|
# @return [Hash]
|
@@ -56,13 +56,13 @@ module Shoppe
|
|
56
56
|
hash.each do |key, value|
|
57
57
|
existing = existing_settings.select { |s| s.key.to_s == key.to_s }.first
|
58
58
|
if existing
|
59
|
-
value.blank? ? existing.destroy! : existing.update!(:
|
59
|
+
value.blank? ? existing.destroy! : existing.update!(value: value)
|
60
60
|
else
|
61
|
-
value.blank? ? nil : self.create!(:
|
61
|
+
value.blank? ? nil : self.create!(key: key, value: value)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
hash
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
end
|
68
68
|
end
|
@@ -2,18 +2,18 @@ module Shoppe
|
|
2
2
|
class StockLevelAdjustment < ActiveRecord::Base
|
3
3
|
|
4
4
|
# The orderable item which the stock level adjustment belongs to
|
5
|
-
belongs_to :item, :
|
5
|
+
belongs_to :item, polymorphic: true
|
6
6
|
|
7
7
|
# The parent (OrderItem) which the stock level adjustment belongs to
|
8
|
-
belongs_to :parent, :
|
8
|
+
belongs_to :parent, polymorphic: true
|
9
9
|
|
10
10
|
# Validations
|
11
|
-
validates :description, :
|
12
|
-
validates :adjustment, :
|
11
|
+
validates :description, presence: true
|
12
|
+
validates :adjustment, numericality: true
|
13
13
|
validate { errors.add(:adjustment, I18n.t('shoppe.activerecord.attributes.stock_level_adjustment.must_be_greater_or_equal_zero')) if adjustment == 0 }
|
14
14
|
|
15
15
|
# All stock level adjustments ordered by their created date desending
|
16
|
-
scope :ordered, -> { order(:
|
16
|
+
scope :ordered, -> { order(id: :desc) }
|
17
17
|
|
18
18
|
end
|
19
19
|
end
|
@@ -1,37 +1,37 @@
|
|
1
1
|
module Shoppe
|
2
2
|
class TaxRate < ActiveRecord::Base
|
3
|
-
|
3
|
+
|
4
4
|
self.table_name = 'shoppe_tax_rates'
|
5
|
-
|
5
|
+
|
6
6
|
include Shoppe::AssociatedCountries
|
7
|
-
|
7
|
+
|
8
8
|
# The order address types which may be used when choosing how to apply the tax rate
|
9
9
|
ADDRESS_TYPES = ['billing', 'delivery']
|
10
|
-
|
10
|
+
|
11
11
|
# Validations
|
12
|
-
validates :name, :
|
13
|
-
validates :address_type, :
|
14
|
-
validates :rate, :
|
15
|
-
|
12
|
+
validates :name, presence: true
|
13
|
+
validates :address_type, inclusion: {in: ADDRESS_TYPES}
|
14
|
+
validates :rate, numericality: true
|
15
|
+
|
16
16
|
# All products which are assigned to this tax rate
|
17
|
-
has_many :products, :
|
18
|
-
|
17
|
+
has_many :products, dependent: :restrict_with_exception, class_name: 'Shoppe::Product'
|
18
|
+
|
19
19
|
# All delivery service prices which are assigned to this tax rate
|
20
|
-
has_many :delivery_service_prices, :
|
21
|
-
|
20
|
+
has_many :delivery_service_prices, dependent: :restrict_with_exception, class_name: 'Shoppe::DeliveryServicePrice'
|
21
|
+
|
22
22
|
# All tax rates ordered by their ID
|
23
23
|
scope :ordered, -> { order(:id)}
|
24
|
-
|
24
|
+
|
25
25
|
# Set the address type if appropriate
|
26
26
|
before_validation { self.address_type = ADDRESS_TYPES.first if self.address_type.blank? }
|
27
|
-
|
27
|
+
|
28
28
|
# A description of the tax rate including its name & percentage
|
29
29
|
#
|
30
30
|
# @return [String]
|
31
31
|
def description
|
32
32
|
"#{name} (#{rate}%)"
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
# The rate for a given order based on the rules on the tax rate
|
36
36
|
#
|
37
37
|
# @return [BigDecimal]
|
@@ -41,6 +41,6 @@ module Shoppe
|
|
41
41
|
return rate if address_type == 'delivery' && (order.delivery_country.nil? || country?(order.delivery_country))
|
42
42
|
BigDecimal(0)
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
end
|
46
46
|
end
|
data/app/models/shoppe/user.rb
CHANGED
@@ -2,48 +2,48 @@ module Shoppe
|
|
2
2
|
class User < ActiveRecord::Base
|
3
3
|
|
4
4
|
self.table_name = 'shoppe_users'
|
5
|
-
|
5
|
+
|
6
6
|
has_secure_password
|
7
|
-
|
7
|
+
|
8
8
|
# Validations
|
9
|
-
validates :first_name, :
|
10
|
-
validates :last_name, :
|
11
|
-
validates :email_address, :
|
12
|
-
|
9
|
+
validates :first_name, presence: true
|
10
|
+
validates :last_name, presence: true
|
11
|
+
validates :email_address, presence: true
|
12
|
+
|
13
13
|
# The user's first name & last name concatenated
|
14
14
|
#
|
15
15
|
# @return [String]
|
16
16
|
def full_name
|
17
17
|
"#{first_name} #{last_name}"
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# The user's first name & initial of last name concatenated
|
21
21
|
#
|
22
22
|
# @return [String]
|
23
23
|
def short_name
|
24
24
|
"#{first_name} #{last_name[0,1]}"
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# Reset the user's password to something random and e-mail it to them
|
28
28
|
def reset_password!
|
29
29
|
self.password = SecureRandom.hex(8)
|
30
30
|
self.password_confirmation = self.password
|
31
31
|
self.save!
|
32
|
-
Shoppe::UserMailer.new_password(self).
|
32
|
+
Shoppe::UserMailer.new_password(self).deliver
|
33
33
|
end
|
34
|
-
|
35
|
-
# Attempt to authenticate a user based on email & password. Returns the
|
34
|
+
|
35
|
+
# Attempt to authenticate a user based on email & password. Returns the
|
36
36
|
# user if successful otherwise returns false.
|
37
37
|
#
|
38
38
|
# @param email_address [String]
|
39
39
|
# @param paassword [String]
|
40
40
|
# @return [Shoppe::User]
|
41
41
|
def self.authenticate(email_address, password)
|
42
|
-
user = self.where(:
|
42
|
+
user = self.where(email_address: email_address).first
|
43
43
|
return false if user.nil?
|
44
44
|
return false unless user.authenticate(password)
|
45
45
|
user
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
end
|
49
49
|
end
|
@@ -3,18 +3,18 @@
|
|
3
3
|
%table.data
|
4
4
|
%thead
|
5
5
|
%tr
|
6
|
-
%th
|
7
|
-
%th
|
8
|
-
%th
|
6
|
+
%th= t('shoppe.customers.type')
|
7
|
+
%th= t('shoppe.customers.default')
|
8
|
+
%th= t('shoppe.custoemrs.address')
|
9
9
|
%th
|
10
10
|
%tbody
|
11
11
|
- if @addresses.empty?
|
12
12
|
%tr.empty
|
13
|
-
%td{:colspan => 4}
|
13
|
+
%td{:colspan => 4}= t('shoppe.customers.no_addresses')
|
14
14
|
- else
|
15
15
|
- for address in @addresses
|
16
16
|
%tr
|
17
17
|
%td= address.address_type.capitalize
|
18
18
|
%td= boolean_tag address.default
|
19
19
|
%td= address.full_address
|
20
|
-
%td= link_to
|
20
|
+
%td= link_to t('shoppe.customers.edit'), edit_customer_address_path(@customer, address)
|
@@ -1,30 +1,37 @@
|
|
1
1
|
= form_for @customer do |f|
|
2
2
|
= f.error_messages
|
3
3
|
|
4
|
-
= field_set_tag
|
4
|
+
= field_set_tag t('shoppe.customers.customer_information') do
|
5
5
|
.splitContainer
|
6
6
|
%dl.third
|
7
|
-
%dt= f.label :first_name
|
7
|
+
%dt= f.label :first_name, t('shoppe.customers.first_name')
|
8
8
|
%dd= f.text_field :first_name, :class => "text focus"
|
9
9
|
%dl.third
|
10
|
-
%dt= f.label :last_name
|
10
|
+
%dt= f.label :last_name, t('shoppe.customers.last_name')
|
11
11
|
%dd= f.text_field :last_name, :class => "text"
|
12
12
|
%dl.third
|
13
|
-
%dt= f.label :company
|
13
|
+
%dt= f.label :company, t('shoppe.customers.company')
|
14
14
|
%dd= f.text_field :company, :class => "text"
|
15
15
|
.splitContainer
|
16
16
|
%dl.third
|
17
|
-
%dt= f.label :email
|
17
|
+
%dt= f.label :email, t('shoppe.customers.email')
|
18
18
|
%dd= f.text_field :email, :class => "text"
|
19
19
|
%dl.third
|
20
|
-
%dt= f.label :phone
|
20
|
+
%dt= f.label :phone, t('shoppe.customers.phone')
|
21
21
|
%dd= f.text_field :phone, :class => "text"
|
22
22
|
%dl.third
|
23
|
-
%dt= f.label :mobile
|
23
|
+
%dt= f.label :mobile, t('shoppe.customers.mobile_phone')
|
24
24
|
%dd= f.text_field :mobile, :class => "text"
|
25
25
|
|
26
26
|
%p.submit
|
27
27
|
- unless @customer.new_record?
|
28
|
-
%span.right
|
29
|
-
|
30
|
-
|
28
|
+
%span.right
|
29
|
+
= link_to t('shoppe.customers.delete'),
|
30
|
+
@customer,
|
31
|
+
class: 'button purple',
|
32
|
+
method: :delete,
|
33
|
+
data: {confirm: t('shoppe.customers.delete_confirmation')}
|
34
|
+
= f.submit t('shoppe.customers.save'),
|
35
|
+
class: 'button green',
|
36
|
+
data: {disable_with: (@customer.new_record? ? t('shoppe.customers.creating_customer') : t('shoppe.customers.updating_customer'))}
|
37
|
+
= link_to t('shoppe.customers.cancel'), :customers, :class => 'button'
|
@@ -1,13 +1,13 @@
|
|
1
1
|
.customerSearch{:style => action_name == 'search' ? "display:block" : ''}
|
2
2
|
= search_form_for @query, :url => search_customers_path, :html => { :method => :post } do |f|
|
3
3
|
%dl.left
|
4
|
-
%dt= f.label :first_name_or_last_name_or_company_cont,
|
4
|
+
%dt= f.label :first_name_or_last_name_or_company_cont, t('shoppe.customers.first_or_last_name')
|
5
5
|
%dd= f.text_field :first_name_or_last_name_or_company_cont
|
6
|
-
%dt= f.label :company_cont,
|
6
|
+
%dt= f.label :company_cont, t('shoppe.customers.company')
|
7
7
|
%dd= f.text_field :company_cont
|
8
8
|
%dl.right
|
9
|
-
%dt= f.label :email_cont,
|
9
|
+
%dt= f.label :email_cont, t('shoppe.customers.email')
|
10
10
|
%dd= f.text_field :email_cont
|
11
|
-
%dt= f.label :phone_cont,
|
11
|
+
%dt= f.label :phone_cont, t('shoppe.customers.phone')
|
12
12
|
%dd= f.text_field :phone_cont
|
13
|
-
%dd= f.submit
|
13
|
+
%dd= f.submit t('shoppe.customers.search'), :class => 'button green button'
|
@@ -1,5 +1,5 @@
|
|
1
|
-
- @page_title =
|
1
|
+
- @page_title = t('shoppe.customers.customers')
|
2
2
|
= content_for :header do
|
3
|
-
%p.buttons= link_to
|
4
|
-
%h2.users
|
5
|
-
= render "form"
|
3
|
+
%p.buttons= link_to t('shoppe.customers.back_to_customers_list'), :customers, :class => 'button'
|
4
|
+
%h2.users= t('shoppe.customers.customers')
|
5
|
+
= render "form"
|
@@ -1,10 +1,10 @@
|
|
1
|
-
- @page_title =
|
1
|
+
- @page_title = t('shoppe.customers.customers')
|
2
2
|
|
3
3
|
= content_for :header do
|
4
4
|
%p.buttons
|
5
|
-
= link_to
|
6
|
-
= link_to
|
7
|
-
%h2.users
|
5
|
+
= link_to t('shoppe.customers.new_customer'), :new_customer, class: "button green"
|
6
|
+
= link_to t('shoppe.customers.search_customer'), '#', class: 'button', rel: "searchCustomers"
|
7
|
+
%h2.users= t('shoppe.customers.customers')
|
8
8
|
|
9
9
|
= render "search_form"
|
10
10
|
|
@@ -12,15 +12,15 @@
|
|
12
12
|
%table.data
|
13
13
|
%thead
|
14
14
|
%tr
|
15
|
-
%th
|
16
|
-
%th
|
17
|
-
%th
|
18
|
-
%th
|
19
|
-
%th
|
15
|
+
%th= t('shoppe.customers.name')
|
16
|
+
%th= t('shoppe.customers.company')
|
17
|
+
%th= t('shoppe.customers.email')
|
18
|
+
%th= t('shoppe.customers.phone')
|
19
|
+
%th= t('shoppe.customers.mobile_phone')
|
20
20
|
%tbody
|
21
21
|
- if @customers.empty?
|
22
22
|
%tr.empty
|
23
|
-
%td{colspan: 5}
|
23
|
+
%td{colspan: 5}= t('shoppe.customers.no_customers')
|
24
24
|
- else
|
25
25
|
- for customer in @customers
|
26
26
|
%tr
|
@@ -29,4 +29,4 @@
|
|
29
29
|
%td= customer.email
|
30
30
|
%td= customer.phone
|
31
31
|
%td= customer.mobile
|
32
|
-
= paginate @customers
|
32
|
+
= paginate @customers
|
@@ -1,5 +1,5 @@
|
|
1
|
-
- @page_title =
|
1
|
+
- @page_title = t('shoppe.customers.customers')
|
2
2
|
= content_for :header do
|
3
|
-
%p.buttons= link_to
|
3
|
+
%p.buttons= link_to t('shoppe.customers.back_to_customers_list'), :customers, :class => 'button'
|
4
4
|
%h2.users Customers
|
5
|
-
= render "form"
|
5
|
+
= render "form"
|
@@ -1,29 +1,28 @@
|
|
1
|
-
- @page_title = "
|
1
|
+
- @page_title = t('shoppe.customers.customers') + " - #{@customer.name}"
|
2
2
|
= content_for :header do
|
3
3
|
%p.buttons
|
4
|
-
= link_to
|
5
|
-
= link_to
|
6
|
-
%h2.users
|
7
|
-
Customer - #{@customer.name}
|
4
|
+
= link_to t('shoppe.customers.new_address'), [:new, @customer, :address], class: "button"
|
5
|
+
= link_to t('shoppe.customers.edit'), [:edit, @customer], class: "button"
|
6
|
+
%h2.users= t('shoppe.customers.customers') + "- #{@customer.name}"
|
8
7
|
|
9
8
|
#customer
|
10
9
|
.details
|
11
10
|
.left
|
12
11
|
%dl
|
13
|
-
%dt
|
12
|
+
%dt= t('shoppe.customers.name')
|
14
13
|
%dd= @customer.full_name
|
15
|
-
%dt
|
14
|
+
%dt= t('shoppe.customers.company')
|
16
15
|
%dd= @customer.company.blank? ? '-' : @customer.company
|
17
16
|
.right
|
18
17
|
%dl
|
19
|
-
%dt
|
18
|
+
%dt= t('shoppe.customers.email')
|
20
19
|
%dd= mail_to @customer.email
|
21
|
-
%dt
|
20
|
+
%dt= t('shoppe.customers.phone')
|
22
21
|
%dd= @customer.phone
|
23
|
-
%dt
|
22
|
+
%dt= t('shoppe.customers.mobile_phone')
|
24
23
|
%dd= @customer.mobile
|
25
24
|
|
26
|
-
= field_set_tag
|
25
|
+
= field_set_tag t('shoppe.customers.addresses'), :class => 'padded' do
|
27
26
|
= render "addresses"
|
28
27
|
|
29
28
|
= field_set_tag t('shoppe.orders.orders'), :class => 'padded' do
|
@@ -50,4 +49,4 @@
|
|
50
49
|
- for item in order.order_items
|
51
50
|
%li #{item.quantity} x #{item.ordered_item.full_name}
|
52
51
|
%td= number_to_currency order.total
|
53
|
-
%td= boolean_tag order.paid_in_full?, nil, :true_text => number_to_currency(order.amount_paid), :false_text => number_to_currency(order.amount_paid)
|
52
|
+
%td= boolean_tag order.paid_in_full?, nil, :true_text => number_to_currency(order.amount_paid), :false_text => number_to_currency(order.amount_paid)
|