diff --git a/.circleci/config.yml b/.circleci/config.yml index a3e9229..77eb92d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: '2.1' jobs: build-ruby-27: - docker: + docker: - image: circleci/ruby:2.7.0 steps: - checkout @@ -17,9 +17,9 @@ jobs: paths: - vendor/bundle - run: bundle exec rspec - + build-ruby-25: - docker: + docker: - image: circleci/ruby:2.5.3 steps: - checkout @@ -28,7 +28,7 @@ jobs: - rubygems-25-cache-{{ .Branch }}-{{ checksum "Gemfile" }}-{{ checksum "flex-commerce-api.gemspec" }} - rubygems-25-cache-{{ .Branch }} - rubygems-25-cache - - run: gem install bundler -v "~> 1.17" + - run: gem install bundler -v "~> 2.1" - run: bundle install --path vendor/bundle - save_cache: key: rubygems-25-cache-{{ .Branch }}-{{ checksum "Gemfile" }}-{{ checksum "flex-commerce-api.gemspec" }} diff --git a/.rubocop.yml b/.rubocop.yml index bc73900..69c9c95 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -464,7 +464,7 @@ Style/TrivialAccessors: - to_str - to_s - to_sym -Style/VariableName: +Naming/VariableName: Description: Use the configured style when naming variables. StyleGuide: https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars Enabled: true @@ -1062,4 +1062,4 @@ Lint/Void: Enabled: false Rails/Delegate: Description: Prefer delegate method for delegations. - Enabled: false \ No newline at end of file + Enabled: false diff --git a/app/models/cart.rb b/app/models/cart.rb index c7b88d0..4928c2c 100644 --- a/app/models/cart.rb +++ b/app/models/cart.rb @@ -128,9 +128,7 @@ def available_shipping_methods private def stock_levels - StockLevel.where(skus: line_items.map { |li| li.item.sku }.join(",")).all + StockLevel.with_params(fields: {stock_levels: "stock_available"}).where(skus: line_items.map { |li| li.item.sku }.join(",")).all end - - end end diff --git a/app/models/customer_account.rb b/app/models/customer_account.rb index fe8c76e..a148288 100644 --- a/app/models/customer_account.rb +++ b/app/models/customer_account.rb @@ -54,22 +54,22 @@ def self.authenticate(attributes = {}) nil end - def self.find_by_email(email) - requestor.custom("email:#{URI.encode_www_form_component(email)}", {request_method: :get}, {}).first + def self.find_by_email(email, options = {}) + requestor.custom("email:#{URI.encode_www_form_component(email)}", {request_method: :get}, options).first rescue ::FlexCommerceApi::Error::NotFound nil end - def self.find_by_reference(reference) - requestor.custom("reference:#{reference}", {request_method: :get}, {}).first + def self.find_by_reference(reference, options = {}) + requestor.custom("reference:#{reference}", {request_method: :get}, options).first rescue ::FlexCommerceApi::Error::NotFound nil end # Find customer account by password reset token provided in email's link # Used in reset password scenario - def self.find_by_token(token) - requestor.custom("token:#{token}", {request_method: :get}, {}).first + def self.find_by_token(token, options = {}) + requestor.custom("token:#{token}", {request_method: :get}, options).first rescue ::FlexCommerceApi::Error::NotFound nil end diff --git a/app/models/oms/billing_address.rb b/app/models/oms/billing_address.rb new file mode 100644 index 0000000..c66e880 --- /dev/null +++ b/app/models/oms/billing_address.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class BillingAddress < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/customer.rb b/app/models/oms/customer.rb new file mode 100644 index 0000000..49ee4dc --- /dev/null +++ b/app/models/oms/customer.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class Customer < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/customer_order.rb b/app/models/oms/customer_order.rb new file mode 100644 index 0000000..d8c7096 --- /dev/null +++ b/app/models/oms/customer_order.rb @@ -0,0 +1,15 @@ +require "flex_commerce_api/oms/api_base" +require "flex_commerce_api/api_base" +module FlexCommerce + module OMS + class CustomerOrder < FlexCommerceApi::OMS::ApiBase + has_many :payments, class_name: "::FlexCommerce::OMS::Payments" + has_many :line_items, class_name: "::FlexCommerce::OMS::LineItem" + has_many :discounts, class_name: "::FlexCommerce::OMS::Discount" + has_many :shipping_address, class_name: "::FlexCommerce::OMS::Address" + has_one :billing_address, class_name: "::FlexCommerce::OMS::Address" + has_one :customer, class_name: "::FlexCommerce::OMS::Customer" + has_one :shipping_method, class_name: "::FlexCommerce::OMS::ShippingMethod" + end + end +end diff --git a/app/models/oms/discount.rb b/app/models/oms/discount.rb new file mode 100644 index 0000000..9591254 --- /dev/null +++ b/app/models/oms/discount.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class Discount < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/line_item.rb b/app/models/oms/line_item.rb new file mode 100644 index 0000000..e3dfe7c --- /dev/null +++ b/app/models/oms/line_item.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class LineItem < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/payment.rb b/app/models/oms/payment.rb new file mode 100644 index 0000000..a97a7d9 --- /dev/null +++ b/app/models/oms/payment.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class Payment < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/shipping_address.rb b/app/models/oms/shipping_address.rb new file mode 100644 index 0000000..f842598 --- /dev/null +++ b/app/models/oms/shipping_address.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class ShippingAddress < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/oms/shipping_method.rb b/app/models/oms/shipping_method.rb new file mode 100644 index 0000000..c52383b --- /dev/null +++ b/app/models/oms/shipping_method.rb @@ -0,0 +1,9 @@ +require "flex_commerce_api/oms/api_base" + +module FlexCommerce + module OMS + class ShippingMethod < FlexCommerceApi::OMS::ApiBase + belongs_to :customer_order, class_name: "::FlexCommerce::OMS::CustomerOrder" + end + end +end diff --git a/app/models/price_entry.rb b/app/models/price_entry.rb new file mode 100644 index 0000000..c4010b3 --- /dev/null +++ b/app/models/price_entry.rb @@ -0,0 +1,15 @@ +require "flex_commerce_api/api_base" + +module FlexCommerce + class PriceEntry < FlexCommerceApi::ApiBase + belongs_to :variant + + property :variant_reference + property :starts_at + property :ends_at + property :was_price + property :current_price + property :created_at + property :updated_at + end +end diff --git a/app/models/variant.rb b/app/models/variant.rb index af6880a..faf9e1d 100644 --- a/app/models/variant.rb +++ b/app/models/variant.rb @@ -11,6 +11,8 @@ class Variant < FlexCommerceApi::ApiBase has_one :product has_many :asset_files has_many :markdown_prices + has_many :price_entries + def add_markdown_prices(markdown_prices) self.class.requestor.custom("relationships/markdown_prices", {request_method: :post}, {id: id, data: markdown_prices.map(&:as_relation)}) end diff --git a/flex-commerce-api.gemspec b/flex-commerce-api.gemspec index 832f96a..f520485 100644 --- a/flex-commerce-api.gemspec +++ b/flex-commerce-api.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_development_dependency "bundler", ">= 1.17" + spec.add_development_dependency "bundler", "~> 2.1" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.3" spec.add_development_dependency "webmock", "~> 1.21" @@ -33,7 +33,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency "json_matchers", ["~> 0.5", ">= 0.5.1"] spec.add_dependency "oj", "~> 2.12" - spec.add_runtime_dependency "json_api_client", "1.5.3" + spec.add_runtime_dependency "json_api_client", "1.6.0" spec.add_runtime_dependency "activesupport", ">= 4.0" spec.add_runtime_dependency "rack", ">= 1.6" spec.add_runtime_dependency "faraday-http-cache", "1.3.0" diff --git a/lib/flex_commerce.rb b/lib/flex_commerce.rb index 394ebea..79ff39b 100644 --- a/lib/flex_commerce.rb +++ b/lib/flex_commerce.rb @@ -12,6 +12,18 @@ module V2 autoload :UnallocateOrder, File.join(FlexCommerce.gem_root, "app", "models", "v2", "unallocate_order") end + # OMS + module OMS + autoload :BillingAddress, File.join(FlexCommerce.gem_root, "app", "models", "oms", "billing_address") + autoload :CustomerOrder, File.join(FlexCommerce.gem_root, "app", "models", "oms", "customer_order") + autoload :Customer, File.join(FlexCommerce.gem_root, "app", "models", "oms", "customer") + autoload :Discount, File.join(FlexCommerce.gem_root, "app", "models", "oms", "discount") + autoload :LineItem, File.join(FlexCommerce.gem_root, "app", "models", "oms", "line_item") + autoload :Payment, File.join(FlexCommerce.gem_root, "app", "models", "oms", "payment") + autoload :ShippingAddress, File.join(FlexCommerce.gem_root, "app", "models", "oms", "shipping_address") + autoload :ShippingMethod, File.join(FlexCommerce.gem_root, "app", "models", "oms", "shipping_method") + end + # V1 Models autoload :Address, File.join(gem_root, "app", "models", "address") autoload :AssetFile, File.join(gem_root, "app", "models", "asset_file") @@ -54,6 +66,7 @@ module V2 autoload :PaymentProvider, File.join(gem_root, "app", "models", "payment_provider") autoload :PaymentProviderSetup, File.join(gem_root, "app", "models", "payment_provider_setup") autoload :PaymentTransaction, File.join(gem_root, "app", "models", "payment_transaction") + autoload :PriceEntry, File.join(gem_root, "app", "models", "price_entry") autoload :Product, File.join(gem_root, "app", "models", "product") autoload :ProductAssetFile, File.join(gem_root, "app", "models", "product_asset_file") autoload :Promotion, File.join(gem_root, "app", "models", "promotion") diff --git a/lib/flex_commerce_api/base_resource.rb b/lib/flex_commerce_api/base_resource.rb index 5f900b8..051652a 100644 --- a/lib/flex_commerce_api/base_resource.rb +++ b/lib/flex_commerce_api/base_resource.rb @@ -33,6 +33,7 @@ class BaseResource < JsonApiClient::Resource self.requestor_class = JsonApiClientExtension::Requestor self.connection_class = ::FlexCommerceApi::JsonApiClientExtension::FlexibleConnection self.parser = ::FlexCommerceApi::JsonApiClientExtension::Parsers::Parser + self.keep_request_params = true class << self def create!(*args) @@ -246,9 +247,6 @@ def get_related(relationship) end FlexCommerceApi::JsonApiClientExtension::RemoteBuilder.new(association.association_class, path: path, connection: connection) end - end - - end end diff --git a/lib/flex_commerce_api/json_api_client_extension/builder.rb b/lib/flex_commerce_api/json_api_client_extension/builder.rb index be7479a..be07936 100644 --- a/lib/flex_commerce_api/json_api_client_extension/builder.rb +++ b/lib/flex_commerce_api/json_api_client_extension/builder.rb @@ -1,28 +1,45 @@ module FlexCommerceApi module JsonApiClientExtension class Builder < ::JsonApiClient::Query::Builder - def initialize(*) - super - @temp_search_criteria = nil + def initialize(klass, opts = {}) + super(klass, opts) + @temp_search_criteria = opts.fetch(:temp_search_criteria, nil) end + def temp_search(options = {}) @temp_search_criteria = options - self + _new_scope end def find(args = {}) case args - when Hash - where(args) - else - @primary_key = args + when Hash + scope = where(args) + else + scope = _new_scope( primary_key: args ) end if @temp_search_criteria.nil? - klass.requestor.get(params) + klass.requestor.get(scope.params) else - klass.requestor.custom(:search, { request_method: :get }, params.merge(filter: @temp_search_criteria)) + klass.requestor.custom(:search, { request_method: :get }, scope.params.merge(filter: @temp_search_criteria)) end end + + private + + def _new_scope( opts = {} ) + self.class.new( @klass, + primary_key: opts.fetch( :primary_key, @primary_key ), + pagination_params: @pagination_params.merge( opts.fetch( :pagination_params, {} ) ), + path_params: @path_params.merge( opts.fetch( :path_params, {} ) ), + additional_params: @additional_params.merge( opts.fetch( :additional_params, {} ) ), + filters: @filters.merge( opts.fetch( :filters, {} ) ), + includes: @includes + opts.fetch( :includes, [] ), + orders: @orders + opts.fetch( :orders, [] ), + fields: @fields + opts.fetch( :fields, [] ), + temp_search_criteria: @temp_search_criteria, + ) + end end end end diff --git a/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb b/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb index 3cb5599..077319d 100644 --- a/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb +++ b/lib/flex_commerce_api/json_api_client_extension/remote_builder.rb @@ -1,28 +1,43 @@ module FlexCommerceApi module JsonApiClientExtension class RemoteBuilder < ::JsonApiClient::Query::Builder - def initialize(klass, path: klass.path, connection: klass.connection) - super(klass) - self.connection = connection - self.path = path + def initialize(klass, **options) + super(klass, **options) + @connection = options[:connection] + @path = options[:path] end def find(args = {}) case args - when Hash - where(args) - else - @primary_key = args + when Hash + scope = where(args) + else + scope = _new_scope( primary_key: args ) end - get_request(params) + get_request(scope.params) end private def get_request(params) - klass.parser.parse(klass, connection.run(:get, path, params, klass.custom_headers)) + klass.parser.parse(klass, connection.run(:get, path, params: params, headers: klass.custom_headers)) end + + def _new_scope( opts = {} ) + self.class.new( @klass, + primary_key: opts.fetch( :primary_key, @primary_key ), + pagination_params: @pagination_params.merge( opts.fetch( :pagination_params, {} ) ), + path_params: @path_params.merge( opts.fetch( :path_params, {} ) ), + additional_params: @additional_params.merge( opts.fetch( :additional_params, {} ) ), + filters: @filters.merge( opts.fetch( :filters, {} ) ), + includes: @includes + opts.fetch( :includes, [] ), + orders: @orders + opts.fetch( :orders, [] ), + fields: @fields + opts.fetch( :fields, [] ), + connection: opts.fetch( :connection, connection), + path: opts.fetch( :path, path)) + end + attr_accessor :path, :connection end end diff --git a/lib/flex_commerce_api/json_api_client_extension/requestor.rb b/lib/flex_commerce_api/json_api_client_extension/requestor.rb index bb4b213..a228ed4 100644 --- a/lib/flex_commerce_api/json_api_client_extension/requestor.rb +++ b/lib/flex_commerce_api/json_api_client_extension/requestor.rb @@ -3,25 +3,31 @@ module JsonApiClientExtension class Requestor < ::JsonApiClient::Query::Requestor # expects a record def create(record) - request(:post, klass.path(record.attributes, record), { - data: record.as_json_api - }) + request( + :post, + klass.path(record.attributes, record), + body: { data: record.as_json_api }, + params: record.request_params&.to_params + ) end def update(record) - request(:patch, resource_path(record.attributes, record), { - data: record.as_json_api - }) + request( + :patch, + resource_path(record.attributes, record), + body: { data: record.as_json_api }, + params: record.request_params&.to_params + ) end def get(params = {}) path = resource_path(params) params.delete(klass.primary_key) - request(:get, path, params) + request(:get, path, params: params) end def destroy(record) - request(:delete, resource_path(record.attributes, record), {}) + request(:delete, resource_path(record.attributes, record)) end protected @@ -39,4 +45,4 @@ def encoded(part) end end end -end \ No newline at end of file +end diff --git a/lib/flex_commerce_api/oms/api_base.rb b/lib/flex_commerce_api/oms/api_base.rb new file mode 100644 index 0000000..54ade02 --- /dev/null +++ b/lib/flex_commerce_api/oms/api_base.rb @@ -0,0 +1,13 @@ +require "flex_commerce_api/base_resource" + +module FlexCommerceApi + module OMS + class ApiBase < FlexCommerceApi::BaseResource + def self.endpoint_version + "v1/oms" + end + + reconfigure + end + end +end diff --git a/lib/flex_commerce_api/version.rb b/lib/flex_commerce_api/version.rb index aaf7496..f9e5154 100644 --- a/lib/flex_commerce_api/version.rb +++ b/lib/flex_commerce_api/version.rb @@ -1,3 +1,3 @@ module FlexCommerceApi - VERSION = "0.8.3" + VERSION = "0.8.4.4" end diff --git a/lib/paypal_express/auth.rb b/lib/paypal_express/auth.rb index 9786745..6005edf 100644 --- a/lib/paypal_express/auth.rb +++ b/lib/paypal_express/auth.rb @@ -6,14 +6,14 @@ module FlexCommerce module PaypalExpress # @class Setup - # + # # This service authorises the payment via the Paypal gateway class Auth include ::Retry include ::FlexCommerce::PaypalExpress::Api - + DEFAULT_CURRENCY = "GBP" - + # @initialize # # @param {String} token - Paypal token @@ -29,9 +29,9 @@ def initialize(cart:, token:, payer_id:, payment_transaction:, gateway_class: :: def call process_with_gateway end - + private - + attr_accessor :cart, :token, :payer_id, :payment_transaction, :gateway_class def process_with_gateway @@ -63,7 +63,7 @@ def process_with_gateway def do_express_checkout_payment Retry.call(no_of_retries: no_of_retires, rescue_errors: ::ActiveMerchant::ConnectionError) { ::NewRelic::Agent.increment_metric('Custom/Paypal/Do_Express_Checkout_Payment') if defined?(NewRelic::Agent) - gateway.order(convert_amount(cart.total), token: token, payer_id: payer_id, currency: DEFAULT_CURRENCY) + gateway.order(convert_amount(payment_transaction.amount), token: token, payer_id: payer_id, currency: DEFAULT_CURRENCY) } end @@ -71,7 +71,7 @@ def do_express_checkout_payment def do_authorization(response) Retry.call(no_of_retries: no_of_retires, rescue_errors: ::ActiveMerchant::ConnectionError) { ::NewRelic::Agent.increment_metric('Custom/Paypal/Do_Auhtorization') if defined?(NewRelic::Agent) - gateway.authorize_transaction(response.params["transaction_id"], convert_amount(cart.total), transaction_entity: "Order", currency: DEFAULT_CURRENCY, payer_id: payer_id) + gateway.authorize_transaction(response.params["transaction_id"], convert_amount(payment_transaction.amount), transaction_entity: "Order", currency: DEFAULT_CURRENCY, payer_id: payer_id) } end diff --git a/lib/paypal_express/generate_summary.rb b/lib/paypal_express/generate_summary.rb index 62f6735..ca07250 100644 --- a/lib/paypal_express/generate_summary.rb +++ b/lib/paypal_express/generate_summary.rb @@ -4,22 +4,23 @@ module FlexCommerce module PaypalExpress # @class GenerateSummary - # + # # This class is used while setting up the paypal for FE # It deals with line items total, sub total, tax calculations and # Also deals with discounted line items and discounts inorder to send to paypal - # + # class GenerateSummary include ::FlexCommerce::PaypalExpress::Api - - def initialize(cart: , use_tax: false) + + def initialize(cart: , use_tax: false, gift_card_amount:) self.cart = cart self.use_tax = use_tax + self.gift_card_amount = gift_card_amount raise "use_tax is not yet supported. FlexCommerce::PaypalExpress::GenerateSummary should support it in the future" if use_tax end # @method call - # + # # @returns an object with subtotal, tax, handling, shipping and items keys def call { @@ -34,35 +35,35 @@ def call private # @method subtotal - # + # # @returns the sum of line items total. This doesnt include any promotions def subtotal items.sum {|i| i[:quantity] * (i[:amount])} end # @method total - # + # # @return amount after converting cart total from pence to pounds def total convert_amount(cart.total) end # @method tax - # + # # @returns the sum of total line items tax def tax items.sum {|i| i[:tax] * i[:quantity]} end # @method handling - # + # # @returns Payment handling charges, which is 0 def handling 0 end # @method shipping - # + # # @returns 0 if cart is eligible for free shipping # @returns cart.shipping_total, if cart is not eligibl for free shipping def shipping @@ -71,14 +72,14 @@ def shipping end # @mthod items - # + # # @returns both line items and discounts def items - normal_items + discount_items + normal_items + discount_items + gift_cards end # @method discounts - # + # # @returns [] if there are no discounts on cart # @returns Array containing about the total discount amount, if any applied. def discount_items @@ -95,8 +96,26 @@ def discount_items ] end + # @method gift_cards + # + # @returns [] if there are no gift cards on cart + # @returns Array containing total gift card amount, if any applied. + def gift_cards + return [] if gift_card_amount.nil? || gift_card_amount.zero? + [ + { + name: "Gift card", + number: "NA", + quantity: 1, + amount: convert_amount(BigDecimal(0) - gift_card_amount), + description: "Gift card", + tax: 0 + } + ] + end + # @method normal_items - # + # # @returns Object, the normal line items added to cart # @note these line items unit prices will be without any discounts def normal_items @@ -112,7 +131,7 @@ def normal_items end end - attr_accessor :cart, :use_tax + attr_accessor :cart, :use_tax, :gift_card_amount end end -end \ No newline at end of file +end diff --git a/lib/paypal_express/process/paypal_params.rb b/lib/paypal_express/process/paypal_params.rb index 5e7923d..3b3d617 100644 --- a/lib/paypal_express/process/paypal_params.rb +++ b/lib/paypal_express/process/paypal_params.rb @@ -9,9 +9,9 @@ class PaypalParams include ::FlexCommerce::PaypalExpress::Api DEFAULT_DESCRIPTION = "Shift Commerce Order".freeze - + # @initialize - # + # # @param {FlexCommerce::PaymentProviderSetup} payment_provider_setup # @param {FlexCommerce::Cart} cart # @param {Paypal Gateway} [gateway_class = ::ActiveMerchant::Billing::PaypalExpressGateway] @@ -23,8 +23,9 @@ class PaypalParams # @param {FlexCommerce::ShippingMethod} shipping_method_model = FlexCommerce::ShippingMethod # @param {boolean} [use_mobile_payments = false] # @param {String} [description] - # - def initialize(cart:,success_url:, cancel_url:, ip_address:, allow_shipping_change: true, callback_url:, shipping_method_model: FlexCommerce::ShippingMethod, use_mobile_payments: false, description:) + # @param {BigDecimal} [gift_card_amount] + # + def initialize(cart:,success_url:, cancel_url:, ip_address:, allow_shipping_change: true, callback_url:, shipping_method_model: FlexCommerce::ShippingMethod, use_mobile_payments: false, description:, gift_card_amount: nil) self.cart = cart self.allow_shipping_change = allow_shipping_change self.success_url = success_url @@ -34,6 +35,7 @@ def initialize(cart:,success_url:, cancel_url:, ip_address:, allow_shipping_chan self.shipping_method_model = shipping_method_model self.use_mobile_payments = use_mobile_payments self.description = description + self.gift_card_amount = gift_card_amount end def call @@ -44,7 +46,7 @@ def call .merge(shipping_options_params) end - attr_accessor :description, :cart, :success_url, :cancel_url, :ip_address, :allow_shipping_change, :callback_url, :shipping_method_model, :use_mobile_payments + attr_accessor :description, :cart, :success_url, :cancel_url, :ip_address, :allow_shipping_change, :callback_url, :shipping_method_model, :use_mobile_payments, :gift_card_amount private @@ -87,16 +89,16 @@ def paypal_items def ui_callback_params return {} unless allow_shipping_change && shipping_methods.count > 0 - { + { callback_url: callback_url, callback_timeout: 6, callback_version: 95, - max_amount: convert_amount((cart.total * 1.2) + shipping_methods.last.total + shipping_methods.last.tax) + max_amount: convert_amount((cart.total * 1.2) + shipping_methods.last.total + shipping_methods.last.tax) } end # @method shipping_methods - # + # # @returns shipping methods with promotions applied def shipping_methods @shipping_methods ||= ShippingMethodsForCart.new(cart: cart, shipping_methods: shipping_method_model.all).call.sort_by(&:total) @@ -115,9 +117,9 @@ def shipping_options_params end def summary - @summary ||= GenerateSummary.new(cart: cart).call + @summary ||= GenerateSummary.new(cart: cart, gift_card_amount: gift_card_amount).call end end end end -end \ No newline at end of file +end diff --git a/lib/paypal_express/process/response_parser.rb b/lib/paypal_express/process/response_parser.rb index c2129c1..338f96d 100644 --- a/lib/paypal_express/process/response_parser.rb +++ b/lib/paypal_express/process/response_parser.rb @@ -19,7 +19,8 @@ def call shipping_method_id: get_shipping_method_details, email: get_email_address, shipping_address_attributes: get_shipping_address_attributes, - billing_address_attributes: get_billing_address_attributes + billing_address_attributes: get_billing_address_attributes, + payment_details: response.params["PaymentDetails"] } end diff --git a/lib/paypal_express/setup.rb b/lib/paypal_express/setup.rb index 726e040..0340cb2 100644 --- a/lib/paypal_express/setup.rb +++ b/lib/paypal_express/setup.rb @@ -4,14 +4,14 @@ module FlexCommerce module PaypalExpress # @class Setup - # + # # This is the main class, which talks to ActiveMerchant gem to initiate a transaction using Paypal class Setup include ::FlexCommerce::PaypalExpress::Api - + # @initialize - # + # # @param {FlexCommerce::PaymentProviderSetup} payment_provider_setup # @param {FlexCommerce::Cart} cart # @param {Paypal Gateway} [gateway_class = ::ActiveMerchant::Billing::PaypalExpressGateway] @@ -23,12 +23,12 @@ class Setup # @param {FlexCommerce::ShippingMethod} shipping_method_model = FlexCommerce::ShippingMethod # @param {boolean} [use_mobile_payments = false] # @param {String} [description = nil] - # + # # @note: # For `::ActiveMerchant::Billing::PaypalExpressGateway` to work # rails-site should include active merchant gem. Ideally this gem should be included in the gemspec. # But as we are using custom gem, which is not published to ruby gems, there is no way of including it within this gem dependency - def initialize(cart:, gateway_class: ::ActiveMerchant::Billing::PaypalExpressGateway, success_url:, cancel_url:, ip_address:, allow_shipping_change: true, callback_url:, shipping_method_model: FlexCommerce::ShippingMethod, use_mobile_payments: false, description: nil) + def initialize(cart:, gateway_class: ::ActiveMerchant::Billing::PaypalExpressGateway, success_url:, cancel_url:, ip_address:, allow_shipping_change: true, callback_url:, shipping_method_model: FlexCommerce::ShippingMethod, use_mobile_payments: false, description: nil, gift_card_amount: BigDecimal(0)) self.gateway_class = gateway_class self.cart = cart self.allow_shipping_change = allow_shipping_change @@ -39,12 +39,13 @@ def initialize(cart:, gateway_class: ::ActiveMerchant::Billing::PaypalExpressGat self.shipping_method_model = shipping_method_model self.use_mobile_payments = use_mobile_payments self.description = description + self.gift_card_amount = gift_card_amount end def call validate_shipping_method - - response = gateway.setup_order(convert_amount(cart.total), paypal_params) + total_amount = cart.total - gift_card_amount + response = gateway.setup_order(convert_amount(total_amount), paypal_params) # If paypal setup went fine, redirect to the paypal page if response.success? PaypalSetup.new(setup_type: "redirect", redirect_url: gateway.redirect_url_for(response.token, mobile: use_mobile_payments)) @@ -59,7 +60,7 @@ def call private - attr_accessor :description, :cart, :gateway_class, :success_url, :cancel_url, :ip_address, :allow_shipping_change, :callback_url, :shipping_method_model, :use_mobile_payments + attr_accessor :description, :cart, :gateway_class, :success_url, :cancel_url, :ip_address, :allow_shipping_change, :callback_url, :shipping_method_model, :use_mobile_payments, :gift_card_amount def paypal_params Process::PaypalParams.new( @@ -71,12 +72,13 @@ def paypal_params callback_url: callback_url, shipping_method_model: shipping_method_model, use_mobile_payments: use_mobile_payments, - description: description + description: description, + gift_card_amount: gift_card_amount ).call end # @method shipping_methods - # + # # @returns shipping methods with promotions applied def shipping_methods @shipping_methods ||= ShippingMethodsForCart.new(cart: cart, shipping_methods: shipping_method_model.all).call.sort_by(&:total) diff --git a/lib/paypal_express/shipping_methods_for_cart.rb b/lib/paypal_express/shipping_methods_for_cart.rb index f5e4e76..5997348 100644 --- a/lib/paypal_express/shipping_methods_for_cart.rb +++ b/lib/paypal_express/shipping_methods_for_cart.rb @@ -26,7 +26,9 @@ def call free_shipping_method_ids.flatten! updated_shipping_methods = [] - shipping_methods.each do |shipping_method| + filtered_shipping_methods = shipping_methods.reject { |sm| sm.meta_attribute(:disabled) == 'true' } + + filtered_shipping_methods.each do |shipping_method| shipping_method_free = free_shipping_method_ids.include?(shipping_method.id) updated_shipping_methods << CartShippingMethod.new(shipping_method, shipping_method_free) end diff --git a/spec/factories/addresses.rb b/spec/factories/addresses.rb index dfd8114..8713d1b 100644 --- a/spec/factories/addresses.rb +++ b/spec/factories/addresses.rb @@ -11,8 +11,8 @@ state { Faker::Address.state } postcode { Faker::Address.postcode } country { Faker::Address.country } - preferred_billing false - preferred_shipping false + preferred_billing { false } + preferred_shipping { false } end factory :addresses_from_fixture, class: JsonStruct do @@ -27,4 +27,4 @@ send(key, value) end end -end \ No newline at end of file +end diff --git a/spec/factories/asset_files.rb b/spec/factories/asset_files.rb index 8cdff27..029d4b9 100644 --- a/spec/factories/asset_files.rb +++ b/spec/factories/asset_files.rb @@ -4,10 +4,10 @@ sequence(:name) { |n| "asset file name-#{n}" } asset_folder_id { Faker::Number.number(2) } sequence(:file_content_filename) { |n| "asset_file_name_#{n}.png" } - file_content_size 106 - file_content_content_type "image/png" + file_content_size { 106 } + file_content_content_type { "image/png" } image_width { Faker::Number.number(4) } image_height { Faker::Number.number(4) } reference { "#{Faker::Code.isbn}-#{Time.now.nsec}" } end -end \ No newline at end of file +end diff --git a/spec/factories/bundle.rb b/spec/factories/bundle.rb index bd8d7b1..5656680 100644 --- a/spec/factories/bundle.rb +++ b/spec/factories/bundle.rb @@ -8,6 +8,6 @@ end factory :bundle_list, parent: :json_api_resource_list do - type "bundle" + type { "bundle" } end end diff --git a/spec/factories/categories.rb b/spec/factories/categories.rb index 69555dd..8b74a07 100644 --- a/spec/factories/categories.rb +++ b/spec/factories/categories.rb @@ -3,7 +3,7 @@ factory :category, class: klass do title { Faker::Lorem.sentence } reference { rand(1000000000).to_s } - category_tree_id "reference:web" + category_tree_id { "reference:web" } end factory :categories_from_fixture, class: JsonStruct do obj = JsonStruct.new(JSON.parse(File.read("spec/fixtures/categories/multiple.json"))) @@ -17,4 +17,4 @@ send(key, value) end end -end \ No newline at end of file +end diff --git a/spec/factories/customer_account.rb b/spec/factories/customer_account.rb index 613568f..f2d5022 100644 --- a/spec/factories/customer_account.rb +++ b/spec/factories/customer_account.rb @@ -5,7 +5,7 @@ end factory :customer_account_from_fixture, class: JsonStruct do transient do - api_root "http://api.dummydomain/v1" + api_root { "http://api.dummydomain/v1" } end after(:build) do |instance, evaluator| obj = JsonStruct.new(JSON.parse(JsonErb.render_from_hash(File.read("spec/fixtures/customer_accounts/singular.json.erb"), api_root: evaluator.api_root))) diff --git a/spec/factories/json_api_error.rb b/spec/factories/json_api_error.rb index b2f41f9..919119e 100644 --- a/spec/factories/json_api_error.rb +++ b/spec/factories/json_api_error.rb @@ -9,9 +9,9 @@ # @param [Hash] meta A meta object containing non-standard meta-information about the error. FactoryBot.define do factory :json_api_error, class: JsonStruct do - status "500" - title "Something went wrong" - detail "Something went wrong because something else caused it to go wrong" + status { "500" } + title { "Something went wrong" } + detail { "Something went wrong because something else caused it to go wrong" } meta { { stack_trace: [ "/full/path/to/file_1.rb:587 method()", diff --git a/spec/factories/json_api_resource.rb b/spec/factories/json_api_resource.rb index 8a71ab7..7a54f46 100644 --- a/spec/factories/json_api_resource.rb +++ b/spec/factories/json_api_resource.rb @@ -25,13 +25,13 @@ klass = Struct.new(:id, :type, :attributes, :links, :relationships) factory :json_api_resource, class: klass do transient do - build_resource nil - base_path "/test_account/v1" - primary_key "id" + build_resource { nil } + base_path { "/test_account/v1" } + primary_key { "id" } end relationships { {} } sequence(:id) { |idx| idx.to_s } - type "Unknown" + type { "Unknown" } links { {} } after(:build) do |ri, evaluator| if evaluator.build_resource.present? diff --git a/spec/factories/json_api_resource_links.rb b/spec/factories/json_api_resource_links.rb index 48595f0..84096d5 100644 --- a/spec/factories/json_api_resource_links.rb +++ b/spec/factories/json_api_resource_links.rb @@ -6,7 +6,7 @@ FactoryBot.define do klass = Struct.new(:href, :meta) factory :json_api_resource_link, class: klass do - href "/test_account/v1/resource_type/pages/1" + href { "/test_account/v1/resource_type/pages/1" } meta { {} } end end diff --git a/spec/factories/json_api_resource_lists.rb b/spec/factories/json_api_resource_lists.rb index 50aaae4..008f423 100644 --- a/spec/factories/json_api_resource_lists.rb +++ b/spec/factories/json_api_resource_lists.rb @@ -19,14 +19,14 @@ data { [] } errors { [] } transient do - quantity 1 - page_size 25 - page 1 - base_path "/test_account/v1" + quantity { 1 } + page_size { 25 } + page { 1 } + base_path { "/test_account/v1" } page_count { (quantity / page_size.to_f).ceil.to_i } - type "" + type { "" } pluralized_type { type.to_s.pluralize } - primary_key "id" + primary_key { "id" } end after(:build) do |list, evaluator| quantity = evaluator.quantity % evaluator.page_size diff --git a/spec/factories/json_api_top_singular_resources.rb b/spec/factories/json_api_top_singular_resources.rb index b24cb5c..b8e4cff 100644 --- a/spec/factories/json_api_top_singular_resources.rb +++ b/spec/factories/json_api_top_singular_resources.rb @@ -9,7 +9,7 @@ klass = Struct.new(:data, :meta, :errors, :links, :included) factory :json_api_top_singular_resource, class: klass do meta { {} } - errors [] + errors { [] } links { {} } included { [] } end diff --git a/spec/factories/order.rb b/spec/factories/order.rb index ea251b3..c982e67 100644 --- a/spec/factories/order.rb +++ b/spec/factories/order.rb @@ -1,11 +1,11 @@ FactoryBot.define do factory :order, class: JsonStruct do - test false + test { false } end factory :order_from_fixture, class: JsonStruct do transient do - api_root "http://api.dummydomain/v1" + api_root { "http://api.dummydomain/v1" } end after(:build) do |instance, evaluator| diff --git a/spec/factories/password_recovery.rb b/spec/factories/password_recovery.rb index b49528c..21d9653 100644 --- a/spec/factories/password_recovery.rb +++ b/spec/factories/password_recovery.rb @@ -5,13 +5,13 @@ token_expired { [true, false].sample} factory :password_recovery_with_valid_token do - token_present true - token_expired false + token_present { true } + token_expired { false } end factory :password_recovery_with_used_token do - token_present false - token_expired true + token_present { false } + token_expired { true } end end end diff --git a/spec/factories/product_lists.rb b/spec/factories/product_lists.rb index fe57ee0..3384c57 100644 --- a/spec/factories/product_lists.rb +++ b/spec/factories/product_lists.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :product_list, parent: :json_api_resource_list do - type "product" - primary_key "slug" + type { "product" } + primary_key { "slug" } after(:build) do |instance| instance.data.each do |ri| ri.relationships.merge(variants: { data: [] }) diff --git a/spec/factories/promotions.rb b/spec/factories/promotions.rb index bccdc34..8b5b365 100644 --- a/spec/factories/promotions.rb +++ b/spec/factories/promotions.rb @@ -1,24 +1,24 @@ FactoryBot.define do klass = Struct.new(:name, :starts_at, :active, :priority, :coupon_type, :coupon_code, :promotion_type, :shipping_methods) factory :promotion, class: klass do - name "Promotion name" + name { "Promotion name" } starts_at { 10.days.ago.strftime('%Y-%m-%dT%T') } # ends_at "2015-09-10 12:25:46" - active true - priority 1 - coupon_type "none" - coupon_code "" - shipping_methods [] + active { true } + priority { 1 } + coupon_type { "none" } + coupon_code { "" } + shipping_methods { [] } trait :fixed_amount do # initialize_with { Promotion::AmountDiscountOnCartRule.new(attributes) } - promotion_type "AmountDiscountOnCartRule" - discount_amount 9.99 + promotion_type { "AmountDiscountOnCartRule" } + discount_amount { 9.99 } end trait :free_shipping do # initialize_with { FlexCommerce::Promotion::FactoryBot.build(attributes).object } - promotion_type "FreeShippingRule" + promotion_type { "FreeShippingRule" } after(:build) do |promotion, _| promotion.shipping_methods << _.shipping_methods end diff --git a/spec/factories/report_list.rb b/spec/factories/report_list.rb index ab5638b..5f3c2f0 100644 --- a/spec/factories/report_list.rb +++ b/spec/factories/report_list.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :report_list, parent: :json_api_resource_list do - type "report" + type { "report" } end end diff --git a/spec/factories/retail_store.rb b/spec/factories/retail_store.rb index 409a42b..418042c 100644 --- a/spec/factories/retail_store.rb +++ b/spec/factories/retail_store.rb @@ -6,6 +6,6 @@ slug { "#{Faker::Number.number(10)}-#{Time.now.nsec}" } latitude { BigDecimal("-1.#{rand(100..998)}") } longitude { BigDecimal("53.#{rand(100..998)}") } - address "Great George St,\nLeeds,\nLS1 3BB" + address { "Great George St,\nLeeds,\nLS1 3BB" } end end diff --git a/spec/factories/retail_store_list.rb b/spec/factories/retail_store_list.rb index 2d1983d..07876b5 100644 --- a/spec/factories/retail_store_list.rb +++ b/spec/factories/retail_store_list.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :retail_store_list, parent: :json_api_resource_list do - type "retail_store" + type { "retail_store" } end end diff --git a/spec/factories/shipping_method.rb b/spec/factories/shipping_method.rb index c71031c..db12066 100644 --- a/spec/factories/shipping_method.rb +++ b/spec/factories/shipping_method.rb @@ -4,6 +4,6 @@ sequence(:price) { |idx| idx * 1.50 } sequence(:reference) { |idx| "shipping_method_#{idx}" } sequence(:description) { |idx| "Shipping Method #{idx}" } - tax 1.00 + tax { 1.00 } end end \ No newline at end of file diff --git a/spec/factories/shipping_method_lists.rb b/spec/factories/shipping_method_lists.rb index 8c7b8fe..383f739 100644 --- a/spec/factories/shipping_method_lists.rb +++ b/spec/factories/shipping_method_lists.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :shipping_method_list, parent: :json_api_resource_list do - type "shipping_method" + type { "shipping_method" } end end diff --git a/spec/factories/static_page_lists.rb b/spec/factories/static_page_lists.rb index ee8d3bc..983f320 100644 --- a/spec/factories/static_page_lists.rb +++ b/spec/factories/static_page_lists.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :static_page_list, parent: :json_api_resource_list do - type "static_page" + type { "static_page" } end end diff --git a/spec/factories/stock_level_lists.rb b/spec/factories/stock_level_lists.rb index 87936c1..6d81772 100644 --- a/spec/factories/stock_level_lists.rb +++ b/spec/factories/stock_level_lists.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :stock_level_list, parent: :json_api_resource_list do - type "stock_level" + type { "stock_level" } end end diff --git a/spec/factories/tax_codes.rb b/spec/factories/tax_codes.rb index a013774..ea01e49 100644 --- a/spec/factories/tax_codes.rb +++ b/spec/factories/tax_codes.rb @@ -6,8 +6,8 @@ starts_at { Time.current } ends_at { 1.days.from_now } - country "GB" - rate 0.1 + country { "GB" } + rate { 0.1 } end factory :tax_codes_from_fixture, class: JsonStruct do diff --git a/spec/factories/taxonomies.rb b/spec/factories/taxonomies.rb index 7a89737..8b0b802 100644 --- a/spec/factories/taxonomies.rb +++ b/spec/factories/taxonomies.rb @@ -3,8 +3,8 @@ FactoryBot.define do factory :taxonomy, class: ::FlexCommerce::Taxonomy do name { Faker::Lorem.sentence } - apply_to_all false - data_type "RetailStore" + apply_to_all { false } + data_type { "RetailStore" } end factory :taxonomy_from_fixture, class: JsonStruct do diff --git a/spec/factories/v2/order.rb b/spec/factories/v2/order.rb index 80e96e3..3a71122 100644 --- a/spec/factories/v2/order.rb +++ b/spec/factories/v2/order.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :v2_order_from_fixture, class: JsonStruct do transient do - api_root "http://api.dummydomain/v2" + api_root { "http://api.dummydomain/v2" } end after(:build) do |instance, evaluator| diff --git a/spec/factories/variants.rb b/spec/factories/variants.rb index 04fa9b3..92970be 100644 --- a/spec/factories/variants.rb +++ b/spec/factories/variants.rb @@ -5,6 +5,6 @@ description { Faker::Lorem.sentence } sku { Faker::Code.ean } reference { Faker::Lorem.words(3).join("-") } - stock_level 100 + stock_level { 100 } end end diff --git a/spec/factories/webhook.rb b/spec/factories/webhook.rb index aefa67f..be0e385 100644 --- a/spec/factories/webhook.rb +++ b/spec/factories/webhook.rb @@ -3,7 +3,7 @@ factory :webhook, class: klass do title { Faker::Lorem.words(3).join(" ") } event { "order_created" } - request_url "https://example.com/bla" - request_headers ({ "X-Token" => "test" }) + request_url { "https://example.com/bla" } + request_headers {{ "X-Token" => "test" }} end end diff --git a/spec/factories/webhook_list.rb b/spec/factories/webhook_list.rb index c5855a2..5c8d4ce 100644 --- a/spec/factories/webhook_list.rb +++ b/spec/factories/webhook_list.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :webhook_list, parent: :json_api_resource_list do - type "webhook" + type { "webhook" } end end diff --git a/spec/integration/cart_spec.rb b/spec/integration/cart_spec.rb index c4310ce..5cce021 100644 --- a/spec/integration/cart_spec.rb +++ b/spec/integration/cart_spec.rb @@ -217,7 +217,7 @@ end end context "using the validate_stock! method" do - let!(:stub) { stub_request(:get, "#{api_root}/stock_levels.json_api").with(headers: {"Accept" => "application/vnd.api+json"}, query: {filter: {skus: "742207266-0-1,742207266-0-2"}}).to_return body: stock_level_list.to_json, status: response_status, headers: default_headers } + let!(:stub) { stub_request(:get, "#{api_root}/stock_levels.json_api").with(headers: {"Accept" => "application/vnd.api+json"}, query: {fields: {stock_levels: "stock_available"}, filter: {skus: "742207266-0-1,742207266-0-2"}}).to_return body: stock_level_list.to_json, status: response_status, headers: default_headers } context "with no stock" do let(:stock_level_list) { { data: [ diff --git a/spec/integration/customer_account_spec.rb b/spec/integration/customer_account_spec.rb index 31a8928..292b889 100644 --- a/spec/integration/customer_account_spec.rb +++ b/spec/integration/customer_account_spec.rb @@ -191,7 +191,7 @@ it "should return password_recovery resource with errors" do expect(subject).to be_kind_of(::FlexCommerce::PasswordRecovery) expect(subject.errors).to be_present - expect(subject.errors.first).to include(error_message) + expect(subject.errors.full_messages).to include(error_message) end end end @@ -241,7 +241,7 @@ it "should return password_recovery resource with errors" do expect(subject).to be_kind_of(::FlexCommerce::PasswordRecovery) expect(subject.errors).to be_present - expect(subject.errors.first).to include(error_message) + expect(subject.errors.full_messages).to include(error_message) end end end diff --git a/spec/integration/oms/customer_order.rb b/spec/integration/oms/customer_order.rb new file mode 100644 index 0000000..84cbd7e --- /dev/null +++ b/spec/integration/oms/customer_order.rb @@ -0,0 +1,14 @@ +require "spec_helper" +require "flex_commerce_api" +require "uri" + + +RSpec.describe "OMS Order History" do + include_context "global context" + + let(:subject_class) { ::FlexCommerce::CustomerOrder } + + context "Fetching Order history via OMS" do + subject_class.fetch_orders(customer_reference: "12345") + end +end \ No newline at end of file diff --git a/spec/lib/paypal_express/auth_spec.rb b/spec/lib/paypal_express/auth_spec.rb index 500361e..f25943b 100644 --- a/spec/lib/paypal_express/auth_spec.rb +++ b/spec/lib/paypal_express/auth_spec.rb @@ -4,11 +4,11 @@ include ActiveSupport::NumberHelper include_context "context store" include_context "housekeeping" - + let(:token) { "fake-token" } let(:payer_id) { "fake-payer-id" } let(:cart) { build_stubbed(:cart, total: 100) } - let(:transaction) { + let(:transaction) { to_clean.transaction = FlexCommerce::PaymentTransaction.create( cart_id: cart.id, gateway_response: { @@ -28,7 +28,7 @@ cart.billing_address.freeze cart.freeze end - + subject { described_class.new(cart: cart, token: token, payer_id: payer_id, payment_transaction: transaction) } shared_context "mocked active merchant" do |expect_login: true, test_mode: true| @@ -85,7 +85,7 @@ context "happy path in production" do include_context "mocked active merchant", test_mode: false - + before(:each) do expect(active_merchant_gateway).to receive(:order).with(convert_amount(cart.total), token: token, payer_id: payer_id, currency: "GBP").and_return order_response expect(active_merchant_gateway).to receive(:authorize_transaction).with(transaction_id, convert_amount(cart.total), transaction_entity: "Order", payer_id: payer_id, currency: "GBP").and_return authorize_order_response @@ -99,7 +99,7 @@ context "with error scenarios" do include_context "mocked active merchant" - + it "should mark the transactions gateway_response as invalid when failure is recoverable in order stage" do order_response = instance_double "ActiveMerchant::Billing::PaypalExpressGateway", "order_response", params: {"error_codes" => "10410", "message" => "Invalid token", "ack" => "Failure", "Ack" => "Failure"}, success?: false expect(active_merchant_gateway).to receive(:order).with(convert_amount(cart.total), token: token, payer_id: payer_id, currency: "GBP").and_return order_response diff --git a/spec/lib/paypal_express/generate_summary_spec.rb b/spec/lib/paypal_express/generate_summary_spec.rb index 6f90985..e00ce07 100644 --- a/spec/lib/paypal_express/generate_summary_spec.rb +++ b/spec/lib/paypal_express/generate_summary_spec.rb @@ -5,10 +5,11 @@ def convert_amount(amt) (amt * 100).to_i end context "without tax" do - subject { described_class.new(cart: cart) } shared_examples_for "a summary from a cart with tax ignored" do it "should have the correct subtotal" do - expect(subject.call).to include subtotal: convert_amount(line_items.sum(&:total)) + # gift card amount is deducted from subtotal if gift card used + subtotal = line_items.sum(&:total) - gift_card_amount + expect(subject.call).to include subtotal: convert_amount(subtotal) end it "should have zero tax" do expect(subject.call).to include tax: 0 @@ -20,6 +21,7 @@ def convert_amount(amt) expect(subject.call).to include shipping: convert_amount(shipping_total) end end + shared_examples_for "paypal items" do it "should have the correct items - amount and quantity" do expect(paypal_items).to match_array(line_items.map { |li| hash_including(amount: convert_amount(li.unit_price), quantity: li.unit_quantity) }) @@ -34,17 +36,34 @@ def convert_amount(amt) expect(paypal_items).to match_array(line_items.map { |li| hash_including(number: li.item.sku) }) end end + + shared_examples_for "non discounted line items" do + let(:gift_card_amount) { BigDecimal.new(0) } + subject { described_class.new(cart: cart, gift_card_amount: gift_card_amount) } + let(:paypal_items) { subject.call[:items] } + include_examples "paypal items" + end + shared_examples_for "discounted line items" do + let(:gift_card_amount) { BigDecimal.new(0) } + subject { described_class.new(cart: cart, gift_card_amount: gift_card_amount) } let(:paypal_items) { subject.call[:items][0...-1] } include_examples "paypal items" it "should have the last item as special discount item" do expect(subject.call).to include(items: array_including(hash_including(amount: convert_amount(BigDecimal(0) - cart.total_discount)))) end end - shared_examples_for "non discounted line items" do - let(:paypal_items) { subject.call[:items] } + + shared_examples_for "line items partially paid with gift cards" do + let(:gift_card_amount) { BigDecimal.new(10) } + subject { described_class.new(cart: cart, gift_card_amount: gift_card_amount) } + let(:paypal_items) { subject.call[:items][0...-1] } include_examples "paypal items" + it "should have the last item as gift card item" do + expect(subject.call).to include(items: array_including(hash_including(amount: convert_amount(BigDecimal.new(0) - gift_card_amount)))) + end end + context "with no shipping" do let(:shipping_total) { BigDecimal(0) } context "with no discounts" do @@ -56,6 +75,17 @@ def convert_amount(amt) include_examples "a summary from a cart with tax ignored" include_examples "non discounted line items" end + + context "with gift card" do + let(:cart) { double(:cart, free_shipping: false, line_items: line_items, shipping_total: shipping_total, total: line_items.sum(&:total) + shipping_total, total_discount: line_item_discount * line_items.length) } + let(:line_item_discount) { BigDecimal.new(0) } + let(:item) { 5.times.map { |n| double(:variant, sku: "sku_#{n}") } } + let(:line_items) { 5.times.map { |n| double(:line_item, unit_price: BigDecimal.new(1.67, 12), item: item[n], unit_quantity: 3, total: BigDecimal.new(5.01, 12), title: "Line item #{n}", tax: BigDecimal.new(0)) } } + + include_examples "a summary from a cart with tax ignored" + include_examples "line items partially paid with gift cards" + end + context "with line item discounts" do let(:cart) { double(:cart, free_shipping: false, line_items: line_items, shipping_total: shipping_total, total: line_items.sum(&:total) + shipping_total, total_discount: line_item_discount * line_items.length) } let(:line_item_discount) { BigDecimal(0.79, 12) } @@ -65,6 +95,7 @@ def convert_amount(amt) include_examples "discounted line items" end end + context "with shipping" do let(:shipping_total) { BigDecimal(3.99, 12) } context "with no discounts" do @@ -75,6 +106,16 @@ def convert_amount(amt) include_examples "a summary from a cart with tax ignored" include_examples "non discounted line items" end + + context "with gift card" do + let(:cart) { double(:cart, free_shipping: false, line_items: line_items, shipping_total: shipping_total, total: line_items.sum(&:total) + shipping_total, total_discount: line_item_discount * line_items.length) } + let(:line_item_discount) { BigDecimal.new(0) } + let(:item) { 5.times.map { |n| double(:variant, sku: "sku_#{n}") } } + let(:line_items) { 5.times.map { |n| double(:line_ttem, unit_price: BigDecimal.new(1.67, 12), item: item[n], unit_quantity: 3, total: BigDecimal.new(5.01, 12), title: "Line item #{n}", tax: BigDecimal.new(0)) } } + include_examples "a summary from a cart with tax ignored" + include_examples "line items partially paid with gift cards" + end + context "with line item discounts" do let(:cart) { double(:cart, free_shipping: false, line_items: line_items, shipping_total: shipping_total, total: line_items.sum(&:total) + shipping_total, total_discount: line_item_discount * line_items.length) } let(:line_item_discount) { BigDecimal(0.79, 12) }