spree_avatax_official 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env-sample +4 -0
- data/.gitignore +22 -0
- data/.rspec +3 -0
- data/.rubocop.yml +77 -0
- data/.travis.yml +78 -0
- data/Appraisals +49 -0
- data/Gemfile +16 -0
- data/LICENSE +26 -0
- data/README.md +173 -0
- data/Rakefile +21 -0
- data/app/controllers/spree/admin/avalara_entity_use_codes_controller.rb +61 -0
- data/app/controllers/spree/admin/avatax_pings_controller.rb +19 -0
- data/app/controllers/spree/admin/avatax_settings_controller.rb +26 -0
- data/app/controllers/spree/admin/users_controller_decorator.rb +27 -0
- data/app/helpers/spree_avatax_official/http_helper.rb +17 -0
- data/app/helpers/spree_avatax_official/tax_adjustment_label_helper.rb +30 -0
- data/app/models/avatax/request_decorator.rb +34 -0
- data/app/models/spree_avatax_official/avatax_log.rb +57 -0
- data/app/models/spree_avatax_official/calculator/avatax_transaction_calculator.rb +28 -0
- data/app/models/spree_avatax_official/configuration.rb +20 -0
- data/app/models/spree_avatax_official/entity_use_code.rb +7 -0
- data/app/models/spree_avatax_official/has_uuid.rb +22 -0
- data/app/models/spree_avatax_official/spree/address_decorator.rb +31 -0
- data/app/models/spree_avatax_official/spree/inventory_unit_decorator.rb +11 -0
- data/app/models/spree_avatax_official/spree/line_item_decorator.rb +27 -0
- data/app/models/spree_avatax_official/spree/order_decorator.rb +91 -0
- data/app/models/spree_avatax_official/spree/refund_decorator.rb +22 -0
- data/app/models/spree_avatax_official/spree/shipment_decorator.rb +25 -0
- data/app/models/spree_avatax_official/spree/tax_category_decorator.rb +14 -0
- data/app/models/spree_avatax_official/spree/user_decorator.rb +11 -0
- data/app/models/spree_avatax_official/transaction.rb +27 -0
- data/app/overrides/spree/admin/shared/sub_menu/_configuration.rb +10 -0
- data/app/overrides/spree/admin/sidebar.rb +14 -0
- data/app/presenters/spree_avatax_official/address_presenter.rb +40 -0
- data/app/presenters/spree_avatax_official/item_presenter.rb +92 -0
- data/app/presenters/spree_avatax_official/ship_to_address_presenter.rb +22 -0
- data/app/presenters/spree_avatax_official/transactions/adjust_presenter.rb +31 -0
- data/app/presenters/spree_avatax_official/transactions/create_presenter.rb +80 -0
- data/app/presenters/spree_avatax_official/transactions/full_refund_presenter.rb +32 -0
- data/app/presenters/spree_avatax_official/transactions/partial_refund_presenter.rb +46 -0
- data/app/services/spree_avatax_official/address/validate.rb +27 -0
- data/app/services/spree_avatax_official/base.rb +71 -0
- data/app/services/spree_avatax_official/create_tax_adjustments_service.rb +133 -0
- data/app/services/spree_avatax_official/generate_transaction_cache_key_service.rb +94 -0
- data/app/services/spree_avatax_official/get_tax_service.rb +20 -0
- data/app/services/spree_avatax_official/settings/update_service.rb +36 -0
- data/app/services/spree_avatax_official/transactions/adjust_service.rb +36 -0
- data/app/services/spree_avatax_official/transactions/create_service.rb +47 -0
- data/app/services/spree_avatax_official/transactions/find_order_transaction_service.rb +23 -0
- data/app/services/spree_avatax_official/transactions/full_refund_service.rb +36 -0
- data/app/services/spree_avatax_official/transactions/get_by_code_service.rb +32 -0
- data/app/services/spree_avatax_official/transactions/partial_refund_service.rb +33 -0
- data/app/services/spree_avatax_official/transactions/refund_service.rb +87 -0
- data/app/services/spree_avatax_official/transactions/void_service.rb +28 -0
- data/app/services/spree_avatax_official/utilities/ping_service.rb +9 -0
- data/app/views/spree/admin/avalara_entity_use_codes/_form.html.erb +20 -0
- data/app/views/spree/admin/avalara_entity_use_codes/edit.html.erb +12 -0
- data/app/views/spree/admin/avalara_entity_use_codes/index.html.erb +35 -0
- data/app/views/spree/admin/avalara_entity_use_codes/new.html.erb +12 -0
- data/app/views/spree/admin/avatax_settings/edit.html.erb +99 -0
- data/app/views/spree/admin/users/avalara_information.html.erb +39 -0
- data/app/views/spree/admin/zones/_form.html.erb +58 -0
- data/bin/rails +8 -0
- data/config/initializers/spree_permitted_attributes.rb +5 -0
- data/config/locales/en.yml +40 -0
- data/config/routes.rb +15 -0
- data/db/migrate/20190118155953_add_exemption_number_to_spree_users.rb +7 -0
- data/db/migrate/20190118160653_create_spree_avalara_entity_use_codes.rb +11 -0
- data/db/migrate/20190118161318_add_avalara_entity_use_code_reference_to_users.rb +7 -0
- data/db/migrate/20190118162345_add_vat_id_to_spree_users.rb +7 -0
- data/db/migrate/20190122154119_create_spree_avatax_official_transactions.rb +10 -0
- data/db/migrate/20190201123706_add_tax_code_to_spree_tax_categories.rb +7 -0
- data/db/migrate/20190208130136_add_avatax_company_code_to_spree_stores.rb +8 -0
- data/db/migrate/20190225145808_add_uuid_to_spree_line_items_and_spree_shipments.rb +13 -0
- data/db/migrate/20190329071439_add_included_in_price_to_spree_zones.rb +5 -0
- data/db/migrate/20191001085458_remove_spree_avalara_entity_use_codes_table.rb +12 -0
- data/db/migrate/20191001092353_create_spree_avatax_official_entity_use_codes_table.rb +18 -0
- data/db/seeds.rb +1 -0
- data/gemfiles/spree_3_1.gemfile +16 -0
- data/gemfiles/spree_3_7.gemfile +13 -0
- data/gemfiles/spree_4_0.gemfile +14 -0
- data/gemfiles/spree_4_1.gemfile +14 -0
- data/gemfiles/spree_4_2.gemfile +14 -0
- data/gemfiles/spree_master.gemfile +14 -0
- data/lib/generators/spree_avatax_official/install/install_generator.rb +38 -0
- data/lib/generators/spree_avatax_official/install/templates/config/initializers/spree_avatax_official.rb +9 -0
- data/lib/spree_avatax_official.rb +5 -0
- data/lib/spree_avatax_official/engine.rb +32 -0
- data/lib/spree_avatax_official/factories.rb +9 -0
- data/lib/spree_avatax_official/seeder.rb +51 -0
- data/lib/spree_avatax_official/version.rb +11 -0
- data/lib/tasks/load_seeds.rake +6 -0
- data/spec/.rubocop.yml +36 -0
- data/spec/factories/canada_address_factory.rb +23 -0
- data/spec/factories/europe_address_factory.rb +22 -0
- data/spec/factories/spree/avatax_global_zone_factory.rb +16 -0
- data/spec/factories/spree/avatax_order_factory.rb +69 -0
- data/spec/factories/spree/avatax_shipment_factory.rb +31 -0
- data/spec/factories/spree/avatax_shipping_method_factory.rb +35 -0
- data/spec/factories/spree/avatax_tax_category_factory.rb +17 -0
- data/spec/factories/spree/avatax_tax_rate_factory.rb +23 -0
- data/spec/factories/spree/line_item_factory.rb +13 -0
- data/spec/factories/spree/promotion_factory.rb +35 -0
- data/spec/factories/spree/shipping_calculator_factory.rb +10 -0
- data/spec/factories/spree_avatax_official/avalara_entity_use_code_factory.rb +7 -0
- data/spec/factories/spree_avatax_official/calculator/avatax_transaction_calculator_factory.rb +3 -0
- data/spec/factories/spree_avatax_official/transaction_factory.rb +12 -0
- data/spec/factories/usa_address_factory.rb +34 -0
- data/spec/features/admin/edit_avatax_settings_spec.rb +11 -0
- data/spec/features/admin/update_avatax_settings_spec.rb +200 -0
- data/spec/features/spree/admin/avalara_entity_use_codes_controller_spec.rb +71 -0
- data/spec/features/spree/admin/users_controller_spec.rb +34 -0
- data/spec/helpers/spree_avatax_official/tax_adjustment_label_helper_spec.rb +81 -0
- data/spec/lib/spree_avatax_official/seeder_spec.rb +23 -0
- data/spec/models/spree/line_item_spec.rb +76 -0
- data/spec/models/spree/order_spec.rb +210 -0
- data/spec/models/spree/shipment_spec.rb +11 -0
- data/spec/models/spree_avatax_official/avatax_log_spec.rb +87 -0
- data/spec/models/spree_avatax_official/entity_use_code_spec.rb +9 -0
- data/spec/models/spree_avatax_official/has_uuid_spec.rb +21 -0
- data/spec/models/spree_avatax_official/spree/refund_decorator_spec.rb +31 -0
- data/spec/models/spree_avatax_official/transaction_spec.rb +9 -0
- data/spec/presenters/spree_avatax_official/address_presenter_spec.rb +53 -0
- data/spec/presenters/spree_avatax_official/item_presenter_spec.rb +159 -0
- data/spec/presenters/spree_avatax_official/ship_to_address_presenter_spec.rb +34 -0
- data/spec/presenters/spree_avatax_official/transactions/adjust_presenter_spec.rb +39 -0
- data/spec/presenters/spree_avatax_official/transactions/create_presenter_spec.rb +110 -0
- data/spec/presenters/spree_avatax_official/transactions/full_refund_presenter_spec.rb +30 -0
- data/spec/presenters/spree_avatax_official/transactions/partial_refund_presenter_spec.rb +48 -0
- data/spec/services/spree_avatax_official/address/validate_spec.rb +47 -0
- data/spec/services/spree_avatax_official/create_tax_adjustments_service_spec.rb +444 -0
- data/spec/services/spree_avatax_official/generate_transaction_cache_key_service_spec.rb +29 -0
- data/spec/services/spree_avatax_official/get_tax_service_spec.rb +33 -0
- data/spec/services/spree_avatax_official/settings/update_service_spec.rb +131 -0
- data/spec/services/spree_avatax_official/transactions/adjust_service_spec.rb +55 -0
- data/spec/services/spree_avatax_official/transactions/create_service_spec.rb +262 -0
- data/spec/services/spree_avatax_official/transactions/find_order_transaction_service_spec.rb +38 -0
- data/spec/services/spree_avatax_official/transactions/full_refund_service_spec.rb +24 -0
- data/spec/services/spree_avatax_official/transactions/get_by_code_service_spec.rb +74 -0
- data/spec/services/spree_avatax_official/transactions/partial_refund_service_spec.rb +33 -0
- data/spec/services/spree_avatax_official/transactions/refund_service_spec.rb +65 -0
- data/spec/services/spree_avatax_official/transactions/void_service_spec.rb +33 -0
- data/spec/services/spree_avatax_official/utilities/ping_service_spec.rb +35 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/support/avatax.rb +15 -0
- data/spec/support/capybara.rb +21 -0
- data/spec/support/database_cleaner.rb +25 -0
- data/spec/support/factory_bot.rb +9 -0
- data/spec/support/vcr.rb +17 -0
- data/spec/vcr/SpreeAvataxOfficial_Transactions_FindOrderTransactionService/_call/with_Spree_Order/when_order_does_NOT_have_a_SalesInvoice_transaction/when_transaction_does_NOT_exist_in_avatax/returns_failure.yml +44 -0
- data/spec/vcr/SpreeAvataxOfficial_Transactions_FindOrderTransactionService/_call/with_Spree_Order/when_order_does_NOT_have_a_SalesInvoice_transaction/when_transaction_exists_in_avatax/returns_success_and_creates_a_new_SalesInvoice_transaction.yml +48 -0
- data/spec/vcr/spree_avatax_official/address/validate_failure.yml +46 -0
- data/spec/vcr/spree_avatax_official/address/validate_success.yml +43 -0
- data/spec/vcr/spree_avatax_official/address/zipcode_failure.yml +45 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/californian_stock_location.yml +60 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/completed_order_line_item_update.yml +62 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/order_adjustment_promotion.yml +61 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/shipment_adjustment_promotion.yml +60 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/simple_completed_order.yml +60 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/line_item_adjustment_promotion.yml +59 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/line_item_and_shipment.yml +60 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/multiple_line_items_multiple_quantity.yml +59 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_excluded/single_line_item.yml +55 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/line_item_adjustment_promotion.yml +59 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/line_item_and_shipment.yml +60 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/multiple_line_items_multiple_quantity.yml +59 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/tax_included/single_line_item.yml +55 -0
- data/spec/vcr/spree_avatax_official/create_tax_adjustments/with_incorrect_address.yml +97 -0
- data/spec/vcr/spree_avatax_official/get_tax/error.yml +49 -0
- data/spec/vcr/spree_avatax_official/get_tax/sucsess.yml +55 -0
- data/spec/vcr/spree_avatax_official/transactions/adjust/failure.yml +101 -0
- data/spec/vcr/spree_avatax_official/transactions/adjust/invoice_order_success.yml +117 -0
- data/spec/vcr/spree_avatax_official/transactions/create/complete_order_success.yml +60 -0
- data/spec/vcr/spree_avatax_official/transactions/create/gst/canada_to_canada.yml +55 -0
- data/spec/vcr/spree_avatax_official/transactions/create/gst/canada_to_us.yml +57 -0
- data/spec/vcr/spree_avatax_official/transactions/create/gst/us_to_canada.yml +56 -0
- data/spec/vcr/spree_avatax_official/transactions/create/invoice_order_success.yml +60 -0
- data/spec/vcr/spree_avatax_official/transactions/create/sales_order_success.yml +60 -0
- data/spec/vcr/spree_avatax_official/transactions/create/timeout.yml +54 -0
- data/spec/vcr/spree_avatax_official/transactions/create/vat/europe_to_europe.yml +57 -0
- data/spec/vcr/spree_avatax_official/transactions/create/vat/europe_to_us.yml +57 -0
- data/spec/vcr/spree_avatax_official/transactions/create/vat/us_to_europe.yml +58 -0
- data/spec/vcr/spree_avatax_official/transactions/get_by_code/error.yml +44 -0
- data/spec/vcr/spree_avatax_official/transactions/get_by_code/order_with_custom_code_success.yml +49 -0
- data/spec/vcr/spree_avatax_official/transactions/get_by_code/order_with_number_success.yml +49 -0
- data/spec/vcr/spree_avatax_official/transactions/get_by_code/order_with_type_success.yml +48 -0
- data/spec/vcr/spree_avatax_official/transactions/get_by_code/order_without_type_success.yml +48 -0
- data/spec/vcr/spree_avatax_official/transactions/refund/full_refund_success.yml +114 -0
- data/spec/vcr/spree_avatax_official/transactions/refund/partial_refund_success.yml +58 -0
- data/spec/vcr/spree_avatax_official/transactions/refund/partial_refund_with_refund_success.yml +58 -0
- data/spec/vcr/spree_avatax_official/transactions/void/success.yml +112 -0
- data/spec/vcr/spree_avatax_official/utilities/ping.yml +41 -0
- data/spec/vcr/spree_avatax_official/utilities/ping_unauthorized.yml +42 -0
- data/spec/vcr/spree_line_item/update_tax_charge/decrease_quantity.yml +55 -0
- data/spec/vcr/spree_line_item/update_tax_charge/increase_quantity.yml +55 -0
- data/spec/vcr/spree_order/california_order.yml +67 -0
- data/spec/vcr/spree_order/complete_order.yml +60 -0
- data/spec/vcr/spree_order/order_transition_to_completed.yml +112 -0
- data/spec/vcr/spree_order/order_with_line_item_adjustment.yml +57 -0
- data/spec/vcr/spree_order/order_with_order_adjustment.yml +57 -0
- data/spec/vcr/spree_order/simple_order_with_single_line_item.yml +55 -0
- data/spec/vcr/spree_order/simple_order_with_single_line_item_and_shipment.yml +57 -0
- data/spec/vcr/spree_order/update_tax_charge/create_line_item.yml +55 -0
- data/spree_avatax_official.gemspec +43 -0
- metadata +486 -0
@@ -0,0 +1,32 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
module Transactions
|
3
|
+
class FullRefundPresenter
|
4
|
+
def initialize(order:, transaction_code:)
|
5
|
+
@order = order
|
6
|
+
@transaction_code = transaction_code
|
7
|
+
end
|
8
|
+
|
9
|
+
# based on https://developer.avalara.com/api-reference/avatax/rest/v2/models/RefundTransactionModel/
|
10
|
+
def to_json
|
11
|
+
{
|
12
|
+
refundTransactionCode: transaction_code,
|
13
|
+
referenceCode: reference_code,
|
14
|
+
refundDate: refund_date,
|
15
|
+
refundType: 'Full'
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :order, :transaction_code
|
22
|
+
|
23
|
+
def reference_code
|
24
|
+
order.number
|
25
|
+
end
|
26
|
+
|
27
|
+
def refund_date
|
28
|
+
order.completed_at.strftime('%Y-%m-%d')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
module Transactions
|
3
|
+
class PartialRefundPresenter < CreatePresenter
|
4
|
+
OVERRIDE_TYPE = 'TaxDate'.freeze
|
5
|
+
OVERRIDE_REASON = 'Refund'.freeze
|
6
|
+
|
7
|
+
def initialize(order:, refund_items:, transaction_code:)
|
8
|
+
@order = order
|
9
|
+
@refund_items = refund_items
|
10
|
+
@transaction_code = transaction_code
|
11
|
+
@transaction_type = SpreeAvataxOfficial::Transaction::RETURN_INVOICE
|
12
|
+
end
|
13
|
+
|
14
|
+
# based on https://developer.avalara.com/api-reference/avatax/rest/v2/models/CreateTransactionModel/
|
15
|
+
# date should be refund date, taxDate should be order's completed_at date
|
16
|
+
def to_json
|
17
|
+
super.merge(
|
18
|
+
date: formatted_date(Time.current),
|
19
|
+
taxOverride: tax_override
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :refund_items
|
26
|
+
|
27
|
+
def tax_override
|
28
|
+
{
|
29
|
+
type: self.class::OVERRIDE_TYPE,
|
30
|
+
reason: self.class::OVERRIDE_REASON,
|
31
|
+
taxDate: formatted_date(order.completed_at)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def items_payload
|
36
|
+
refund_items.map do |item, (quantity, amount)|
|
37
|
+
SpreeAvataxOfficial::ItemPresenter.new(
|
38
|
+
item: item,
|
39
|
+
custom_quantity: quantity,
|
40
|
+
custom_amount: amount
|
41
|
+
).to_json
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
module Address
|
3
|
+
class Validate < SpreeAvataxOfficial::Base
|
4
|
+
def call(address:)
|
5
|
+
response = send_request(address)
|
6
|
+
|
7
|
+
return failure(response) if errors?(response)
|
8
|
+
|
9
|
+
success(response)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def errors?(response)
|
15
|
+
response.body['messages'] || response.body['error']
|
16
|
+
end
|
17
|
+
|
18
|
+
def send_request(address)
|
19
|
+
ship_to_address_model = SpreeAvataxOfficial::ShipToAddressPresenter.new(
|
20
|
+
address: address
|
21
|
+
).to_json
|
22
|
+
|
23
|
+
client.resolve_address(ship_to_address_model)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
class Base
|
3
|
+
prepend ::Spree::ServiceModule::Base
|
4
|
+
|
5
|
+
APP_NAME = 'a0o0b000005HsXPAA0'.freeze
|
6
|
+
APP_VERSION = 'Spree by Spark'.freeze
|
7
|
+
SUCCESS_STATUSES = [200, 201].freeze
|
8
|
+
CONNECTION_OPTIONS = ::AvaTax::Configuration::DEFAULT_CONNECTION_OPTIONS.merge(
|
9
|
+
request: {
|
10
|
+
timeout: SpreeAvataxOfficial::Config.read_timeout,
|
11
|
+
open_timeout: SpreeAvataxOfficial::Config.open_timeout
|
12
|
+
}
|
13
|
+
).freeze
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def client
|
18
|
+
AvaTax::Client.new(
|
19
|
+
app_name: APP_NAME,
|
20
|
+
app_version: APP_VERSION,
|
21
|
+
connection_options: CONNECTION_OPTIONS,
|
22
|
+
logger: true,
|
23
|
+
faraday_response: true,
|
24
|
+
endpoint: SpreeAvataxOfficial::Config.endpoint,
|
25
|
+
username: SpreeAvataxOfficial::Config.account_number,
|
26
|
+
password: SpreeAvataxOfficial::Config.license_key
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def company_code(order)
|
31
|
+
order.store&.avatax_company_code || SpreeAvataxOfficial::Config.company_code
|
32
|
+
end
|
33
|
+
|
34
|
+
def request_result(response, object = nil)
|
35
|
+
status = response.try(:status)
|
36
|
+
response_body = status ? response.body : response
|
37
|
+
|
38
|
+
if request_error?(status, response_body)
|
39
|
+
logger.error(object, response)
|
40
|
+
|
41
|
+
failure(response_body)
|
42
|
+
else
|
43
|
+
yield if block_given?
|
44
|
+
|
45
|
+
logger.info(response, object)
|
46
|
+
|
47
|
+
success(response_body)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_error?(status, response_body)
|
52
|
+
response_body['error'].present? || !status.in?(SUCCESS_STATUSES)
|
53
|
+
end
|
54
|
+
|
55
|
+
def refund_transaction_code(order_number, refundable_id)
|
56
|
+
"#{order_number}-#{refundable_id}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_transaction!(order:, code: nil, transaction_type: nil)
|
60
|
+
SpreeAvataxOfficial::Transaction.create!(
|
61
|
+
code: code || order.number,
|
62
|
+
order: order,
|
63
|
+
transaction_type: transaction_type || SpreeAvataxOfficial::Transaction::SALES_INVOICE
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def logger
|
68
|
+
@logger ||= SpreeAvataxOfficial::AvataxLog.new
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
class CreateTaxAdjustmentsService < SpreeAvataxOfficial::Base
|
3
|
+
include SpreeAvataxOfficial::TaxAdjustmentLabelHelper
|
4
|
+
|
5
|
+
def call(order:) # rubocop:disable Metrics/AbcSize
|
6
|
+
return failure(::Spree.t('spree_avatax_official.create_tax_adjustments.order_canceled')) if order.canceled?
|
7
|
+
|
8
|
+
order.all_adjustments.tax.destroy_all
|
9
|
+
|
10
|
+
return failure(::Spree.t('spree_avatax_official.create_tax_adjustments.tax_calculation_unnecessary')) unless order.avatax_tax_calculation_required?
|
11
|
+
|
12
|
+
transaction_cache_key = SpreeAvataxOfficial::GenerateTransactionCacheKeyService.call(order: order).value
|
13
|
+
|
14
|
+
avatax_response = Rails.cache.fetch(transaction_cache_key, expires_in: 5.minutes) do
|
15
|
+
send_transaction_to_avatax(order)
|
16
|
+
end
|
17
|
+
|
18
|
+
return failure(build_error_message_from_response(avatax_response.value)) if avatax_failed_response?(avatax_response)
|
19
|
+
|
20
|
+
process_avatax_items(order, avatax_response.value['lines'])
|
21
|
+
|
22
|
+
success(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def send_transaction_to_avatax(order)
|
28
|
+
if order.avatax_sales_invoice_transaction.present?
|
29
|
+
SpreeAvataxOfficial::Transactions::AdjustService.call(
|
30
|
+
order: order,
|
31
|
+
adjustment_reason: SpreeAvataxOfficial::Transaction::DEFAULT_ADJUSTMENT_REASON,
|
32
|
+
adjustment_description: ::Spree.t('spree_avatax_official.create_tax_adjustments.adjustment_description')
|
33
|
+
)
|
34
|
+
else
|
35
|
+
SpreeAvataxOfficial::Transactions::CreateService.call(order: order)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def avatax_failed_response?(avatax_response)
|
40
|
+
avatax_response.failure? || avatax_response.value['totalTax'].zero?
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_avatax_items(order, avatax_items)
|
44
|
+
avatax_items.each { |avatax_item| process_avatax_item(order, avatax_item) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_avatax_item(order, avatax_item)
|
48
|
+
tax_amount = avatax_item['taxCalculated']
|
49
|
+
|
50
|
+
return if tax_amount.zero?
|
51
|
+
|
52
|
+
item_suffix = avatax_item['lineNumber'].slice(0..2)
|
53
|
+
item_id = avatax_item['lineNumber'].slice(3..-1)
|
54
|
+
item = find_item(order, item_id, item_suffix)
|
55
|
+
|
56
|
+
# Spree allows to setup shipping methods without tax category and
|
57
|
+
# in that case it doesn't make sense to collect any tax,
|
58
|
+
# especially because of validation that requires presence of tax category
|
59
|
+
return if item.tax_category.nil?
|
60
|
+
|
61
|
+
tax_rate = find_or_create_tax_rate(item, avatax_item)
|
62
|
+
|
63
|
+
store_pre_tax_amount(item, tax_rate, tax_amount)
|
64
|
+
|
65
|
+
create_tax_adjustment(item, tax_rate, tax_amount)
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_item(order, uuid, suffix)
|
69
|
+
case suffix
|
70
|
+
when 'LI-'
|
71
|
+
order.line_items.find_by(avatax_uuid: uuid)
|
72
|
+
when 'FR-'
|
73
|
+
order.shipments.find_by(avatax_uuid: uuid)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_or_create_tax_rate(item, avatax_item)
|
78
|
+
::Spree::TaxRate.find_or_create_by!(
|
79
|
+
name: tax_rate_name,
|
80
|
+
amount: sum_rates_from_details(avatax_item),
|
81
|
+
zone: item.tax_zone&.reload,
|
82
|
+
tax_category: item.tax_category,
|
83
|
+
show_rate_in_label: false,
|
84
|
+
included_in_price: item.included_in_price
|
85
|
+
) do |tax_rate|
|
86
|
+
tax_rate.calculator = SpreeAvataxOfficial::Calculator::AvataxTransactionCalculator.new
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def tax_rate_name
|
91
|
+
ENV.fetch('AVATAX_TAX_RATE_NAME', 'AvaTax Official Tax Rate')
|
92
|
+
end
|
93
|
+
|
94
|
+
def create_tax_adjustment(item, source, amount)
|
95
|
+
item.adjustments.create!(
|
96
|
+
source: source,
|
97
|
+
amount: amount,
|
98
|
+
included: item.included_in_price,
|
99
|
+
label: tax_adjustment_label(item, source.amount),
|
100
|
+
order: item.order
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
def store_pre_tax_amount(item, tax_rate, tax_amount)
|
105
|
+
pre_tax_amount = case item.class.name.demodulize
|
106
|
+
when 'LineItem' then item.discounted_amount
|
107
|
+
when 'Shipment' then item.discounted_cost
|
108
|
+
end
|
109
|
+
|
110
|
+
pre_tax_amount -= tax_amount if tax_rate.included_in_price?
|
111
|
+
|
112
|
+
item.update_column(:pre_tax_amount, pre_tax_amount)
|
113
|
+
end
|
114
|
+
|
115
|
+
def sum_rates_from_details(avatax_item)
|
116
|
+
avatax_item['details']
|
117
|
+
.sum { |detail_entry| detail_entry['rate'] }
|
118
|
+
.round(6)
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_error_message_from_response(avatax_response)
|
122
|
+
return ::Spree.t('spree_avatax_official.create_tax_adjustments.tax_calculation_failed') unless error_present?(avatax_response)
|
123
|
+
|
124
|
+
avatax_response['error']['details'].map do |error_detail_entry|
|
125
|
+
"#{error_detail_entry['number']} - #{error_detail_entry['message']} - #{error_detail_entry['description']}."
|
126
|
+
end.join(' ')
|
127
|
+
end
|
128
|
+
|
129
|
+
def error_present?(avatax_response)
|
130
|
+
avatax_response && avatax_response['error'].present?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
class GenerateTransactionCacheKeyService < SpreeAvataxOfficial::Base
|
3
|
+
def call(order:)
|
4
|
+
reloaded_order = order.class.includes(
|
5
|
+
:line_items,
|
6
|
+
:adjustments,
|
7
|
+
:shipments => :stock_location,
|
8
|
+
order.tax_address_symbol => %i[state country]
|
9
|
+
).find(order.id)
|
10
|
+
|
11
|
+
success(transaction_cache_key(reloaded_order))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def transaction_cache_key(order)
|
17
|
+
transaction_cache_key = [
|
18
|
+
order_cache_key(order),
|
19
|
+
tax_address_cache_key(order.tax_address),
|
20
|
+
line_items_cache_key(order),
|
21
|
+
shipments_cache_key(order.shipments),
|
22
|
+
avatax_preferences_cache_key(order)
|
23
|
+
].join('-')
|
24
|
+
|
25
|
+
# Cache key length has to be compressed as max_length is 250:
|
26
|
+
# https://github.com/memcached/memcached/blob/master/memcached.h#L36
|
27
|
+
"AvaTax-transaction-#{Digest::MD5.hexdigest(transaction_cache_key)}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def order_cache_key(order)
|
31
|
+
[
|
32
|
+
order.number,
|
33
|
+
order.avatax_discount_amount
|
34
|
+
].join('-')
|
35
|
+
end
|
36
|
+
|
37
|
+
def tax_address_cache_key(tax_address)
|
38
|
+
[
|
39
|
+
tax_address.address1,
|
40
|
+
tax_address.address2,
|
41
|
+
tax_address.city,
|
42
|
+
tax_address.state.try(:abbr),
|
43
|
+
tax_address.country.try(:iso),
|
44
|
+
tax_address.zipcode
|
45
|
+
].join('-')
|
46
|
+
end
|
47
|
+
|
48
|
+
def line_items_cache_key(order) # rubocop:disable Metrics/MethodLength
|
49
|
+
order.line_items.map do |line_item|
|
50
|
+
[
|
51
|
+
line_item.avatax_uuid,
|
52
|
+
line_item.quantity,
|
53
|
+
line_item.variant_id,
|
54
|
+
line_item.price,
|
55
|
+
line_item.amount,
|
56
|
+
line_item.discounted_amount,
|
57
|
+
order.line_items_discounted_in_avatax?,
|
58
|
+
line_item.avatax_tax_code
|
59
|
+
].join('-')
|
60
|
+
end.join('-')
|
61
|
+
end
|
62
|
+
|
63
|
+
def shipments_cache_key(shipments)
|
64
|
+
shipments.map do |shipment|
|
65
|
+
[
|
66
|
+
shipment.avatax_uuid,
|
67
|
+
shipment.discounted_cost,
|
68
|
+
shipment.avatax_tax_code,
|
69
|
+
stock_location_cache_key(shipment.stock_location)
|
70
|
+
].join('-')
|
71
|
+
end.join('-')
|
72
|
+
end
|
73
|
+
|
74
|
+
def stock_location_cache_key(stock_location)
|
75
|
+
[
|
76
|
+
stock_location.address1,
|
77
|
+
stock_location.address2,
|
78
|
+
stock_location.city,
|
79
|
+
stock_location.state.try(:abbr),
|
80
|
+
stock_location.country.try(:iso)
|
81
|
+
].join('-')
|
82
|
+
end
|
83
|
+
|
84
|
+
def avatax_preferences_cache_key(order)
|
85
|
+
ship_from_address_timestamp = ship_from_address_preference.try(:updated_at).try(:utc).try(:to_s, :number)
|
86
|
+
|
87
|
+
"#{company_code(order)}-#{ship_from_address_timestamp}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def ship_from_address_preference
|
91
|
+
::Spree::Preference.find_by(key: 'spree_avatax_official/configuration/ship_from_address')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
class GetTaxService < SpreeAvataxOfficial::Base
|
3
|
+
def call(order:, options: {})
|
4
|
+
response = send_request(order, options)
|
5
|
+
|
6
|
+
return failure(response) if response.body['error'].present?
|
7
|
+
|
8
|
+
success(taxCalculated: response.body['totalTaxCalculated'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def send_request(order, options)
|
12
|
+
create_transaction_model = SpreeAvataxOfficial::Transactions::CreatePresenter.new(
|
13
|
+
order: order,
|
14
|
+
transaction_type: SpreeAvataxOfficial::Transaction::SALES_ORDER
|
15
|
+
).to_json
|
16
|
+
|
17
|
+
client.create_transaction(create_transaction_model, options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SpreeAvataxOfficial
|
2
|
+
module Settings
|
3
|
+
class UpdateService < SpreeAvataxOfficial::Base
|
4
|
+
def call(params:)
|
5
|
+
update_settings(params)
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def update_settings(params)
|
11
|
+
update_address_settings(params[:ship_from])
|
12
|
+
|
13
|
+
SpreeAvataxOfficial::Config.account_number = params[:account_number] if params.key?(:account_number)
|
14
|
+
SpreeAvataxOfficial::Config.license_key = params[:license_key] if params.key?(:license_key)
|
15
|
+
SpreeAvataxOfficial::Config.company_code = params[:company_code] if params.key?(:company_code)
|
16
|
+
SpreeAvataxOfficial::Config.endpoint = params[:endpoint] if params.key?(:endpoint)
|
17
|
+
SpreeAvataxOfficial::Config.address_validation_enabled = params[:address_validation_enabled] if params.key?(:address_validation_enabled)
|
18
|
+
SpreeAvataxOfficial::Config.commit_transaction_enabled = params[:commit_transaction_enabled] if params.key?(:commit_transaction_enabled)
|
19
|
+
SpreeAvataxOfficial::Config.enabled = params[:enabled] if params.key?(:enabled)
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_address_settings(ship_from_params)
|
23
|
+
return unless ship_from_params
|
24
|
+
|
25
|
+
SpreeAvataxOfficial::Config.ship_from_address = {
|
26
|
+
line1: ship_from_params[:line1],
|
27
|
+
line2: ship_from_params[:line2],
|
28
|
+
city: ship_from_params[:city],
|
29
|
+
region: ship_from_params[:region],
|
30
|
+
country: ship_from_params[:country],
|
31
|
+
postalCode: ship_from_params[:postal_code]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|