solidus-returnly 0.4.3 → 0.5.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 56f2b7c03fb59b4fb1f229ca1c9824d957edc6a2
4
- data.tar.gz: cf3b33c27bdd470a760543664fc4da4cec015fc6
3
+ metadata.gz: 174cd758f9a9329da775abb30290df3a942f727f
4
+ data.tar.gz: c825cf7169dc8cc4b45d8c64248db506433a2b0f
5
5
  SHA512:
6
- metadata.gz: c71b20d2958ef1b9a2fe8fb6aaf681d6a0aa074b30da7a862e59b4c53e5d9171f4bb8798059d8ecdebd8af0358d526ccbf27801f38decfdde7130a8713b3f0a5
7
- data.tar.gz: cec89b1f3fba2e61007df085ce75d18847a7d89f50d6fe58868c3ee10556483cb66f08466cd4e90b7dc1e52c9114606769478147f326bfa1505ab439201c06df
6
+ metadata.gz: 32530c1a9fae7cc15034e47ee6af6c5ba21204c86a0bf29499587ebcf389932f75bfa846a93dca38df553984bf0c7dd84dffcc94e16e261ac96529a2b8774964
7
+ data.tar.gz: cd2c85551f5f9fbd4210bd47c655f1551386fdc8cb115aad79bec8958594aed748c3364834fcad882ee44add7d2e7412366447eabcec9458e776e013444ad0a1
data/README.md CHANGED
@@ -3,6 +3,19 @@ Solidus Returnly
3
3
 
4
4
  This gem adds the required APIs to allow returnly obtain an estimate, process a return and apply a reimbursement
5
5
 
6
+
7
+ Spree/Solidus
8
+ -----
9
+ This gem can be installed on spree and solidus, to release a new version to both registries you need to run
10
+ `bash
11
+ $ rake both_releases
12
+ `
13
+ this will generate two packages, `solidus-returnly` and `spree-returnly`
14
+
15
+ Current Compat Versions:
16
+ - Solidus : `~>2.0.0`
17
+ - Spree : `~>3.0.1`
18
+
6
19
  Installation
7
20
  ------------
8
21
 
@@ -63,15 +76,15 @@ API
63
76
 
64
77
  To get an estimate of the return order and taxes
65
78
 
66
- `POST /api/returnly/refunds/:order_number/estimate`
79
+ `POST /api/returnly/orders/:order_number/refund_estimate`
67
80
 
68
81
  Payload:
69
82
  ```json
70
83
  {
71
- "line_items": [
84
+ "items": [
72
85
  {
73
86
  "order_line_item_id": "{spree order line item id}",
74
- "quantity": 1
87
+ "units": 1
75
88
  }
76
89
  ]
77
90
  }
@@ -81,19 +94,18 @@ Response:
81
94
 
82
95
  ```json
83
96
  {
84
- "total_amount": "{money}",
85
- "subtotal_amount": "{money}",
97
+ "product_amount": "{money}",
86
98
  "tax_amount": "{money}",
87
99
  "discount_amount": "{money}",
88
- "shipping_amount": "{money}",
89
- "line_items": [
100
+ "total_refund_amount": "{money}",
101
+ "refundable_shipping_amount": "{money}",
102
+ "refundable_shipping_tax_amount": "{money}",
103
+ "max_refundable_amount": "{money}",
104
+ "refunds_by_payment_method": [
90
105
  {
91
- "order_line_item_id": "{x_order_line_item_id1}",
92
- "quantity": 1,
93
- "total_amount": "{money}",
94
- "subtotal_amount": "{money}",
95
- "tax_amount": "{money}",
96
- "discount_amount": "{money}",
106
+ "code": "Credit Card",
107
+ "amount": "{money}",
108
+ "original_transaction_id": 1
97
109
  }
98
110
  ]
99
111
  }
@@ -103,24 +115,23 @@ To process the return and apply the reimbursement**immediately**
103
115
  you can pass the `restock` parameter on false to prevent the api
104
116
  to immediately change the stock in the default warehouse
105
117
 
106
- `POST /api/returnly/refunds/:order_number`
118
+ `POST /api/returnly/orders/:order_number/refund`
107
119
 
108
120
  Payload:
109
121
 
110
122
  ```json
111
123
  {
112
- "line_items": [
124
+ "items": [
113
125
  {
114
126
  "order_line_item_id": "{x_order_line_item_id1}",
115
- "quantity": 2
116
- },
127
+ "units": 2,
128
+ "restock": true
129
+ }
117
130
  ],
118
- "transactions": [{
119
- "parent_id": "{parent (order) tx id}",
120
- "amount": "{money}"
121
- }],
122
- "note": "comment text",
123
- "restock": true
131
+ "notify_shopper": true,
132
+ "product_refund_amount": "{money}",
133
+ "shipping_refund_amount": "{money}",
134
+ "refund_note": "comment text"
124
135
  }
125
136
  ```
126
137
 
@@ -129,22 +140,21 @@ Response:
129
140
  ```json
130
141
  {
131
142
  "refund_id": "{x_refund_id}",
132
- "created_at": "{timestamp}",
133
- "updated_at": "{timestamp}",
134
143
  "user_id": "{optional merchant user ID}",
135
144
  "line_items": [
136
145
  {
137
146
  "refund_line_item_id": "{x_refund_line_item_id1}",
138
147
  "order_line_item_id": "{x_order_line_item_id1}",
139
- "quantity": 2
148
+ "units": 2
140
149
  },
141
150
  ],
142
- "transactions": [
143
- "id": "{refund tx id}",
144
- "amount": "{money}"
151
+ "transactions": [
152
+ {
153
+ "id": "{refund tx id}",
154
+ "amount": "{money}"
155
+ }
145
156
  ],
146
- "note": "comment text",
147
- "restock": true
157
+ "refund_note": "comment text",
148
158
  }
149
159
  ```
150
160
 
data/Rakefile CHANGED
@@ -1,6 +1,16 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
1
2
  require 'bundler'
2
3
 
3
- Bundler::GemHelper.install_tasks
4
+ def load_library_env
5
+ lib_name = ENV['SPREE_BUILD'] ? 'spree-returnly' : 'solidus-returnly'
6
+
7
+ puts "Loading #{lib_name}"
8
+
9
+ Bundler::GemHelper.install_tasks name: lib_name
10
+ end
11
+
12
+
13
+ load_library_env
4
14
 
5
15
  begin
6
16
  require 'spree/testing_support/extension_rake'
@@ -20,6 +30,15 @@ task :first_run do
20
30
  end
21
31
  end
22
32
 
33
+ task :both_releases do
34
+ %w(spree-returnly solidus-returnly).each do |lib_name|
35
+ Bundler::GemHelper.install_tasks name: lib_name
36
+ Rake::Task[:release].invoke
37
+ end
38
+
39
+ load_library_env
40
+ end
41
+
23
42
  desc 'Generates a dummy app for testing'
24
43
  task :test_app do
25
44
  ENV['LIB_NAME'] = 'solidus-returnly'
@@ -5,8 +5,8 @@ module Spree
5
5
  before_filter :set_headers
6
6
 
7
7
  def set_headers
8
- response.headers['X-Returnly-Extension-Version'] = Solidus::Returnly::VERSION
9
- response.headers['X-Returnly-Extension-Platform-Qualifier'] = Spree.solidus_version
8
+ response.headers['X-Returnly-Extension-Version'] = ::Returnly::VERSION
9
+ response.headers['X-Returnly-Extension-Platform-Qualifier'] = ::Returnly.platform_version
10
10
  end
11
11
  end
12
12
  end
@@ -2,7 +2,7 @@ module Spree
2
2
  module Api
3
3
  module Returnly
4
4
  class RefundsController < ApiController
5
- include Solidus::Returnly::RefundsConfiguration
5
+ include ::Returnly::RefundsConfiguration
6
6
 
7
7
  def estimate
8
8
  authorize! :create, Order
@@ -14,23 +14,23 @@ module Spree
14
14
 
15
15
  refunds = refunder_class.new order: order,
16
16
  line_items: line_items_params,
17
- restock: refund_restock_param,
18
- transactions: transactions_params
17
+ product_refund_amount: product_refund_amount_param,
18
+ shipping_refund_amount: shipping_refund_amount_param
19
19
  render json: refunds.proceed!
20
20
  end
21
21
 
22
22
  private
23
23
 
24
24
  def line_items_params
25
- params.require(:line_items)
25
+ params.require(:items)
26
26
  end
27
27
 
28
- def transactions_params
29
- params.require(:transactions)
28
+ def product_refund_amount_param
29
+ params.require(:product_refund_amount)
30
30
  end
31
31
 
32
- def refund_restock_param
33
- params.require(:restock)
32
+ def shipping_refund_amount_param
33
+ params.require(:shipping_refund_amount)
34
34
  end
35
35
 
36
36
  def order
@@ -4,8 +4,8 @@ module Spree
4
4
  class VersionController < ApiController
5
5
  def index
6
6
  render json: {
7
- extension_version: Solidus::Returnly::VERSION,
8
- platform_version: Spree.solidus_version
7
+ extension_version: ::Returnly::VERSION,
8
+ platform_version: ::Returnly.platform_version
9
9
  }
10
10
  end
11
11
  end
@@ -0,0 +1,14 @@
1
+ Spree::ReturnItem.class_eval do
2
+ unless ::Returnly.is_solidus?
3
+ attr_accessor :amount
4
+
5
+ def amount
6
+ self.pre_tax_amount + self.included_tax_total
7
+ end
8
+
9
+
10
+ def amount=(amount)
11
+ self.pre_tax_amount = amount - self.included_tax_total
12
+ end
13
+ end
14
+ end
data/config/routes.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  Spree::Core::Engine.routes.draw do
2
2
  namespace :api, defaults: { format: 'json' } do
3
- post '/returnly/refunds/:order_id/estimate', to: 'returnly/refunds#estimate'
4
- post '/returnly/refunds/:order_id', to: 'returnly/refunds#create'
3
+ post '/returnly/orders/:order_id/refund_estimate', to: 'returnly/refunds#estimate'
4
+ post '/returnly/orders/:order_id/refund', to: 'returnly/refunds#create'
5
+
5
6
  get '/returnly/version', to: 'returnly/version#index'
6
7
  end
7
8
  end
data/lib/returnly.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'returnly/engine'
2
+ require 'returnly/refunds_configuration'
3
+ require 'returnly/refunder'
4
+ require 'returnly/refund_calculator'
@@ -0,0 +1,65 @@
1
+ module Returnly
2
+ class << self
3
+ mattr_accessor :return_item_amount_calculator,
4
+ :return_item_restock_policy,
5
+ :refunder,
6
+ :refund_calculator
7
+ end
8
+
9
+ def self.configure(&block)
10
+ yield self if block
11
+ end
12
+
13
+ class Engine < Rails::Engine
14
+ require 'spree/core'
15
+
16
+ engine_name 'returnly'
17
+
18
+ config.autoload_paths += %W(#{config.root}/lib)
19
+
20
+ # use rspec for tests
21
+ config.generators do |g|
22
+ g.test_framework :rspec
23
+ end
24
+
25
+ def self.activate
26
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
27
+ Rails.configuration.cache_classes ? require(c) : load(c)
28
+ end
29
+ end
30
+
31
+ config.to_prepare(&method(:activate).to_proc)
32
+
33
+ rake_tasks do
34
+ namespace :returnly do
35
+ require 'securerandom'
36
+
37
+ desc "Creates an returnly user and prints the generated API key"
38
+ task install_user: :environment do
39
+
40
+ returnly_email = 'api@returnly.com'
41
+
42
+ password = SecureRandom.hex(24)
43
+
44
+ if (user = Spree::User.find_by email: returnly_email)
45
+ puts "The Returnly User is already generated"
46
+ puts "The Api key is: #{user.spree_api_key}"
47
+ else
48
+ user = Spree::User.new email: returnly_email, password: password,
49
+ password_confirmation: password
50
+
51
+ user.generate_spree_api_key!
52
+
53
+ if user.save!
54
+ puts "****************************************************"
55
+ puts "Api key Generated #{user.spree_api_key}"
56
+ puts "Returnly User Password #{password}"
57
+ puts "Returnly User email #{returnly_email}"
58
+ puts "****************************************************"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
File without changes
@@ -0,0 +1,21 @@
1
+ module Returnly
2
+ module Refund
3
+ class AmountCalculator
4
+ attr_accessor :refund
5
+
6
+ def initialize(refund)
7
+ self.refund = refund
8
+ end
9
+
10
+ def return_item_refund_amount(return_item)
11
+ default_amount(return_item)
12
+ end
13
+
14
+ protected
15
+ def default_amount(return_item)
16
+ refund.refund_amount_per_item -
17
+ Money.from_amount(return_item.total - return_item.pre_tax_amount)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ module Returnly
2
+ module Refund
3
+ class ReturnItemRestockPolicy
4
+ attr_accessor :refund
5
+
6
+ def initialize(refund)
7
+ self.refund = refund
8
+ end
9
+
10
+ def should_return_item?(return_item)
11
+ return_item.resellable?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,91 @@
1
+ module Returnly
2
+ class RefundCalculator
3
+ attr_accessor :line_items, :order
4
+
5
+ def self.process(order, line_items)
6
+ RefundPresenter.present_estimate new(order, line_items)
7
+ end
8
+
9
+ def initialize(order, line_items)
10
+ self.line_items = line_items
11
+ self.order = order
12
+ end
13
+
14
+ def set_tax(return_item)
15
+ percent_of_tax = (return_item.amount <= 0) ? 0 : return_item.amount /
16
+ Spree::ReturnItem.refund_amount_calculator.new.compute(return_item)
17
+
18
+ additional_tax_total = percent_of_tax * return_item.inventory_unit.additional_tax_total
19
+ included_tax_total = percent_of_tax * return_item.inventory_unit.included_tax_total
20
+
21
+ return_item.additional_tax_total = additional_tax_total
22
+ return_item.included_tax_total = included_tax_total
23
+
24
+ return_item
25
+ end
26
+
27
+ def shipping_tax
28
+ order.all_adjustments.where(source_type: 'Spree::TaxRate', adjustable_type: 'Spree::Shipment').sum(&:amount)
29
+ end
30
+
31
+ def line_items_return_items
32
+ found_return_items = {}
33
+
34
+ line_items.each do |line_item|
35
+ with_available_inventory_units line_item do |inventory_unit, quantity|
36
+ next unless quantity > 0
37
+
38
+ found_return_items[line_item[:order_line_item_id]] ||= []
39
+ return_item = create_return_item(inventory_unit, restock: line_item[:restock])
40
+ found_return_items[line_item[:order_line_item_id]] << set_tax(return_item)
41
+ end
42
+ end
43
+
44
+ found_return_items
45
+ end
46
+
47
+ def line_item_inventory_unit(line_item)
48
+ returnable_inventory_units.select { |iu| iu.line_item_id == line_item[:order_line_item_id].to_i }
49
+ end
50
+
51
+ def line_item(id)
52
+ returnable_line_items.find { |i| i.id == id.to_i }
53
+ end
54
+
55
+ def returnable_inventory_units
56
+ @_inventory_units ||= order.inventory_units.includes(:line_item)
57
+ .where(line_item_id: requested_line_items)
58
+ .where('state is not "returned"')
59
+ end
60
+
61
+ def returnable_line_items
62
+ @_line_items ||= returnable_inventory_units.map(&:line_item)
63
+ end
64
+
65
+ def requested_line_items
66
+ line_items.map { |item| item[:order_line_item_id] }
67
+ end
68
+
69
+
70
+ protected
71
+
72
+ def create_return_item(inventory_unit, restock: false)
73
+ Spree::ReturnItem.new(
74
+ amount: inventory_unit.line_item.price,
75
+ acceptance_status: 'accepted',
76
+ inventory_unit_id: inventory_unit.id,
77
+ reception_status_event: 'receive',
78
+ resellable: restock
79
+ )
80
+ end
81
+
82
+ def with_available_inventory_units(line_item)
83
+ quantity = line_item[:units].to_i
84
+
85
+ available_inventory_units = line_item_inventory_unit(line_item).slice(0, quantity)
86
+ available_inventory_units.each do |inventory_unit|
87
+ yield inventory_unit, quantity
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,78 @@
1
+ module Returnly
2
+ class RefundPresenter
3
+
4
+ def self.present_estimate(calculator)
5
+ total = Money.new 0
6
+ sub_total = Money.new 0
7
+
8
+ calculator.line_items_return_items.values.flatten.each do |return_item|
9
+ total += Money.from_amount return_item.total
10
+ sub_total += Money.from_amount return_item.pre_tax_amount
11
+ end
12
+
13
+ {
14
+ product_amount: sub_total.to_f,
15
+ tax_amount: (total - sub_total).to_f,
16
+ discount_amount: calculator.order.promo_total.to_f,
17
+ total_refund_amount: (total - calculator.order.promo_total).to_f,
18
+ refundable_shipping_amount: calculator.order.shipment_total.to_f,
19
+ refundable_shipping_tax_amount: calculator.shipping_tax.to_f,
20
+ max_refundable_amount: calculator.order.total.to_f,
21
+ refunds_by_payment_method: refunds_by_payment(calculator.order)
22
+ }
23
+ end
24
+
25
+ def self.present_line_items(calculator)
26
+ calculator.line_items_return_items.values.map do |return_items|
27
+ line_item = return_items.first.inventory_unit.line_item
28
+
29
+ total_amount = 0.0
30
+ sub_total = 0.0
31
+
32
+ return_items.each do |return_item|
33
+ total_amount += return_item.total
34
+ sub_total += return_item.pre_tax_amount
35
+ end
36
+
37
+ {
38
+ order_line_item_id: line_item.id,
39
+ quantity: return_items.size,
40
+ total_amount: total_amount.to_f,
41
+ tax_amount: (total_amount - sub_total).to_f
42
+ }
43
+ end
44
+ end
45
+
46
+
47
+ def self.present_refund(refunder)
48
+ customer_return = refunder.customer_return
49
+
50
+ {
51
+ refund_id: customer_return.number,
52
+ line_items: customer_return.return_items.group_by { |ri| ri.inventory_unit.line_item.id }.map do |_, return_items|
53
+ first_return_item = return_items.first
54
+ {
55
+ refund_line_item_id: first_return_item.id,
56
+ order_line_item_id: first_return_item.inventory_unit.line_item.id,
57
+ refunded_amount_per_item: first_return_item.amount.to_f,
58
+ units: return_items.size
59
+ }
60
+ end,
61
+ transactions: [
62
+ id: refunder.reimbursement.id,
63
+ amount: refunder.reimbursement.total
64
+ ]
65
+ }
66
+ end
67
+
68
+ private_class_method def self.refunds_by_payment(order)
69
+ order.payments.each_with_object([]) do |payment, payments|
70
+ payments << {
71
+ code: payment.source_type.constantize.model_name.human,
72
+ amount: (order.item_total + order.adjustment_total).to_f,
73
+ original_transaction_id: ''
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,82 @@
1
+ module Returnly
2
+ class Refunder
3
+ include Returnly::RefundsConfiguration
4
+
5
+ attr_accessor :order,
6
+ :line_items,
7
+ :product_refund_amount,
8
+ :shipping_refund_amount,
9
+ :customer_return,
10
+ :return_item_amount_calculator,
11
+ :return_item_restock_policy,
12
+ :refund_calculator
13
+
14
+ def initialize(order:, line_items:, product_refund_amount:, shipping_refund_amount:)
15
+ self.order = order
16
+ self.line_items = line_items
17
+ self.customer_return = Spree::CustomerReturn.new customer_return_params
18
+ self.product_refund_amount = product_refund_amount
19
+ self.shipping_refund_amount = shipping_refund_amount
20
+
21
+ configure
22
+ end
23
+
24
+ def process_return_items
25
+ each_return_item do |return_item|
26
+ return_item.amount = return_item_amount_calculator.return_item_refund_amount return_item
27
+ return_item.resellable = return_item_restock_policy.should_return_item? return_item
28
+ end
29
+ end
30
+
31
+ def proceed!
32
+ process_return_items
33
+
34
+ if customer_return.save!
35
+ perform_reimbursement
36
+ RefundPresenter.present_refund(self)
37
+ end
38
+ end
39
+
40
+ def refund_available_amount
41
+ @available_amount ||= Money.from_amount(product_refund_amount.to_f + shipping_refund_amount.to_f)
42
+ end
43
+
44
+ def refund_amount_per_item
45
+ refund_available_amount / line_items.size
46
+ end
47
+
48
+ def reimbursement
49
+ @_reimbursement ||= Spree::Reimbursement.build_from_customer_return(customer_return)
50
+ end
51
+
52
+ private
53
+
54
+ def configure
55
+ self.return_item_amount_calculator = return_item_amount_calculator_class.new(self)
56
+ self.return_item_restock_policy = return_item_restock_policy_class.new(self)
57
+ self.refund_calculator = refund_calculator_class.new(order, line_items)
58
+ end
59
+
60
+ def perform_reimbursement
61
+ reimbursement.save!
62
+ reimbursement.perform!
63
+ end
64
+
65
+ def each_return_item
66
+ customer_return.return_items = refund_calculator.line_items_return_items.values.flatten
67
+ customer_return.return_items.each do |return_item|
68
+ yield return_item
69
+ end
70
+ end
71
+
72
+ def customer_return_params
73
+ {
74
+ stock_location_id: stock_location_id
75
+ }
76
+ end
77
+
78
+ def stock_location_id
79
+ order.shipments.last.stock_location.id
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,20 @@
1
+ module Returnly
2
+ module RefundsConfiguration
3
+ def return_item_amount_calculator_class
4
+ Returnly.return_item_amount_calculator || Returnly::Refund::AmountCalculator
5
+ end
6
+
7
+ # return item qualifies to be restocked (after refund)
8
+ def return_item_restock_policy_class
9
+ Returnly.return_item_restock_policy || Returnly::Refund::ReturnItemRestockPolicy
10
+ end
11
+
12
+ def refunder_class
13
+ Returnly.refunder || Returnly::Refunder
14
+ end
15
+
16
+ def refund_calculator_class
17
+ Returnly.refund_calculator || Returnly::RefundCalculator
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Returnly
2
+ VERSION = '0.5.0.pre'
3
+
4
+ def platform_version
5
+ defined?(Solidus)
6
+ end
7
+ end
@@ -1,12 +1,12 @@
1
- require 'solidus_core'
1
+ require 'spree_core'
2
+ require 'returnly'
2
3
 
3
- require 'solidus/returnly/engine'
4
- require 'solidus/returnly/version'
5
- require 'solidus/returnly/refunds_configuration'
4
+ module Returnly
5
+ def self.is_solidus?
6
+ true
7
+ end
6
8
 
7
- require 'solidus/returnly/refund/amount_calculator'
8
- require 'solidus/returnly/refund/return_item_restock_policy'
9
-
10
- require 'solidus/returnly/refunder'
11
- require 'solidus/returnly/refund_calculator'
12
- require 'solidus/returnly/refund_presenter'
9
+ def self.platform_version
10
+ Spree.solidus_version
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'spree_core'
2
+ require 'returnly'
3
+
4
+ module Returnly
5
+ def self.is_solidus?
6
+ false
7
+ end
8
+
9
+ def self.platform_version
10
+ Spree.version
11
+ end
12
+ end
metadata CHANGED
@@ -1,57 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus-returnly
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Returnly Technologies, Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-22 00:00:00.000000000 Z
11
+ date: 2017-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: solidus_auth_devise
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: solidus
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '2.0'
33
+ version: 2.0.0
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '2.0'
40
+ version: 2.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 5.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 5.0.0
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: solidus_core
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: '2.0'
61
+ version: 2.0.0
34
62
  type: :runtime
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: '2.0'
68
+ version: 2.0.0
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: solidus_api
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '2.0'
75
+ version: 2.0.0
48
76
  type: :runtime
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '2.0'
82
+ version: 2.0.0
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: capybara
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +94,34 @@ dependencies:
66
94
  - - ">="
67
95
  - !ruby/object:Gem::Version
68
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: erubis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: minitest
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
69
125
  - !ruby/object:Gem::Dependency
70
126
  name: awesome_print
71
127
  requirement: !ruby/object:Gem::Requirement
@@ -274,19 +330,21 @@ files:
274
330
  - app/controllers/spree/api/returnly/api_controller.rb
275
331
  - app/controllers/spree/api/returnly/refunds_controller.rb
276
332
  - app/controllers/spree/api/returnly/version_controller.rb
333
+ - app/models/spree/return_item_decorator.rb
277
334
  - config/locales/en.yml
278
335
  - config/routes.rb
279
- - lib/generators/solidus_returnly/install/install_generator.rb
336
+ - lib/returnly.rb
337
+ - lib/returnly/engine.rb
338
+ - lib/returnly/factories.rb
339
+ - lib/returnly/refund/amount_calculator.rb
340
+ - lib/returnly/refund/return_item_restock_policy.rb
341
+ - lib/returnly/refund_calculator.rb
342
+ - lib/returnly/refund_presenter.rb
343
+ - lib/returnly/refunder.rb
344
+ - lib/returnly/refunds_configuration.rb
345
+ - lib/returnly/version.rb
280
346
  - lib/solidus-returnly.rb
281
- - lib/solidus/returnly/engine.rb
282
- - lib/solidus/returnly/factories.rb
283
- - lib/solidus/returnly/refund/amount_calculator.rb
284
- - lib/solidus/returnly/refund/return_item_restock_policy.rb
285
- - lib/solidus/returnly/refund_calculator.rb
286
- - lib/solidus/returnly/refund_presenter.rb
287
- - lib/solidus/returnly/refunder.rb
288
- - lib/solidus/returnly/refunds_configuration.rb
289
- - lib/solidus/returnly/version.rb
347
+ - lib/spree-returnly.rb
290
348
  homepage: https://github.com/returnly/solidus-returnly
291
349
  licenses:
292
350
  - MIT
@@ -302,9 +360,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
302
360
  version: '0'
303
361
  required_rubygems_version: !ruby/object:Gem::Requirement
304
362
  requirements:
305
- - - ">="
363
+ - - ">"
306
364
  - !ruby/object:Gem::Version
307
- version: '0'
365
+ version: 1.3.1
308
366
  requirements: []
309
367
  rubyforge_project:
310
368
  rubygems_version: 2.6.11
@@ -1,19 +0,0 @@
1
- module SolidusReturnly
2
- module Generators
3
- class InstallGenerator < Rails::Generators::Base
4
- class_option :auto_run_migrations, type: :boolean, default: false
5
-
6
- def add_javascripts
7
- end
8
-
9
- def add_stylesheets
10
- end
11
-
12
- def add_migrations
13
- end
14
-
15
- def run_migrations
16
- end
17
- end
18
- end
19
- end
@@ -1,67 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- class << self
4
- mattr_accessor :return_item_amount_calculator,
5
- :return_item_restock_policy,
6
- :refunder,
7
- :refund_calculator
8
- end
9
-
10
- def self.configure(&block)
11
- yield self if block
12
- end
13
-
14
- class Engine < Rails::Engine
15
- require 'spree/core'
16
-
17
- engine_name 'returnly'
18
-
19
- config.autoload_paths += %W(#{config.root}/lib)
20
-
21
- # use rspec for tests
22
- config.generators do |g|
23
- g.test_framework :rspec
24
- end
25
-
26
- def self.activate
27
- Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
28
- Rails.configuration.cache_classes ? require(c) : load(c)
29
- end
30
- end
31
-
32
- config.to_prepare(&method(:activate).to_proc)
33
-
34
- rake_tasks do
35
- namespace :returnly do
36
- require 'securerandom'
37
-
38
- desc "Creates an returnly user and prints the generated API key"
39
- task install_user: :environment do
40
-
41
- returnly_email = 'api@returnly.com'
42
-
43
- password = SecureRandom.hex(24)
44
-
45
- if (user = Spree::User.find_by email: returnly_email)
46
- puts "The Returnly User is already generated"
47
- puts "The Api key is: #{user.spree_api_key}"
48
- else
49
- user = Spree::User.new email: returnly_email, password: password,
50
- password_confirmation: password
51
-
52
- api_key = user.generate_spree_api_key
53
-
54
- if user.save!
55
- puts "****************************************************"
56
- puts "Api key Generated #{api_key}"
57
- puts "Returnly User Password #{password}"
58
- puts "Returnly User email #{returnly_email}"
59
- puts "****************************************************"
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
66
- end
67
- end
@@ -1,23 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- module Refund
4
- class AmountCalculator
5
- attr_accessor :refund
6
-
7
- def initialize(refund)
8
- self.refund = refund
9
- end
10
-
11
- def return_item_refund_amount(return_item)
12
- default_amount(return_item)
13
- end
14
-
15
- protected
16
- def default_amount(return_item)
17
- refund.refund_amount_per_item -
18
- Money.from_amount(return_item.total - return_item.pre_tax_amount)
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,18 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- module Refund
4
- class ReturnItemRestockPolicy
5
- attr_accessor :refund
6
-
7
- def initialize(refund)
8
- self.refund = refund
9
- end
10
-
11
- #
12
- def should_return_item?(_)
13
- refund.restock
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,87 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- class RefundCalculator
4
- attr_accessor :line_items, :order
5
-
6
- def self.process(order, line_items)
7
- RefundPresenter.present_estimate new(order, line_items)
8
- end
9
-
10
- def initialize(order, line_items)
11
- self.line_items = line_items
12
- self.order = order
13
- end
14
-
15
- def set_tax(return_item)
16
- percent_of_tax = (return_item.amount <= 0) ? 0 : return_item.amount /
17
- Spree::ReturnItem.refund_amount_calculator.new.compute(return_item)
18
-
19
- additional_tax_total = percent_of_tax * return_item.inventory_unit.additional_tax_total
20
- included_tax_total = percent_of_tax * return_item.inventory_unit.included_tax_total
21
-
22
- return_item.additional_tax_total = additional_tax_total
23
- return_item.included_tax_total = included_tax_total
24
-
25
- return_item
26
- end
27
-
28
- def line_items_return_items
29
- found_return_items = {}
30
-
31
- line_items.each do |line_item|
32
- with_available_inventory_units line_item do |inventory_unit, quantity|
33
- next unless quantity > 0
34
-
35
- found_return_items[line_item[:order_line_item_id]] ||= []
36
- return_item = create_return_item(inventory_unit)
37
- found_return_items[line_item[:order_line_item_id]] << set_tax(return_item)
38
- end
39
- end
40
-
41
- found_return_items
42
- end
43
-
44
- def line_item_inventory_unit(line_item)
45
- returnable_inventory_units.select { |iu| iu.line_item_id == line_item[:order_line_item_id].to_i }
46
- end
47
-
48
- def line_item(id)
49
- returnable_line_items.find { |i| i.id == id.to_i }
50
- end
51
-
52
- def returnable_inventory_units
53
- @_inventory_units ||= order.inventory_units.includes(:line_item)
54
- .where(line_item_id: requested_line_items)
55
- .where('state != ?', 'returned')
56
- end
57
-
58
- def returnable_line_items
59
- @_line_items ||= returnable_inventory_units.map(&:line_item)
60
- end
61
-
62
- def requested_line_items
63
- line_items.map { |item| item[:order_line_item_id] }
64
- end
65
-
66
-
67
- protected
68
-
69
- def create_return_item(inventory_unit)
70
- Spree::ReturnItem.new({ amount: inventory_unit.line_item.price,
71
- acceptance_status: 'accepted',
72
- inventory_unit_id: inventory_unit.id,
73
- reception_status_event: 'receive'
74
- })
75
- end
76
-
77
- def with_available_inventory_units(line_item)
78
- quantity = line_item[:quantity].to_i
79
-
80
- available_inventory_units = line_item_inventory_unit(line_item).slice(0, quantity)
81
- available_inventory_units.each do |inventory_unit|
82
- yield inventory_unit, quantity
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,73 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- class RefundPresenter
4
-
5
- def self.present_estimate(calculator)
6
- total = Money.new 0
7
- sub_total = Money.new 0
8
-
9
- calculator.line_items_return_items.values.flatten.each do |return_item|
10
- total += Money.from_amount return_item.total
11
- sub_total += Money.from_amount return_item.pre_tax_amount
12
- end
13
-
14
- {
15
- total_amount: total.to_f,
16
- subtotal_amount: sub_total.to_f,
17
- tax_amount: (total - sub_total).to_f,
18
- discount_amount: calculator.order.promo_total.to_f,
19
- shipping_amount: calculator.order.shipment_total.to_f,
20
- line_items: present_line_items(calculator)
21
- }
22
- end
23
-
24
-
25
- def self.present_line_items(calculator)
26
- calculator.line_items_return_items.values.map do |return_items|
27
- line_item = return_items.first.inventory_unit.line_item
28
-
29
- total_amount = 0.0
30
- sub_total = 0.0
31
-
32
- return_items.each do |return_item|
33
- total_amount += return_item.total
34
- sub_total += return_item.pre_tax_amount
35
- end
36
-
37
- {
38
- order_line_item_id: line_item.id,
39
- quantity: return_items.size,
40
- total_amount: total_amount.to_f,
41
- tax_amount: (total_amount - sub_total).to_f
42
- }
43
- end
44
- end
45
-
46
-
47
- def self.present_refund(refunder)
48
- customer_return = refunder.customer_return
49
-
50
- {
51
- refund_id: customer_return.number,
52
- created_at: customer_return.created_at,
53
- updated_at: customer_return.updated_at,
54
- line_items: customer_return.return_items.group_by{ |ri| ri.inventory_unit.line_item.id }.map do |_, return_items|
55
- first_return_item = return_items.first
56
- {
57
- refund_line_item_id: first_return_item.id,
58
- order_line_item_id: first_return_item.inventory_unit.line_item.id,
59
- refunded_amount_per_item: first_return_item.amount.to_f,
60
- total_refunded_per_line_item: return_items.sum(&:amount).to_f,
61
- quantity: return_items.size
62
- }
63
- end,
64
- transactions: [
65
- id: refunder.reimbursement.id,
66
- amount: refunder.reimbursement.total
67
- ],
68
- restock: refunder.restock
69
- }
70
- end
71
- end
72
- end
73
- end
@@ -1,84 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- class Refunder
4
- include Solidus::Returnly::RefundsConfiguration
5
-
6
- attr_accessor :order,
7
- :line_items,
8
- :restock,
9
- :transactions,
10
- :customer_return,
11
- :return_item_amount_calculator,
12
- :return_item_restock_policy,
13
- :refund_calculator
14
-
15
- def initialize(order:, line_items:, restock:, transactions:)
16
- self.order = order
17
- self.line_items = line_items
18
- self.customer_return = Spree::CustomerReturn.new customer_return_params
19
- self.transactions = transactions
20
- self.restock = restock
21
-
22
- configure
23
- end
24
-
25
- def process_return_items
26
- each_return_item do |return_item|
27
- return_item.amount = return_item_amount_calculator.return_item_refund_amount return_item
28
- return_item.resellable = return_item_restock_policy.should_return_item? return_item
29
- end
30
- end
31
-
32
- def proceed!
33
- process_return_items
34
-
35
- if customer_return.save!
36
- perform_reimbursement
37
- RefundPresenter.present_refund(self)
38
- end
39
- end
40
-
41
- def refund_available_amount
42
- @available_amount ||= transactions.sum { |t| Money.from_amount(t[:amount].to_f) }
43
- end
44
-
45
- def refund_amount_per_item
46
- refund_available_amount / line_items.size
47
- end
48
-
49
- def reimbursement
50
- @_reimbursement ||= Spree::Reimbursement.build_from_customer_return(customer_return)
51
- end
52
-
53
- private
54
-
55
- def configure
56
- self.return_item_amount_calculator = return_item_amount_calculator_class.new(self)
57
- self.return_item_restock_policy = return_item_restock_policy_class.new(self)
58
- self.refund_calculator = refund_calculator_class.new(order, line_items)
59
- end
60
-
61
- def perform_reimbursement
62
- reimbursement.save!
63
- reimbursement.perform!
64
- end
65
-
66
- def each_return_item
67
- customer_return.return_items = refund_calculator.line_items_return_items.values.flatten
68
- customer_return.return_items.each do |return_item|
69
- yield return_item
70
- end
71
- end
72
-
73
- def customer_return_params
74
- {
75
- stock_location_id: stock_location_id
76
- }
77
- end
78
-
79
- def stock_location_id
80
- order.shipments.last.stock_location.id
81
- end
82
- end
83
- end
84
- end
@@ -1,22 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- module RefundsConfiguration
4
- def return_item_amount_calculator_class
5
- Solidus::Returnly.return_item_amount_calculator || Solidus::Returnly::Refund::AmountCalculator
6
- end
7
-
8
- # return item qualifies to be restocked (after refund)
9
- def return_item_restock_policy_class
10
- Solidus::Returnly.return_item_restock_policy || Solidus::Returnly::Refund::ReturnItemRestockPolicy
11
- end
12
-
13
- def refunder_class
14
- Solidus::Returnly.refunder || Solidus::Returnly::Refunder
15
- end
16
-
17
- def refund_calculator_class
18
- Solidus::Returnly.refund_calculator || Solidus::Returnly::RefundCalculator
19
- end
20
- end
21
- end
22
- end
@@ -1,5 +0,0 @@
1
- module Solidus
2
- module Returnly
3
- VERSION = '0.4.3'
4
- end
5
- end