spree_marketplace 3.1.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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE +21 -0
  7. data/README.md +87 -0
  8. data/Rakefile +15 -0
  9. data/Versionfile +12 -0
  10. data/app/assets/javascripts/spree/backend/spree_marketplace.js.coffee +24 -0
  11. data/app/assets/javascripts/spree/frontend/spree_marketplace.js +2 -0
  12. data/app/assets/javascripts/spree/frontend/supplier_address.js.coffee +56 -0
  13. data/app/assets/stylesheets/spree/backend/spree_marketplace.css +3 -0
  14. data/app/assets/stylesheets/spree/frontend/spree_marketplace.css +10 -0
  15. data/app/controllers/spree/admin/marketplace_settings_controller.rb +19 -0
  16. data/app/controllers/spree/admin/supplier_bank_accounts_controller.rb +24 -0
  17. data/app/controllers/spree/suppliers_controller.rb +67 -0
  18. data/app/models/spree/marketplace_ability.rb +14 -0
  19. data/app/models/spree/marketplace_configuration.rb +27 -0
  20. data/app/models/spree/supplier_ability.rb +76 -0
  21. data/app/models/spree/supplier_bank_account.rb +15 -0
  22. data/app/models/spree/supplier_decorator.rb +55 -0
  23. data/app/overrides/spree/admin/shared/_configuration_menu/add_marketplace_settings.html.erb.deface +3 -0
  24. data/app/overrides/spree/admin/suppliers/edit/add_bank_accounts.html.erb.deface +14 -0
  25. data/app/overrides/spree/layouts/admin/add_stripe_js_to_head.html.erb.deface +6 -0
  26. data/app/overrides/spree/users/show/add_supplier_info.html.erb.deface +17 -0
  27. data/app/views/spree/admin/marketplace_settings/edit.html.erb +23 -0
  28. data/app/views/spree/admin/supplier_bank_accounts/new.html.erb +28 -0
  29. data/app/views/spree/suppliers/new.html.erb +29 -0
  30. data/config/locales/en.yml +14 -0
  31. data/config/locales/es.yml +30 -0
  32. data/config/routes.rb +9 -0
  33. data/db/migrate/20130424201333_create_supplier_bank_accounts.rb +12 -0
  34. data/db/migrate/20131209022116_convert_to_stripe.rb +6 -0
  35. data/lib/generators/spree_marketplace/install/install_generator.rb +31 -0
  36. data/lib/spree_marketplace.rb +4 -0
  37. data/lib/spree_marketplace/engine.rb +47 -0
  38. data/lib/spree_marketplace/factories.rb +29 -0
  39. data/script/rails +7 -0
  40. data/spec/features/admin/bank_accounts_spec.rb +50 -0
  41. data/spec/features/admin/products_spec.rb +225 -0
  42. data/spec/features/admin/properties_spec.rb +91 -0
  43. data/spec/features/admin/relations_spec.rb +31 -0
  44. data/spec/features/admin/return_authorizations_spec.rb +7 -0
  45. data/spec/features/admin/settings_spec.rb +29 -0
  46. data/spec/features/admin/suppliers_controller_spec.rb +43 -0
  47. data/spec/features/supplier_signup_spec.rb +134 -0
  48. data/spec/models/spree/marketplace_ability_spec.rb +32 -0
  49. data/spec/models/spree/supplier_ability_spec.rb +337 -0
  50. data/spec/models/spree/supplier_bank_account_spec.rb +13 -0
  51. data/spec/models/spree/supplier_spec.rb +7 -0
  52. data/spec/spec_helper.rb +85 -0
  53. data/spec/support/integration_helpers.rb +15 -0
  54. data/spree_marketplace.gemspec +90 -0
  55. metadata +391 -0
@@ -0,0 +1,76 @@
1
+ module Spree
2
+ class SupplierAbility
3
+ include CanCan::Ability
4
+
5
+ def initialize(user)
6
+ user ||= Spree.user_class.new
7
+
8
+ if user.supplier
9
+ if SpreeMarketplace::Engine.spree_digital_available?
10
+ # can [:admin, :manage], Spree::Digital, variant: { supplier_ids: user.supplier_id }
11
+ can [:admin, :manage], Spree::Digital do |digital|
12
+ digital.variant.supplier_ids.include?(user.supplier_id)
13
+ end
14
+ can :create, Spree::Digital
15
+ end
16
+ can [:admin, :manage], Spree::Image do |image|
17
+ image.viewable.product.supplier_ids.include?(user.supplier_id)
18
+ end
19
+ can :create, Spree::Image
20
+ if SpreeMarketplace::Engine.spree_group_price_available?
21
+ # can [:admin, :manage], Spree::GroupPrice, variant: { supplier_ids: user.supplier_id }
22
+ can [:admin, :manage], Spree::GroupPrice do |price|
23
+ price.variant.supplier_ids.include?(user.supplier_id)
24
+ end
25
+ end
26
+ if SpreeMarketplace::Engine.spree_related_products_available?
27
+ # can [:admin, :manage], Spree::Relation, relatable: { supplier_ids: user.supplier_id }
28
+ can [:admin, :manage], Spree::Relation do |relation|
29
+ relation.relatable.supplier_ids.include?(user.supplier_id)
30
+ end
31
+ end
32
+ # TODO: Want this to be inline like:
33
+ # can [:admin, :manage, :stock], Spree::Product, suppliers: { id: user.supplier_id }
34
+ can [:admin, :manage, :stock], Spree::Product do |product|
35
+ product.supplier_ids.include?(user.supplier_id)
36
+ end
37
+ can [:admin, :create, :index], Spree::Product
38
+ # can [:admin, :manage], Spree::ProductProperty, product: { supplier_ids: user.supplier_id }
39
+ can [:admin, :manage, :stock], Spree::ProductProperty do |property|
40
+ property.product.supplier_ids.include?(user.supplier_id)
41
+ end
42
+ can [:admin, :index, :read], Spree::Property
43
+ can [:admin, :read], Spree::Prototype
44
+ can [:admin, :manage, :read, :ready, :ship], Spree::Shipment, order: { state: 'complete' }, stock_location: { supplier_id: user.supplier_id }
45
+ can [:admin, :create, :update], :stock_items
46
+ can [:admin, :manage], Spree::StockItem, stock_location_id: user.supplier.stock_locations.pluck(:id)
47
+ can [:admin, :manage], Spree::StockLocation, supplier_id: user.supplier_id
48
+ can :create, Spree::StockLocation
49
+ can [:admin, :manage], Spree::StockMovement, stock_item: { stock_location_id: user.supplier.stock_locations.pluck(:id) }
50
+ can :create, Spree::StockMovement
51
+ can [:admin, :update], Spree::Supplier, id: user.supplier_id
52
+ # TODO: Want this to be inline like:
53
+ # can [:admin, :manage], Spree::Variant, supplier_ids: user.supplier_id
54
+ can [:admin, :create, :index], Spree::Variant
55
+ can [:admin, :manage], Spree::Variant do |variant|
56
+ variant.supplier_ids.include?(user.supplier_id)
57
+ end
58
+ end
59
+
60
+ if SpreeMarketplace::Config[:allow_signup]
61
+ can :create, Spree::Supplier
62
+ end
63
+
64
+ if SpreeMarketplace::Engine.ckeditor_available?
65
+ can :access, :ckeditor
66
+
67
+ can :create, Ckeditor::AttachmentFile
68
+ can [:read, :index, :destroy], Ckeditor::AttachmentFile, supplier_id: user.supplier_id
69
+
70
+ can :create, Ckeditor::Picture
71
+ can [:read, :index, :destroy], Ckeditor::Picture, supplier_id: user.supplier_id
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ class SupplierBankAccount < ActiveRecord::Base
3
+
4
+ attr_accessor :account_number, :routing_number
5
+
6
+ belongs_to :supplier
7
+
8
+ validates :country_iso, presence: true
9
+ validates :masked_number, presence: true
10
+ validates :name, presence: true
11
+ validates :supplier, presence: true
12
+ validates :token, presence: true, uniqueness: true
13
+
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ Spree::Supplier.class_eval do
2
+
3
+ attr_accessor :first_name, :last_name, :merchant_type
4
+
5
+ has_many :bank_accounts, class_name: 'Spree::SupplierBankAccount'
6
+ has_attached_file :profile_picture, dependent: :destroy, :styles => {:medium => "300x300>", :thumb => "100x100>"}, :path => ":rails_root/public/assets/profile_pictures/:style/:filename", :url => "/assets/profile_pictures/:style/:filename"
7
+ validates_attachment :profile_picture, :content_type => { :content_type => /\Aimage\/.*\Z/ }, :size => { :in => 0..500.kilobytes }
8
+
9
+ validates :tax_id, length: { is: 9, allow_blank: true }
10
+
11
+ before_create :assign_name
12
+
13
+ private
14
+
15
+ def assign_name
16
+ self.address = Spree::Address.default unless self.address.present?
17
+ self.address.first_name = self.first_name unless self.address.first_name.present?
18
+ self.address.last_name = self.last_name unless self.address.last_name.present?
19
+ end
20
+
21
+ def stripe_recipient_setup
22
+ return if self.tax_id.blank? and self.address.blank?
23
+
24
+ recipient = Stripe::Recipient.create(
25
+ :name => (self.merchant_type == 'business' ? self.name : "#{self.address.first_name} #{self.address.last_name}"),
26
+ :type => (self.merchant_type == 'business' ? 'corporation' : "individual"),
27
+ :email => self.email,
28
+ :bank_account => self.bank_accounts.first.try(:token)
29
+ )
30
+
31
+ if new_record?
32
+ self.token = recipient.id
33
+ else
34
+ self.update_column :token, recipient.id
35
+ end
36
+ end
37
+
38
+ def stripe_recipient_update
39
+ unless new_record? or !changed?
40
+ if token.present?
41
+ rp = Stripe::Recipient.retrieve(token)
42
+ rp.name = name
43
+ rp.email = email
44
+ if tax_id.present?
45
+ rp.tax_id = tax_id
46
+ end
47
+ rp.bank_account = bank_accounts.first.token if bank_accounts.first
48
+ rp.save
49
+ else
50
+ stripe_recipient_setup
51
+ end
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,3 @@
1
+ <!-- insert_bottom "[data-hook='admin_configurations_sidebar_menu'], #admin_configurations_sidebar_menu[data-hook]" -->
2
+
3
+ <%= configurations_sidebar_menu_item Spree.t(:marketplace_settings), spree.edit_admin_marketplace_settings_path %>
@@ -0,0 +1,14 @@
1
+ <!-- insert_bottom "[data-hook='supplier_right_column_wrapper']" -->
2
+
3
+ <fieldset class="no-border-bottom supplier-banks">
4
+ <legend><%= I18n.t('.bank_accounts') %></legend>
5
+ <ol>
6
+ <% @supplier.bank_accounts.each do |bank_account| %>
7
+ <li>
8
+ <%= bank_account.name %> - <%= bank_account.masked_number %>
9
+ <!-- TODO Add a remove link? -->
10
+ </li>
11
+ <% end %>
12
+ </ol>
13
+ <%= button_link_to t('.add_bank_account'), spree.new_admin_supplier_bank_account_path(@supplier) %>
14
+ </fieldset>
@@ -0,0 +1,6 @@
1
+ <!-- insert_bottom 'head' -->
2
+
3
+ <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
4
+ <script type="text/javascript">
5
+ Stripe.setPublishableKey("<%= SpreeMarketplace::Config[:stripe_publishable_key] %>");
6
+ </script>
@@ -0,0 +1,17 @@
1
+ <!-- insert_bottom '#user-info' -->
2
+
3
+ <% if SpreeMarketplace::Config[:allow_signup] or @user.supplier? %>
4
+ <br/>
5
+ <dt class='supplier-info'><%= Spree.t(:supplier_info) %></dt>
6
+ <dd class='supplier-info'>
7
+ <% if @user.supplier? %>
8
+ <%= @user.supplier.name %>
9
+ (<%= link_to Spree.t(:edit), spree.edit_admin_supplier_path(@user.supplier) %>)
10
+ <%= link_to @user.supplier.url, @user.supplier.url, target: '_blank' if @user.supplier.url.present? %>
11
+ <br/>
12
+ Shipments: <b><%= @user.supplier.shipments.size %></b> (<%= link_to Spree.t(:manage), spree.admin_shipments_path %>)
13
+ <% else %>
14
+ <%= link_to Spree.t(:signup_to_become_a_supplier), spree.new_supplier_path %>
15
+ <% end %>
16
+ </dd>
17
+ <% end %>
@@ -0,0 +1,23 @@
1
+ <%= render partial: 'spree/admin/shared/configuration_menu' %>
2
+
3
+ <% content_for :page_title do %>
4
+ <%= Spree.t(:marketplace_settings) %>
5
+ <% end %>
6
+
7
+ <%= form_tag(spree.admin_marketplace_settings_path, method: :put) do %>
8
+ <fieldset class="general no-border-top">
9
+ <% %w(allow_signup ).each do |key|
10
+ type = SpreeMarketplace::Config.preference_type(key) %>
11
+ <div class="field">
12
+ <%= label_tag(key, Spree.t(key) + ': ') + tag(:br) if type != :boolean %>
13
+ <%= preference_field_tag(key, SpreeMarketplace::Config[key], :type => type) %>
14
+ <%= label_tag(key, Spree.t(key)) + tag(:br) if type == :boolean %>
15
+ </div>
16
+ <% end %>
17
+ <div class="form-buttons filter-actions actions" data-hook="buttons">
18
+ <%= button Spree.t(:update), 'fa fa-refresh' %>
19
+ <span class="or"><%= Spree.t(:or) %></span>
20
+ <%= link_to_with_icon 'remove', Spree.t(:cancel), spree.edit_admin_marketplace_settings_url, :class => 'button' %>
21
+ </div>
22
+ </fieldset>
23
+ <% end %>
@@ -0,0 +1,28 @@
1
+ <% content_for :page_actions do %>
2
+ <li><%= button_link_to Spree.t(:cancel), spree.edit_admin_supplier_path(@supplier), :icon => 'icon-arrow-left' %></li>
3
+ <% end %>
4
+
5
+ <% content_for :page_title do %>
6
+ <%= I18n.t '.new_bank_account' %>
7
+ <% end %>
8
+
9
+ <div id='stripeError' class='errorExplanation' style='display:none;'></div>
10
+
11
+ <%= form_for @object, url: spree.admin_supplier_bank_accounts_path(@supplier) do |f| %>
12
+ <%= f.field_container :account_number do %>
13
+ <%= f.label :account_number %>:<br />
14
+ <%= f.text_field :account_number %>
15
+ <% end %>
16
+ <%= f.field_container :routing_number do %>
17
+ <%= f.label :routing_number %>:<br />
18
+ <%= f.text_field :routing_number %>
19
+ <% end %>
20
+ <%= f.field_container :country_iso do %>
21
+ <%= f.label :country_iso, Spree.t(:country) %>:<br />
22
+ <%= f.collection_select :country_iso, available_countries, :iso, :name, {selected: 'US'}, {class: 'select2'} %>
23
+ <% end %>
24
+ <%= f.hidden_field :masked_number %>
25
+ <%= f.hidden_field :name %>
26
+ <%= f.hidden_field :token %>
27
+ <%= f.submit Spree.t(:save) %>
28
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <% content_for :head do -%>
2
+ <meta name="title" content="<%= Spree.t(:supplier_signup) %>">
3
+ <% end -%>
4
+ <%= render 'spree/shared/error_messages', :target => @supplier %>
5
+
6
+ <div class="col-md-6 col-md-offset-3">
7
+ <div class="panel panel-default">
8
+ <div class="panel-heading">
9
+ <h1><%= Spree.t(:supplier_signup) %></h1>
10
+ </div>
11
+ <div class="panel">
12
+ <%= render :partial => 'spree/signup_text' %>
13
+ </div>
14
+ <div id="new-supplier" class="panel-body" data-hook="supplier_fieldset_wrapper">
15
+ <%= form_for @supplier do |form| %>
16
+ <div data-hook="signup_inside_form">
17
+ <%= render :partial => 'spree/shared/supplier_form', :locals => { :form => form } %>
18
+ <p>
19
+ <%= form.submit Spree.t(:signup), :class => 'btn btn-lg btn-success btn-block' %>
20
+ </p>
21
+ </div>
22
+ <% end %>
23
+ <div class="text-center">
24
+ <%= Spree.t('or') %> <%= link_to Spree.t('cancel'), spree.account_path %>
25
+ </div>
26
+ <div data-hook="login_extras"></div>
27
+ </div>
28
+ </div>
29
+ </div>
@@ -0,0 +1,14 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ add_bank_account: Add Bank Account
6
+ bank_accounts: Bank Accounts
7
+ new_bank_account: New Bank Account
8
+ spree:
9
+ admin:
10
+ marketplace_settings:
11
+ update:
12
+ success: Marketplace settings successfully updated.
13
+ contacts_first_name: "Contact's First Name"
14
+ contacts_last_name: "Contact's Last Name"
@@ -0,0 +1,30 @@
1
+ ---
2
+ es:
3
+ bank_accounts: "Cuentas bancarias"
4
+ add_bank_account: "Añadir cuenta bancaria"
5
+ new_bank_account: "Nueva cuenta bancaria"
6
+ spree:
7
+ supplier_signup: "Registro de proveedores"
8
+ name: "Nombre de la marca o la empresa"
9
+ password: "Contraseña"
10
+ password_confirmation: "Contraseña (otra vez)"
11
+ brand: "Nombre de la marca"
12
+ profile_picture: "Imagen de perfil"
13
+ admin:
14
+ marketplace_settings:
15
+ update:
16
+ contacts_first_name: "Nombre (persona de contacto)"
17
+ contacts_last_name: "Apellidos (persona de contacto)"
18
+ tax_id: "Número de identificación fiscal: CIF, NIF o equivalente"
19
+ success: "Ajustes de marketplace actualizados con éxito."
20
+ signup: "Enviar"
21
+ save: "Guardar"
22
+ supplier_since: "Miembro desde hace"
23
+ seconds_ago: "segundos"
24
+ minutes_ago: "minutos"
25
+ hours_ago: "horas"
26
+ days_ago: "días"
27
+ months_ago: "meses"
28
+ years_ago: "años"
29
+ details: "Detalles"
30
+ profile_picture: "Imagen de perfil"
@@ -0,0 +1,9 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ namespace :admin do
3
+ resource :marketplace_settings
4
+ resources :suppliers do
5
+ resources :bank_accounts, controller: 'supplier_bank_accounts'
6
+ end
7
+ end
8
+ resources :suppliers, only: [:create, :new, :index, :show]
9
+ end
@@ -0,0 +1,12 @@
1
+ class CreateSupplierBankAccounts < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_supplier_bank_accounts do |t|
4
+ t.string :masked_number
5
+ t.belongs_to :supplier
6
+ t.string :token
7
+ t.timestamps
8
+ end
9
+ add_index :spree_supplier_bank_accounts, :supplier_id
10
+ add_index :spree_supplier_bank_accounts, :token
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class ConvertToStripe < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_supplier_bank_accounts, :country_iso, :integer
4
+ add_column :spree_supplier_bank_accounts, :name, :string
5
+ end
6
+ end
@@ -0,0 +1,31 @@
1
+ module SpreeMarketplace
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, :type => :boolean, :default => false
6
+
7
+ def add_javascripts
8
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/spree_marketplace\n"
9
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/spree_marketplace\n"
10
+ end
11
+
12
+ def add_stylesheets
13
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/spree_marketplace\n", :before => /\*\//, :verbose => true
14
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/spree_marketplace\n", :before => /\*\//, :verbose => true
15
+ end
16
+
17
+ def add_migrations
18
+ run 'bundle exec rake railties:install:migrations FROM=spree_marketplace'
19
+ end
20
+
21
+ def run_migrations
22
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
23
+ if run_migrations
24
+ run 'bundle exec rake db:migrate'
25
+ else
26
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ require 'stripe'
2
+ require 'spree_core'
3
+ require 'spree_drop_ship'
4
+ require 'spree_marketplace/engine'
@@ -0,0 +1,47 @@
1
+ module SpreeMarketplace
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace Spree
5
+ engine_name 'spree_marketplace'
6
+
7
+ config.autoload_paths += %W(#{config.root}/lib)
8
+
9
+ # use rspec for tests
10
+ config.generators do |g|
11
+ g.test_framework :rspec
12
+ end
13
+
14
+ initializer "spree_marketplace.preferences", before: :load_config_initializers do |app|
15
+ SpreeMarketplace::Config = Spree::MarketplaceConfiguration.new
16
+ end
17
+
18
+ config.after_initialize do
19
+ Stripe.api_key = SpreeMarketplace::Config[:stripe_secret_key]
20
+ end
21
+
22
+ def self.activate
23
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
24
+ Rails.configuration.cache_classes ? require(c) : load(c)
25
+ end
26
+ Spree::Ability.register_ability(Spree::MarketplaceAbility)
27
+ end
28
+
29
+ def self.ckeditor_available?
30
+ @@ckeditor_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('Ckeditor::Engine')
31
+ end
32
+
33
+ def self.spree_digital_available?
34
+ @@spree_digital_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('SpreeDigital::Engine')
35
+ end
36
+
37
+ def self.spree_group_price_available?
38
+ @@spree_group_price_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('SpreeGroupPricing::Engine')
39
+ end
40
+
41
+ def self.spree_related_products_available?
42
+ @@spree_related_procues_available ||= ::Rails::Engine.subclasses.map(&:instance).map{ |e| e.class.to_s }.include?('SpreeRelatedProducts::Engine')
43
+ end
44
+
45
+ config.to_prepare &method(:activate).to_proc
46
+ end
47
+ end