spree_channable 0.0.18.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +24 -0
  5. data/.travis.yml +35 -0
  6. data/Appraisals +27 -0
  7. data/Gemfile +17 -0
  8. data/LICENSE +29 -0
  9. data/README.md +56 -0
  10. data/Rakefile +21 -0
  11. data/app/assets/javascripts/spree/backend/spree_channable.js +2 -0
  12. data/app/assets/javascripts/spree/frontend/spree_channable.js +2 -0
  13. data/app/assets/stylesheets/spree/backend/spree_channable.css +4 -0
  14. data/app/assets/stylesheets/spree/frontend/spree_channable.css +4 -0
  15. data/app/controllers/spree/admin/channable_settings_controller.rb +14 -0
  16. data/app/controllers/spree/api/v1/channable_controller.rb +21 -0
  17. data/app/jobs/application_job.rb +7 -0
  18. data/app/jobs/spree_channable/order_import_job.rb +37 -0
  19. data/app/jobs/spree_channable/return_import_job.rb +36 -0
  20. data/app/models/channable_setting.rb +10 -0
  21. data/app/models/spree/order_decorator.rb +127 -0
  22. data/app/models/spree/product_decorator.rb +118 -0
  23. data/app/models/spree/reimbursement_decorator.rb +39 -0
  24. data/app/models/spree/shipment_decorator.rb +28 -0
  25. data/app/models/spree/shipment_handler_decorator.rb +11 -0
  26. data/app/models/spree/variant_decorator.rb +78 -0
  27. data/app/overrides/add_settings_to_admin_configurator.rb +32 -0
  28. data/app/overrides/order_index.rb +13 -0
  29. data/app/views/spree/admin/channable_settings/_form.html.erb +105 -0
  30. data/app/views/spree/admin/channable_settings/edit.html.erb +1 -0
  31. data/app/views/spree/admin/channable_settings/new.html.erb +1 -0
  32. data/app/views/spree/admin/orders/_index_channable_state_override.html.erb +7 -0
  33. data/app/views/spree/admin/orders/_index_channable_state_override_head.html.erb +1 -0
  34. data/bin/rails +8 -0
  35. data/config/locales/en.yml +5 -0
  36. data/config/routes.rb +18 -0
  37. data/db/migrate/20190710092345_create_channable_settings.rb +16 -0
  38. data/db/migrate/20190710103701_add_settings_to_settings_table.rb +9 -0
  39. data/db/migrate/20190710115409_add_order_columns.rb +9 -0
  40. data/db/migrate/20190711122425_set_order_connection_defaults.rb +5 -0
  41. data/db/migrate/20190711123402_add_channable_fields_to_shipping_methods.rb +8 -0
  42. data/db/migrate/20190711140015_add_payment_method_for_channable.rb +7 -0
  43. data/db/migrate/20190711145940_add_order_import_settings.rb +9 -0
  44. data/db/migrate/20190717120144_add_channable_id_to_return_authorisations.rb +5 -0
  45. data/gemfiles/spree_3_2.gemfile +10 -0
  46. data/gemfiles/spree_3_5.gemfile +10 -0
  47. data/gemfiles/spree_3_7.gemfile +10 -0
  48. data/gemfiles/spree_master.gemfile +10 -0
  49. data/lib/channable/client.rb +42 -0
  50. data/lib/channable/response.rb +15 -0
  51. data/lib/generators/spree_channable/install/install_generator.rb +20 -0
  52. data/lib/spree_channable/engine.rb +20 -0
  53. data/lib/spree_channable/factories.rb +6 -0
  54. data/lib/spree_channable/order_importer.rb +280 -0
  55. data/lib/spree_channable/return_importer.rb +63 -0
  56. data/lib/spree_channable/version.rb +18 -0
  57. data/lib/spree_channable.rb +36 -0
  58. data/spec/fixtures/invalid_channable_order.json +117 -0
  59. data/spec/fixtures/valid_channable_order.json +117 -0
  60. data/spree_channable.gemspec +50 -0
  61. metadata +451 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cc45055d9e1bef626d21f196471a45402d5c220cc05d0db9f7488630addc54a6
4
+ data.tar.gz: 0ef0d52b27cce58f5fe3edbc8fac265163b517ae4354c671513689d0dbe8ee3b
5
+ SHA512:
6
+ metadata.gz: c83193ff3364edc06054c6ecea2fe661c6d30de3af8f01aa4f95998b72d99dbcab4a633bce31c6736104519992a8443bd67696f7f38fce81f3e044b7913f717f
7
+ data.tar.gz: 43dac314d47fb2fdd16374b6a6ace86df645655accb6e63d9e20c1241b36b9fca52fac39fda740276a7ad3abd81d3437ed69ecd6b77f040e70069d8ebc09c2a6
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ \#*
2
+ *~
3
+ .#*
4
+ .DS_Store
5
+ .idea
6
+ .localeapp/locales
7
+ .project
8
+ .vscode
9
+ coverage
10
+ default
11
+ Gemfile.lock
12
+ tmp
13
+ nbproject
14
+ pkg
15
+ *.sw?
16
+ spec/dummy
17
+ .rvmrc
18
+ .sass-cache
19
+ public/spree
20
+ .ruby-version
21
+ .ruby-gemset
22
+ gemfiles/*.gemfile.lock
23
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ -r spec_helper
3
+ -f documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,24 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+ TargetRubyVersion: 2.3
4
+ Include:
5
+ - '**/Gemfile'
6
+ - '**/Rakefile'
7
+ - '**/Appraisals'
8
+ Exclude:
9
+ - 'spec/dummy/**/*'
10
+ - 'lib/generators/**/*'
11
+
12
+ Rails:
13
+ Enabled: true
14
+
15
+ Metrics/LineLength:
16
+ Max: 150
17
+
18
+ # DISABLED
19
+
20
+ Style/Documentation:
21
+ Enabled: false
22
+
23
+ Style/FrozenStringLiteralComment:
24
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,35 @@
1
+ sudo: required
2
+ dist: trusty
3
+
4
+ language: ruby
5
+
6
+ env:
7
+ - DB=postgres
8
+ - DB=mysql
9
+
10
+ gemfile:
11
+ - gemfiles/spree_3_2.gemfile
12
+ - gemfiles/spree_3_5.gemfile
13
+ - gemfiles/spree_3_7.gemfile
14
+ - gemfiles/spree_master.gemfile
15
+
16
+ script:
17
+ - bundle exec rake test_app
18
+ - bundle exec rake spec
19
+
20
+ rvm:
21
+ - 2.5.1
22
+ - 2.4.2
23
+
24
+ matrix:
25
+ allow_failures:
26
+ - gemfile: gemfiles/spree_master.gemfile
27
+
28
+ before_install:
29
+ - mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
30
+ - wget -N https://chromedriver.storage.googleapis.com/2.35/chromedriver_linux64.zip -P ~/
31
+ - unzip ~/chromedriver_linux64.zip -d ~/
32
+ - rm ~/chromedriver_linux64.zip
33
+ - sudo mv -f ~/chromedriver /usr/local/share/
34
+ - sudo chmod +x /usr/local/share/chromedriver
35
+ - sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
data/Appraisals ADDED
@@ -0,0 +1,27 @@
1
+ appraise 'spree-3-2' do
2
+ gem 'spree_core', '~> 3.2.0'
3
+ gem 'spree_backend', '~> 3.2.0'
4
+ gem 'spree_auth_devise', '~> 3.4.0'
5
+ gem 'rails-controller-testing'
6
+ end
7
+
8
+ appraise 'spree-3-5' do
9
+ gem 'spree_core', '~> 3.5.0'
10
+ gem 'spree_backend', '~> 3.5.0'
11
+ gem 'spree_auth_devise', '~> 3.4.0'
12
+ gem 'rails-controller-testing'
13
+ end
14
+
15
+ appraise 'spree-3-7' do
16
+ gem 'spree_core', '~> 3.7.0'
17
+ gem 'spree_backend', '~> 3.7.0'
18
+ gem 'spree_auth_devise', '~> 3.5.0'
19
+ gem 'rails-controller-testing'
20
+ end
21
+
22
+ appraise 'spree-master' do
23
+ gem 'spree_core', github: 'spree/spree', branch: 'master'
24
+ gem 'spree_backend', github: 'spree/spree', branch: 'master'
25
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master'
26
+ gem 'rails-controller-testing'
27
+ end
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) do |repo_name|
4
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
5
+ "https://github.com/#{repo_name}.git"
6
+ end
7
+
8
+ gem 'spree_core', github: 'spree/spree', branch: 'master'
9
+ gem 'spree_backend', github: 'spree/spree', branch: 'master'
10
+ # Provides basic authentication functionality for testing parts of your engine
11
+ gem 'spree_auth_devise', github: 'spree/spree_auth_devise', branch: 'master'
12
+ gem 'rails-controller-testing'
13
+
14
+ gem 'rubocop', require: false
15
+ gem 'rubocop-rspec', require: false
16
+
17
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2019, Fabian Oudhaarlem
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # SpreeChannable
2
+
3
+ Channable offers many ways to transform a product feed to many marketplace formats. On top channable offers an order connection to accept incoming orders from connected marketplaces. This gem connects your spree store to the channable platform. The feeds are configurable and can be directly loaded into channable.
4
+ The order connection can be enabled by channable on request and can be easily configured through the spree admin panel.
5
+
6
+ ## Installation
7
+
8
+ 1. Add this extension to your Gemfile with this line:
9
+ ```ruby
10
+ gem 'spree_channable', github: 'Oldharlem/spree_channable'
11
+ ```
12
+
13
+ 2. Install the gem using Bundler:
14
+ ```ruby
15
+ bundle install
16
+ ```
17
+
18
+ 3. Copy & run migrations
19
+ ```ruby
20
+ bundle exec rails g spree_channable:install
21
+ ```
22
+
23
+ 4. Schedule order imports
24
+
25
+ Schedule the `SpreeChannable::OrderImportJob.perform_later` to run every `SpreeChannable.configuration.polling_interval` minutes. This script will pull all orders placed `SpreeChannable.configuration.polling_interval` minutes ago. Please configure the integration through the channable settings under spree configurations before running the script.
26
+
27
+ 5. Restart your server
28
+
29
+ If your server was running, restart it so that it can find the assets properly.
30
+
31
+
32
+
33
+ ## Testing
34
+
35
+ First bundle your dependencies, then run `rake`. `rake` will default to building the dummy app if it does not exist, then it will run specs. The dummy app can be regenerated by using `rake test_app`.
36
+
37
+ ```shell
38
+ bundle
39
+ bundle exec rake
40
+ ```
41
+
42
+ When testing your applications integration with this extension you may use it's factories.
43
+ Simply add this require statement to your spec_helper:
44
+
45
+ ```ruby
46
+ require 'spree_channable/factories'
47
+ ```
48
+
49
+
50
+ ## Contributing
51
+
52
+ If you'd like to contribute, please take a look at the
53
+ [instructions](CONTRIBUTING.md) for installing dependencies and crafting a good
54
+ pull request.
55
+
56
+ Copyright (c) 2019 Fabian Oudhaarlem, released under the New BSD License
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default do
10
+ if Dir['spec/dummy'].empty?
11
+ Rake::Task[:test_app].invoke
12
+ Dir.chdir('../../')
13
+ end
14
+ Rake::Task[:spec].invoke
15
+ end
16
+
17
+ desc 'Generates a dummy app for testing'
18
+ task :test_app do
19
+ ENV['LIB_NAME'] = 'spree_channable'
20
+ Rake::Task['extension:test_app'].invoke
21
+ end
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js'
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
@@ -0,0 +1,14 @@
1
+ module Spree
2
+ module Admin
3
+ class ChannableSettingsController < ResourceController
4
+ def index
5
+ path = model_class.last ? edit_admin_channable_setting_path(model_class.last.id) : new_admin_channable_setting_path
6
+ redirect_to path
7
+ end
8
+
9
+ def model_class
10
+ @model_class ||= ::ChannableSetting
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ class Spree::Api::V1::ChannableController < Spree::Api::BaseController
2
+
3
+ before_action :get_products, only: [:variant_feed, :product_feed]
4
+
5
+ def variant_feed
6
+ headers["Content-Type"] = 'application/atom+xml; charset=utf-8'
7
+ render plain: Spree::Product.to_channable_variant_xml(@products)
8
+ end
9
+
10
+ def product_feed
11
+ headers["Content-Type"] = 'application/atom+xml; charset=utf-8'
12
+ render plain: Spree::Product.to_channable_product_xml(@products)
13
+ end
14
+
15
+ private
16
+
17
+ def get_products
18
+ @products = Spree::Product.active.includes([:option_types, :taxons, product_properties: :property, variants: [{option_values: :option_type}, :default_price, :images], master: [{option_values: :option_type}, :default_price, :images]])
19
+ end
20
+
21
+ end
@@ -0,0 +1,7 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ # Automatically retry jobs that encountered a deadlock
3
+ # retry_on ActiveRecord::Deadlocked
4
+
5
+ # Most jobs are safe to ignore if the underlying records are no longer available
6
+ # discard_on ActiveJob::DeserializationError
7
+ end
@@ -0,0 +1,37 @@
1
+ module SpreeChannable
2
+ class OrderImportJob < ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform(*args)
6
+ return unless ::SpreeChannable.configuration.active?
7
+
8
+ @client = ::Channable::Client.new
9
+ channable_orders = get_orders
10
+ channable_orders.each &method(:persist_order)
11
+ end
12
+
13
+ def get_orders
14
+ limit = 100
15
+ orders = []
16
+ loop do
17
+ order_data = client.get_orders(offset: orders.size, limit: limit, start_date: (SpreeChannable.configuration.polling_interval * 2).minutes.ago)
18
+ order_data.data.orders.each {|order| orders << order} if order_data.data.orders.any?
19
+
20
+ break if order_data.data.total < limit || (!order_data.success && order_data.response.code != 429)
21
+ end
22
+ orders
23
+ end
24
+
25
+ def persist_order(channable_order)
26
+ return if Spree::Order.exists?(channable_order_id: channable_order.id)
27
+ begin
28
+ order_attributes = Spree::Order.channable_to_order_params(channable_order)
29
+ SpreeChannable::OrderImporter.import(nil, order_attributes)
30
+ rescue StandardError => e
31
+ Rails.logger.warn "[CHANNABLE] Failed to import order #{channable_order.id}. #{e}"
32
+ @client.cancellation_update(channable_order.id)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ module SpreeChannable
2
+ class ReturnImportJob < ApplicationJob
3
+ queue_as :default
4
+
5
+ def perform(*args)
6
+ return unless ::SpreeChannable.configuration.active?
7
+
8
+ @client = ::Channable::Client.new
9
+ channable_returns = get_returns
10
+ channable_returns.each &method(:persist_return)
11
+ end
12
+
13
+ def get_returns
14
+ limit = 100
15
+ returns = []
16
+ loop do
17
+ return_data = @client.get_returns(offset: returns.size, limit: limit, start_date: (SpreeChannable.configuration.polling_interval * 2).minutes.ago)
18
+ return_data.data.returns.each {|return_data| returns << return_data} if return_data.data.returns.any?
19
+
20
+ break if return_data.data.total < limit || (!return_data.success && return_data.response.code != 429)
21
+ end
22
+ returns
23
+ end
24
+
25
+ def persist_return(channable_return)
26
+ return if Spree::ReturnAuthorization.exists?(channable_return_id: channable_return.channable_id)
27
+ begin
28
+ SpreeChannable::ReturnImporter.import(channable_return)
29
+ rescue StandardError => e
30
+ Rails.logger.warn "[CHANNABLE] Failed to import return #{channable_return.channable_id}. #{e}"
31
+ @client.return_update(channable_return.channable_id, {status: 'cancelled'}.to_json)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ class ChannableSetting < ActiveRecord::Base
2
+
3
+ belongs_to :stock_location, foreign_key: 'spree_stock_location_id', class_name: 'Spree::StockLocation'
4
+ belongs_to :payment_method, foreign_key: 'spree_payment_method_id', class_name: 'Spree::PaymentMethod'
5
+
6
+ PRODUCT_CONDITIONS = ['New', 'Used']
7
+
8
+ validates_inclusion_of :product_condition, in: PRODUCT_CONDITIONS
9
+
10
+ end
@@ -0,0 +1,127 @@
1
+ module Spree
2
+ module SpreeChannable
3
+ module OrderDecorator
4
+
5
+ def channable_client
6
+ @channable_client ||= ::Channable::Client.new
7
+ end
8
+
9
+ def deliver_order_confirmation_email
10
+ unless is_channable_order?
11
+ super
12
+ end
13
+ end
14
+
15
+ def send_cancel_email
16
+ unless is_channable_order?
17
+ super
18
+ end
19
+ end
20
+
21
+ def is_channable_order?
22
+ channable_order_id.present?
23
+ end
24
+
25
+ def cancel_channable_order
26
+ return unless is_channable_order?
27
+
28
+ channable_client.cancellation_update(channable_order_id)
29
+ end
30
+
31
+ def self.prepended(base)
32
+ base.state_machine.after_transition to: :cancelled, do: :cancel_channable_order
33
+
34
+ class << base
35
+
36
+ def channable_to_order_params(channable_order)
37
+ order_params = {}
38
+
39
+ order_params[:channable_order_id] = channable_order.id
40
+ order_params[:channable_channel_order_id] = channable_order.channel_id
41
+ order_params[:channable_channel_name] = channable_order.channel_name
42
+ order_params[:completed_at] = DateTime.parse(channable_order.created)
43
+ order_params[:email] = channable_order.data.shipping.email
44
+
45
+ order_params[:bill_address_attributes] = build_address(channable_order.data.billing)
46
+ order_params[:ship_address_attributes] = build_address(channable_order.data.shipping)
47
+
48
+ order_params[:shipments_attributes] = build_shipments_attributes(channable_order)
49
+ order_params[:payments_attributes] = build_payments_attributes(channable_order)
50
+ order_params[:line_items_attributes] = build_line_items_attributes(channable_order)
51
+
52
+ order_params
53
+ end
54
+
55
+ def build_address(address_params)
56
+ {
57
+ firstname: address_params.first_name,
58
+ lastname: address_params.last_name,
59
+ address1: address_params.street,
60
+ address2: [address_params.house_number, address_params.house_number_ext].join(' '),
61
+ city: address_params.city,
62
+ zipcode: address_params.zip_code,
63
+ phone: '0612345678',
64
+ country: {
65
+ 'iso' => address_params.country_code
66
+ }
67
+ }
68
+ end
69
+
70
+ def build_shipments_attributes(channable_order)
71
+ [
72
+ {
73
+ tracking: nil,
74
+ stock_location: ::SpreeChannable.configuration.stock_location,
75
+ shipping_method: channable_order.channel_name,
76
+ inventory_units: channable_order.data.products.flat_map do |item|
77
+ variant = Spree::Variant.active.find_by_sku!(item.ean)
78
+ item.quantity.times.map do
79
+ {
80
+ variant_id: variant.id
81
+ }
82
+ end
83
+ end
84
+ }
85
+ ]
86
+ end
87
+
88
+ def build_payments_attributes(channable_order)
89
+ [
90
+ {
91
+ amount: channable_order.data.price.total.to_f,
92
+ state: 'completed',
93
+ payment_method: ::SpreeChannable.configuration.payment_method
94
+ }
95
+ ]
96
+ end
97
+
98
+ def build_line_items_attributes(channable_order)
99
+ channable_order.data.products.map do |item|
100
+ variant = Spree::Variant.active.find_by_sku!(item.ean)
101
+ {
102
+ variant_id: variant.id,
103
+ quantity: item.quantity,
104
+ price: item.price.to_f
105
+ }
106
+ end.compact
107
+ end
108
+
109
+
110
+ def build_adjustments_attributes(channable_order)
111
+ raise 'Not implemented'
112
+ [
113
+ {
114
+ label: 'Channable Discount',
115
+ amount: -(channable_order['discount'].abs.to_f)
116
+ }
117
+ ]
118
+ end
119
+
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ Spree::Order.prepend(Spree::SpreeChannable::OrderDecorator)
127
+
@@ -0,0 +1,118 @@
1
+ require 'parallel'
2
+
3
+ module Spree
4
+ module SpreeChannable
5
+ module ProductDecorator
6
+ def self.prepended(base)
7
+ class << base
8
+
9
+ def to_channable_variant_xml(products)
10
+ builder = Nokogiri::XML::Builder.new do |xml|
11
+ xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do
12
+ xml.title 'Channable variant feed'
13
+ xml.link(rel: 'self', href: ::SpreeChannable.configuration.host)
14
+ xml.updated DateTime.now.strftime('%Y-%m-%dT%H:%M:%S%z')
15
+
16
+ Parallel.map(products) {|product| product.to_channable_variant_xml}.each do |products_xml|
17
+ products_xml.each do |variant_xml|
18
+ xml.parent << Nokogiri::XML(variant_xml).at('product')
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+
25
+ builder.to_xml
26
+ end
27
+
28
+ def to_channable_product_xml(products)
29
+ builder = Nokogiri::XML::Builder.new do |xml|
30
+ xml.feed(xmlns: 'http://www.w3.org/2005/Atom') do
31
+ xml.title 'Channable product feed'
32
+ xml.link(rel: 'self', href: ::SpreeChannable.configuration.host)
33
+ xml.updated DateTime.now.strftime('%Y-%m-%dT%H:%M:%S%z')
34
+
35
+ xml.products do
36
+ Parallel.map(products) {|product| product.to_channable_product_xml}.each do |product_xml|
37
+ xml.parent << Nokogiri::XML(product_xml).at('product')
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ builder.to_xml
45
+ end
46
+ end
47
+ end
48
+
49
+
50
+ def to_channable_variant_xml
51
+ variants.active.map do |variant|
52
+ variant.to_channable_feed_entry
53
+ end
54
+ end
55
+
56
+ def to_channable_product_xml
57
+ Nokogiri::XML::Builder.new do |xml|
58
+ xml.product do
59
+ xml.id id
60
+ xml.title "#{name}"
61
+ xml.description ActionController::Base.helpers.strip_tags(normalized_description)
62
+ xml.link URI.join(::SpreeChannable.configuration.host, "/#{::SpreeChannable.configuration.url_prefix}/" + slug).to_s
63
+ (xml.image_link URI.join(::SpreeChannable.configuration.image_host, images.first.attachment.url(:large)).to_s) if images.any?
64
+ xml.condition property('product_condition') || ::SpreeChannable.configuration.product_condition
65
+
66
+ xml.price price
67
+
68
+ xml.brand property('brand') || ::SpreeChannable.configuration.brand
69
+
70
+ xml.categories do
71
+ taxons.each do |taxon|
72
+ xml.category taxon.self_and_ancestors.collect(&:name).join('|')
73
+ end
74
+ end
75
+
76
+ xml.currency Spree::Config.currency
77
+ xml.locale I18n.default_locale
78
+
79
+ # Property fields
80
+
81
+ xml.gender property('gender') || 'Not set'
82
+ xml.delivery_period property('delivery_period') || ::SpreeChannable.configuration.delivery_period
83
+ xml.material property('material') || 'Not set'
84
+
85
+ xml.variants do
86
+ variants.each do |variant|
87
+ xml.variant do
88
+ xml.id variant.id
89
+ xml.product_id id
90
+ xml.options_text variant.options_text
91
+ (xml.image_link URI.join(::SpreeChannable.configuration.image_host, variant.get_images.first.attachment.url(:large)).to_s) unless variant.get_images.empty?
92
+
93
+ xml.availability variant.can_supply?
94
+ xml.stock variant.total_on_hand
95
+ xml.price variant.price
96
+ xml.sale_price variant.respond_to?(:sale_price) ? (variant.sale_price || variant.price) : variant.price
97
+
98
+ xml.sku variant.sku
99
+
100
+ xml.currency Spree::Config.currency
101
+ xml.locale I18n.default_locale
102
+
103
+ variant.option_values.each do |option_value|
104
+ xml.send(option_value.option_type.name, option_value.presentation)
105
+ end
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end.to_xml
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+
118
+ Spree::Product.prepend(Spree::SpreeChannable::ProductDecorator)