solidus-returnly 0.4.3 → 0.5.0.pre

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.
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