text:
require 'spec_helper'
require 'spree/testing_support/bar_ability'

module Spree
  describe Api::Returnly::RefundsController, type: :controller do
    render_views

    let!(:default_refund_reason) do
      Spree::RefundReason.find_or_create_by!(name: Spree::RefundReason::RETURN_PROCESSING_REASON, mutable: false)
    end

    let(:order) { create(:shipped_order, line_items_count: line_items_count, line_items_attributes: line_items_attributes, shipment_cost: shipment_cost) }
    let!(:line_items_count) { 2 }
    let!(:line_items_attributes) { [{ quantity: 1, price: 24.99 }] * 2 }
    let!(:shipment_cost) { 5 }
    let(:line_item1) { order.line_items.first }
    let(:line_item2) { order.line_items.last }

    let(:attributes) {
      [:number, :item_total, :display_total, :total,
       :state, :adjustment_total,
       :user_id, :created_at, :updated_at,
       :completed_at, :payment_total, :shipment_state,
       :payment_state, :email, :special_instructions,
       :total_quantity, :display_item_total, :currency]
    }

    let(:address_params) { { country_id: Country.first.id, state_id: State.first.id } }

    let(:current_api_user) do
      user = Spree.user_class.new(email: "spree@example.com",
                                  password: 'test123',
                                  password_confirmation: 'test123')
      user.generate_spree_api_key!
      user
    end

    before do
      stub_authentication!
    end

    describe "POST create" do
      let(:line_items_request) {
        {
          order_id:               order.number,
          items:                  [
                                    {
                                      order_line_item_id: line_item1.id,
                                      units:              1,
                                      restock:            true
                                    }
                                  ],
          notify_shopper:         false,
          product_refund_amount:  order.total,
          shipping_refund_amount: 0,
          refund_note:            'comment text'
        }
      }
      subject { api_post :create, line_items_request }

      stub_authorization! do |_|
        can :create, Spree::Order
      end

      it { expect { subject }.to change { Spree::CustomerReturn.count }.by(1) }
      it { expect { subject }.to change { Spree::Reimbursement.count }.by(1) }

      it 'should create a refund for the reimbursement' do
        subject
        order.reload
        expect(order.reimbursements.size).to be(1)
        expect(order.reimbursements.first.refunds.size).to be(1)
      end

      it 'should make a reimbursement for the total in transactions ' do
        subject
        expect(Spree::Reimbursement.last.paid_amount.to_f).to be_within(0.1).of(line_item1.total.to_f)
      end

      it "returns transaction data" do
        subject
        json = JSON.parse(response.body)
        expect(json.keys).to include 'refund_id', 'line_items', 'transactions'
      end

      it "should include version headers" do
        subject
        expect(response.headers['X-Returnly-Extension-Version']).to_not be_nil
        expect(response.headers['X-Returnly-Extension-Platform-Qualifier']).to_not be_nil
      end

      context 'When refund amount is 0' do
        before do
          line_items_request[:product_refund_amount] = 0
        end

        it 'marks items as returned' do
          expect { subject }.to_not change { Spree::Reimbursement.count }
          json = JSON.parse(response.body)
          expect(json.delete('created_at')).to match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/)
          expect(json).to eq(
            'refund_id' => '1',
            'line_items' => [
              {
                'refund_line_item_id' => '1',
                'order_line_item_id' => '1',
                'units' => 1,
                'restock' => true
              }
            ],
            'transactions' => []
          )
        end
      end

      context 'When refunding shipping' do
        let(:line_items_request) {
          {
            order_id:               order.number,
            items:                  [
                                      {
                                        order_line_item_id: line_item1.id,
                                        units:              1,
                                        restock:            true
                                      }
                                    ],
            notify_shopper:         false,
            product_refund_amount:  order.item_total,
            shipping_refund_amount: order.shipment_total,
            refund_note:            'comment text'
          }
        }

        before { api_post :create, line_items_request }

        it 'creates a refund payment for shipping' do
          expect(order.reload.refunds.reload.count).to eq 2
          expect(order.refunds.first.amount).to eq line_item1.total
          expect(order.refunds.last.amount).to eq order.shipment_total
        end
      end

      context 'When creating a refund for the item but with a lower product refund amount' do
        let(:line_items_request) do
          {
            order_id:               order.number,
            items:                  [
                                      {
                                        order_line_item_id: line_item1.id,
                                        units:              1,
                                        restock:            true
                                      }
                                    ],
            notify_shopper:         false,
            product_refund_amount:  10,
            shipping_refund_amount: 0,
            refund_note:            'comment text'
          }
        end

        before { api_post :create, line_items_request }

        it 'can create another refund to that order without specifying a line item' do
          request = {
            order_id:               order.number,
            items:                  [],
            notify_shopper:         false,
            product_refund_amount:  14.99,
            shipping_refund_amount: 0,
            refund_note:            'comment text'
          }

          expect { api_post :create, request }.to change { Spree::Reimbursement.count }.by(1)
          expect(order.refunds.reload.count).to eq 2
          expect(order.refunds.last.amount).to eq 14.99
          expect(order.refunds.sum(&:amount)).to be_within(0.1).of(24.99)

          json_response = JSON.parse(response.body)
          last_refund = Spree::Refund.last
          expect(json_response['transactions']).to eq [
            {
              'id' => last_refund.transaction_id,
              'amount' => '14.99',
              'status' => 'SUCCESS',
              'type' => 'REFUND',
              'gateway' => 'BogusCreditCard',
              'is_online' => true,
              'is_test' => true,
              'payment_details' => {},
              'created_at' => last_refund.created_at.iso8601(3),
              'updated_at' => last_refund.updated_at.iso8601(3)
            }
          ]
        end

        context 'When trying to refund more amount' do
          it 'creates a refund but without the extra amount' do
            request = {
              order_id:               order.number,
              items:                  [],
              notify_shopper:         false,
              product_refund_amount:  order.total,
              shipping_refund_amount: 5,
              refund_note:            'comment text'
            }

            expect { api_post :create, request }.to change { Spree::Reimbursement.count }.by(1)
            expect(order.refunds.reload.count).to eq 2
            expect(order.refunds.last.amount).to be_within(0.1).of(order.total - 10) # First refund amount is 10
            expect(order.refunds.sum(&:amount)).to eq order.total

            json_response = JSON.parse(response.body)
            last_refund = Spree::Refund.last
            expect(json_response['transactions']).to eq [
              {
                'id' => last_refund.transaction_id,
                'amount' => '44.99',
                'status' => 'SUCCESS',
                'type' => 'REFUND',
                'gateway' => 'BogusCreditCard',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {

                },
                'created_at' => last_refund.created_at.iso8601(3),
                'updated_at' => last_refund.updated_at.iso8601(3)
              }
            ]
          end
        end
      end

      context 'Given order with different line items' do
        let!(:zone) { create(:zone) }
        let!(:country) { create(:country) }
        let!(:zone_member) { Spree::ZoneMember.create(zoneable: country, zone: zone) }
        let!(:zone_members) { zone.zone_members << zone_member }
        let!(:tax_rate) { create(:tax_rate, amount: 0.05, zone: zone) }
        let!(:tax_category) { tax_rate.tax_categories.first }
        let!(:line_items_attributes) { [{ quantity: 2, price: 21.49, tax_category_id: tax_category.id }, { quantity: 4, price: 19.99, tax_category_id: tax_category.id }] }

        context 'When refunding 1 unit of each line item' do
          before do
            request = {
              order_id:               order.number,
              items:                  [
                                        {
                                          order_line_item_id: line_item1.id,
                                          units:              1,
                                          restock:            true
                                        },
                                        {
                                          order_line_item_id: line_item2.id,
                                          units:              1,
                                          restock:            true
                                        }
                                      ],
              notify_shopper:         false,
              product_refund_amount:  43.55, # Sum of line item's amount + line item's tax (by each unit)
              shipping_refund_amount: 0,
              refund_note:            'comment text'
            }

            api_post :create, request
          end

          it 'creates a correct refund' do
            return_items = Spree::ReturnItem.last(2)
            expect(return_items.first.total).to eq 22.56
            expect(return_items.last.total).to eq 20.99
          end

          context 'When refunding different units for each line item' do
            before do
              request = {
                order_id:               order.number,
                items:                  [
                                          {
                                            order_line_item_id: line_item1.id,
                                            units:              1,
                                            restock:            true
                                          },
                                          {
                                            order_line_item_id: line_item2.id,
                                            units:              2,
                                            restock:            true
                                          }
                                        ],
                notify_shopper:         false,
                product_refund_amount:  64.54, # Sum of line item's amount + line item's tax (by each unit)
                shipping_refund_amount: 0,
                refund_note:            'comment text'
              }

              api_post :create, request
            end

            it 'creates a correct refund' do
              return_items = Spree::ReturnItem.last(3)
              expect(return_items.first.total).to eq 22.56
              expect(return_items.second.total).to eq 20.99
              expect(return_items.last.total).to eq 20.99
            end

            context 'When refunding the last unit' do
              before do
                request = {
                  order_id:               order.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  20.99, # Sum of available line item's amount + line item's tax (by each unit)
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_item = Spree::ReturnItem.last
                expect(return_item.total).to eq 20.99
              end
            end
          end
        end

        context 'When partially refunding 1 unit of each line item' do
          before do
            request = {
              order_id:               order.number,
              items:                  [
                                        {
                                          order_line_item_id: line_item1.id,
                                          units:              1,
                                          restock:            true
                                        },
                                        {
                                          order_line_item_id: line_item2.id,
                                          units:              1,
                                          restock:            true
                                        }
                                      ],
              notify_shopper:         false,
              product_refund_amount:  25,
              shipping_refund_amount: 0,
              refund_note:            'comment text'
            }

            api_post :create, request
          end

          it 'creates a correct refund' do
            return_items = Spree::ReturnItem.last(2)
            expect(return_items.first.total).to be_within(0.1).of(12.95)
            expect(return_items.last.total).to be_within(0.1).of(12.05)
          end

          context 'When partially refunding different units for each line item' do
            before do
              request = {
                order_id:               order.number,
                items:                  [
                                          {
                                            order_line_item_id: line_item1.id,
                                            units:              1,
                                            restock:            true
                                          },
                                          {
                                            order_line_item_id: line_item2.id,
                                            units:              2,
                                            restock:            true
                                          }
                                        ],
                notify_shopper:         false,
                product_refund_amount:  57,
                shipping_refund_amount: 0,
                refund_note:            'comment text'
              }

              api_post :create, request
            end

            it 'creates a correct refund' do
              return_items = Spree::ReturnItem.last(3)
              expect(return_items.first.total).to be_within(0.1).of(19.92)
              expect(return_items.second.total).to be_within(0.1).of(18.53)
              expect(return_items.last.total).to be_within(0.1).of(18.53)
            end

            context 'When refunding the last unit' do
              before do
                request = {
                  order_id:               order.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  10,
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_item = Spree::ReturnItem.last
                expect(return_item.total).to be_within(0.1).of(10)
              end
            end
          end
        end
      end

      context 'Given an order with 1 promotion discount' do
        let(:order_with_promotion) { create(:completed_order_with_promotion, line_items_count: line_items_count, line_items_attributes: line_items_attributes, promotion: promotion, shipment_cost: shipment_cost) }
        let!(:zone) { create(:zone) }
        let!(:country) { create(:country) }
        let!(:zone_member) { Spree::ZoneMember.create(zoneable: country, zone: zone) }
        let!(:zone_members) { zone.zone_members << zone_member }
        let!(:tax_rate) { create(:tax_rate, amount: 0.05, zone: zone) }
        let!(:tax_category) { tax_rate.tax_categories.first }

        before do
          promo_total = order_with_promotion.adjustments.where(source_type: 'Spree::PromotionAction').sum(&:amount)
          order_with_promotion.update_column(:promo_total, promo_total)

          create(:credit_card_payment, amount: order.total, order: order_with_promotion, state: 'completed')

          order_with_promotion.update_column(:payment_state, 'paid')
          order_with_promotion.shipments.each do |shipment|
            shipment.inventory_units.update_all state: 'shipped'
            shipment.update_columns(state: 'shipped', shipped_at: Time.current)
          end
          order_with_promotion.update_columns(shipment_state: 'shipped')
          order_with_promotion.reload
        end

        context 'When promotion is an order adjustment' do
          let!(:promotion) { create(:promotion_with_order_adjustment, :with_item_total_rule) }

          context 'And order has 2 line items with different quantities and prices' do
            let!(:line_items_attributes) { [{ quantity: 2, price: 21.49, tax_category_id: tax_category.id }, { quantity: 4, price: 19.99, tax_category_id: tax_category.id }] }
            let(:line_item1) { order_with_promotion.line_items.first }
            let(:line_item2) { order_with_promotion.line_items.last }

            context 'When refunding 1 unit of each line item' do
              before do
                request = {
                  order_id:               order_with_promotion.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item1.id,
                                              units:              1,
                                              restock:            true
                                            },
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  39.76, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_items = Spree::ReturnItem.last(2)
                expect(return_items.first.total).to be_within(0.1).of(19.97)
                expect(return_items.last.total).to be_within(0.1).of(19.79)
              end

              context 'When refunding different units for each line item' do
                before do
                  request = {
                    order_id:               order_with_promotion.number,
                    items:                  [
                                              {
                                                order_line_item_id: line_item1.id,
                                                units:              1,
                                                restock:            true
                                              },
                                              {
                                                order_line_item_id: line_item2.id,
                                                units:              2,
                                                restock:            true
                                              }
                                            ],
                    notify_shopper:         false,
                    product_refund_amount:  59.52, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                    shipping_refund_amount: 0,
                    refund_note:            'comment text'
                  }

                  api_post :create, request
                end

                it 'creates a correct refund' do
                  return_items = Spree::ReturnItem.last(3)
                  expect(return_items.first.total).to be_within(0.1).of(19.97)
                  expect(return_items.second.total).to be_within(0.1).of(19.79)
                  expect(return_items.last.total).to be_within(0.1).of(19.79)
                end

                context 'When refunding the last unit' do
                  before do
                    request = {
                      order_id:               order_with_promotion.number,
                      items:                  [
                                                {
                                                  order_line_item_id: line_item2.id,
                                                  units:              1,
                                                  restock:            true
                                                }
                                              ],
                      notify_shopper:         false,
                      product_refund_amount:  19.79, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                      shipping_refund_amount: 0,
                      refund_note:            'comment text'
                    }

                    api_post :create, request
                  end

                  it 'creates a correct refund' do
                    return_item = Spree::ReturnItem.last
                    expect(return_item.total).to be_within(0.1).of(19.79)
                  end
                end
              end
            end

            context 'When partially refunding 1 unit of each line item' do
              before do
                request = {
                  order_id:               order_with_promotion.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item1.id,
                                              units:              1,
                                              restock:            true
                                            },
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  25,
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_items = Spree::ReturnItem.last(2)
                expect(return_items.first.total).to be_within(0.1).of(12.55)
                expect(return_items.last.total).to be_within(0.1).of(12.44)
              end

              context 'When partially refunding different units for each line item' do
                before do
                  request = {
                    order_id:               order_with_promotion.number,
                    items:                  [
                                              {
                                                order_line_item_id: line_item1.id,
                                                units:              1,
                                                restock:            true
                                              },
                                              {
                                                order_line_item_id: line_item2.id,
                                                units:              2,
                                                restock:            true
                                              }
                                            ],
                    notify_shopper:         false,
                    product_refund_amount:  57,
                    shipping_refund_amount: 0,
                    refund_note:            'comment text'
                  }

                  api_post :create, request
                end

                it 'creates a correct refund' do
                  return_items = Spree::ReturnItem.last(3)
                  expect(return_items.first.total).to be_within(0.1).of(19.11)
                  expect(return_items.second.total).to be_within(0.1).of(18.94)
                  expect(return_items.last.total).to be_within(0.1).of(18.94)
                end

                context 'When refunding the last unit' do
                  before do
                    request = {
                      order_id:               order_with_promotion.number,
                      items:                  [
                                                {
                                                  order_line_item_id: line_item2.id,
                                                  units:              1,
                                                  restock:            true
                                                }
                                              ],
                      notify_shopper:         false,
                      product_refund_amount:  10,
                      shipping_refund_amount: 0,
                      refund_note:            'comment text'
                    }

                    api_post :create, request
                  end

                  it 'creates a correct refund' do
                    return_item = Spree::ReturnItem.last
                    expect(return_item.total).to be_within(0.1).of(10)
                  end
                end
              end
            end
          end
        end

        context 'When promotion is a line item adjustment' do
          let!(:promotion) { create(:promotion_with_item_adjustment, :with_item_total_rule) }

          context 'And order has 2 line items with different quantities and prices' do
            let!(:line_items_attributes) { [{ quantity: 2, price: 21.49, tax_category_id: tax_category.id }, { quantity: 4, price: 19.99, tax_category_id: tax_category.id }] }
            let(:line_item1) { order_with_promotion.line_items.first }
            let(:line_item2) { order_with_promotion.line_items.last }

            context 'When refunding 1 unit of each line item' do
              before do
                request = {
                  order_id:               order_with_promotion.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item1.id,
                                              units:              1,
                                              restock:            true
                                            },
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  35.68, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_items = Spree::ReturnItem.last(2)
                expect(return_items.first.total).to be_within(0.1).of(17.315)
                expect(return_items.last.total).to be_within(0.1).of(18.365)
              end

              context 'When refunding different units for each line item' do
                before do
                  request = {
                    order_id:               order_with_promotion.number,
                    items:                  [
                                              {
                                                order_line_item_id: line_item1.id,
                                                units:              1,
                                                restock:            true
                                              },
                                              {
                                                order_line_item_id: line_item2.id,
                                                units:              2,
                                                restock:            true
                                              }
                                            ],
                    notify_shopper:         false,
                    product_refund_amount:  54.03, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                    shipping_refund_amount: 0,
                    refund_note:            'comment text'
                  }

                  api_post :create, request
                end

                it 'creates a correct refund' do
                  return_items = Spree::ReturnItem.last(3)
                  expect(return_items.first.total).to be_within(0.1).of(17.315)
                  expect(return_items.second.total).to be_within(0.1).of(18.365)
                  expect(return_items.last.total).to be_within(0.1).of(18.365)
                end

                context 'When refunding the last unit' do
                  before do
                    request = {
                      order_id:               order_with_promotion.number,
                      items:                  [
                                                {
                                                  order_line_item_id: line_item2.id,
                                                  units:              1,
                                                  restock:            true
                                                }
                                              ],
                      notify_shopper:         false,
                      product_refund_amount:  18.365, # Sum of line item's amount + line item's tax (by each unit) - line item's discount (by each unit)
                      shipping_refund_amount: 0,
                      refund_note:            'comment text'
                    }

                    api_post :create, request
                  end

                  it 'creates a correct refund' do
                    return_item = Spree::ReturnItem.last
                    expect(return_item.total).to be_within(0.1).of(18.365)
                  end
                end
              end
            end

            context 'When partially refunding 1 unit of each line item' do
              before do
                request = {
                  order_id:               order_with_promotion.number,
                  items:                  [
                                            {
                                              order_line_item_id: line_item1.id,
                                              units:              1,
                                              restock:            true
                                            },
                                            {
                                              order_line_item_id: line_item2.id,
                                              units:              1,
                                              restock:            true
                                            }
                                          ],
                  notify_shopper:         false,
                  product_refund_amount:  25,
                  shipping_refund_amount: 0,
                  refund_note:            'comment text'
                }

                api_post :create, request
              end

              it 'creates a correct refund' do
                return_items = Spree::ReturnItem.last(2)
                expect(return_items.first.total).to be_within(0.1).of(12.13)
                expect(return_items.last.total).to be_within(0.1).of(12.86)
              end

              context 'When partially refunding different units for each line item' do
                before do
                  request = {
                    order_id:               order_with_promotion.number,
                    items:                  [
                                              {
                                                order_line_item_id: line_item1.id,
                                                units:              1,
                                                restock:            true
                                              },
                                              {
                                                order_line_item_id: line_item2.id,
                                                units:              2,
                                                restock:            true
                                              }
                                            ],
                    notify_shopper:         false,
                    product_refund_amount:  57,
                    shipping_refund_amount: 0,
                    refund_note:            'comment text'
                  }

                  api_post :create, request
                end

                it 'creates a correct refund' do
                  return_items = Spree::ReturnItem.last(3)
                  expect(return_items.first.total).to be_within(0.1).of(17.315)
                  expect(return_items.second.total).to be_within(0.1).of(18.365)
                  expect(return_items.last.total).to be_within(0.1).of(18.365)
                end

                context 'When refunding the last unit' do
                  before do
                    request = {
                      order_id:               order_with_promotion.number,
                      items:                  [
                                                {
                                                  order_line_item_id: line_item2.id,
                                                  units:              1,
                                                  restock:            true
                                                }
                                              ],
                      notify_shopper:         false,
                      product_refund_amount:  10,
                      shipping_refund_amount: 0,
                      refund_note:            'comment text'
                    }

                    api_post :create, request
                  end

                  it 'creates a correct refund' do
                    return_item = Spree::ReturnItem.last
                    expect(return_item.total).to be_within(0.1).of(10)
                  end
                end
              end
            end
          end
        end
      end
    end

    describe "POST estimate" do
      let(:line_items_request) {
        {
            items: [
                {
                    order_line_item_id: line_item1.id.to_s,
                    units: 1
                },
                {
                  order_line_item_id: line_item2.id.to_s,
                  units: 1
                }
            ],
            order_id: order.number
        }
      }

      subject { api_post :estimate, line_items_request }

      stub_authorization! do |_|
        can :create, Spree::Order
      end

      let(:shipment_tax) { order.all_adjustments.where(source_type: 'Spree::TaxRate', adjustable_type: 'Spree::Shipment').sum(&:amount) }
      let(:transactions) do
        order.payments.completed.each_with_object([]) do |_payment, payments|
          payments << {
            'id' => '1',
            'amount' => (line_item1.price + line_item2.price + order.adjustment_total + order.ship_total).to_s,
            'status' => 'PENDING',
            'type' => 'REFUND',
            'gateway' => 'BogusCreditCard',
            'is_online' => true,
            'is_test' => true,
            'payment_details' => {

            }
          }
        end
      end
      let(:response_estimate_refund) do
        {
          'product_amount' => '49.98',
          'tax_amount' => '0.00',
          'discount_amount' => '0.00',
          'total_refund_amount' => '49.98',
          'refundable_shipping_amount' => ::Money.from_amount(order.shipment_total).to_s,
          'refundable_shipping_tax_amount' => '0.00',
          'max_refundable_amount' => order.total.to_s
        }
      end

      it "should return an estimate of the credit to be refunded", focus: true do
        subject
        json = JSON.parse(response.body)

        expect(response).to be_success
        json_transactions = json.delete('transactions')
        expect(json).to eq response_estimate_refund
        json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
          expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
        end
        expect(json_transactions).to eq transactions
      end

      context 'Given an order with 1 line item and quantity 2' do
        let(:order_one_line_item_two_units) { create(:shipped_order, line_items_count: 1, line_items_attributes: [{ quantity: 2, price: 24.99 }]) }
        let(:line_item) { order_one_line_item_two_units.line_items.first }

        context 'When a refund has been created for 1 unit' do
          let(:line_items_request) {
            {
              order_id:               order_one_line_item_two_units.number,
              items:                  [
                                        {
                                          order_line_item_id: line_item.id,
                                          units:              1,
                                          restock:            true
                                        }
                                      ],
              notify_shopper:         false,
              product_refund_amount:  10,
              shipping_refund_amount: 0,
              refund_note:            'Returnly'
            }
          }
          let(:shipment_tax) { order_one_line_item_two_units.all_adjustments.where(source_type: 'Spree::TaxRate', adjustable_type: 'Spree::Shipment').sum(&:amount) }
          let(:transactions) do
            order_one_line_item_two_units.payments.completed.each_with_object([]) do |payment, payments|
              payments << {
                'id' => payment.id.to_s,
                'amount' => (line_item.price + line_item.adjustment_total + order_one_line_item_two_units.ship_total).to_s,
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'BogusCreditCard',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {

                }
              }
            end
          end
          let(:estimate_response) do
            {
              'product_amount' => line_item.price.to_s,
              'tax_amount' => '0.00',
              'discount_amount' => '0.00',
              'total_refund_amount' => (line_item.price + line_item.adjustment_total - order_one_line_item_two_units.promo_total).to_s,
              'refundable_shipping_amount' => ::Money.from_amount(order_one_line_item_two_units.shipment_total).to_s,
              'refundable_shipping_tax_amount' => '0.00',
              'max_refundable_amount' => (order_one_line_item_two_units.total - order_one_line_item_two_units.refunds.sum(&:amount)).to_s
            }
          end

          before { api_post :create, line_items_request }

          it 'Estimating refund should take in consideration the refund when generating estimation' do
            request = {
              items: [
                { order_line_item_id: line_item.id, units: 1 }
              ],
              order_id: order_one_line_item_two_units.number
            }

            api_post :estimate, request
            json_response = JSON.parse(response.body)
            json_transactions = json_response.delete('transactions')
            expect(json_response).to eq estimate_response
            json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
              expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
            end
            expect(json_transactions).to eq transactions
          end
        end
      end

      context 'Given an order with a promotion discount' do
        let(:order_with_promotion) { create(:completed_order_with_promotion, line_items_count: 2, line_items_attributes: line_items_attributes, promotion: promotion, shipment_cost: 5) }
        let(:shipment_tax) { order_with_promotion.all_adjustments.where(source_type: 'Spree::TaxRate', adjustable_type: 'Spree::Shipment').sum(&:amount) }

        before do
          promo_total = order_with_promotion.adjustments.where(source_type: 'Spree::PromotionAction').sum(&:amount)
          order_with_promotion.update_column(:promo_total, promo_total)
          create(:credit_card_payment, amount: order.total, order: order_with_promotion, state: 'completed')
        end

        context 'When promotion is an order adjustment' do
          let!(:promotion) { create(:promotion_with_order_adjustment, :with_item_total_rule) }

          context 'And order has 2 line items with the same price' do
            let!(:line_items_attributes) { [{ quantity: 1, price: 24.99 }] * 2 }

            context 'When estimating refund for 1 line item' do
              let(:line_item) { order_with_promotion.line_items.first }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item.price + line_item.adjustment_total - 5.0 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(line_item.price).to_s,
                  'tax_amount' => ::Money.from_amount(line_item.adjustment_total).to_s,
                  'discount_amount' => '5.00',
                  'total_refund_amount' => ::Money.from_amount(line_item.price + line_item.adjustment_total + -5.0).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => (order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimating refund has the discount of the order' do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: 1 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end
          end

          context 'And order has 2 line items with different price' do
            let!(:line_items_attributes) { [{ quantity: 1, price: 25 }, { quantity: 1, price: 75 }] }

            context 'When estimating refund for lower price line item' do
              let(:line_item) { order_with_promotion.line_items.first }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }

              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item.price + line_item_tax - 2.5 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(line_item.price).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => '2.50',
                  'total_refund_amount' => ::Money.from_amount(line_item.price + line_item_tax + -2.5).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimate refund has the discount of the order properly calculated for the line item' do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: 1 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end

            context 'When estimating refund for higher price line item' do
              let(:line_item) { order_with_promotion.line_items.last }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }

              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item.price + line_item_tax - 7.5 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(line_item.price).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => '7.50',
                  'total_refund_amount' => ::Money.from_amount(line_item.price + line_item_tax + -7.5).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimate refund has the discount of the order properly calculated for the line item' do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: 1 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end

            context 'When estimating refund for both items' do
              let(:line_item1) { order_with_promotion.line_items.first }
              let(:line_item2) { order_with_promotion.line_items.last }
              let(:line_item1_tax) { line_item1.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:line_item2_tax) { line_item2.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item1.price + line_item1_tax + line_item2.price + line_item2_tax - 10 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end

              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(line_item1.price + line_item2.price).to_s,
                  'tax_amount' => ::Money.from_amount(line_item1_tax + line_item2_tax).to_s,
                  'discount_amount' => '10.00',
                  'total_refund_amount' => ::Money.from_amount(line_item1.price + line_item1_tax + line_item2.price + line_item2_tax + -10).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimate refund has the discount of the order properly calculated' do
                request = {
                  items:    [
                              { order_line_item_id: line_item1.id, units: 1 }, { order_line_item_id: line_item2.id, units: 1 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end
          end

          context 'And order has 2 line items with different quantities and prices' do
            let!(:line_items_attributes) { [{ quantity: 3, price: 25 }, { quantity: 5, price: 75 }] }

            context 'When estimating refund for lower price line item' do
              let(:line_item) { order_with_promotion.line_items.first }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:product_amount) { line_item.price * units }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (product_amount + line_item_tax + discount_amount + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(product_amount).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => ::Money.from_amount(discount_amount * -1).to_s,
                  'total_refund_amount' => ::Money.from_amount(product_amount + line_item_tax + discount_amount).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              before do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: units }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
              end

              context 'For 1 unit' do
                let!(:units) { 1 }
                let!(:discount_amount) { -0.82 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 2 units' do
                let!(:units) { 2 }
                let!(:discount_amount) { -1.65 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 3 units' do
                let!(:units) { 3 }
                let!(:discount_amount) { -2.5 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end
            end

            context 'When estimating refund for higher price line item' do
              let(:line_item) { order_with_promotion.line_items.last }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:product_amount) { line_item.price * units }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (product_amount + line_item_tax + discount_amount + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(product_amount).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => ::Money.from_amount(discount_amount * -1).to_s,
                  'total_refund_amount' => ::Money.from_amount(product_amount + line_item_tax + discount_amount).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              before do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: units }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
              end

              context 'For 1 unit' do
                let!(:units) { 1 }
                let!(:discount_amount) { -1.5 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 2 units' do
                let!(:units) { 2 }
                let!(:discount_amount) { -3 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 3 units' do
                let!(:units) { 3 }
                let!(:discount_amount) { -4.5 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 4 units' do
                let!(:units) { 4 }
                let!(:discount_amount) { -6 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 5 units' do
                let!(:units) { 5 }
                let!(:discount_amount) { -7.5 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end
            end

            context 'When estimating refund for both line items' do
              let(:line_item1) { order_with_promotion.line_items.first }
              let(:line_item2) { order_with_promotion.line_items.last }

              let(:line_item1_tax) { line_item1.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:line_item2_tax) { line_item2.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }

              let(:product1_amount) { line_item1.price * units1 }
              let(:product2_amount) { line_item2.price * units2 }

              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (product1_amount + line_item1_tax + discount_amount1 + product2_amount + line_item2_tax + discount_amount2 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(product1_amount + product2_amount).to_s,
                  'tax_amount' => ::Money.from_amount(line_item1_tax + line_item2_tax).to_s,
                  'discount_amount' => ::Money.from_amount((discount_amount1 + discount_amount2) * -1).to_s,
                  'total_refund_amount' => ::Money.from_amount(product1_amount + product2_amount + line_item1_tax + line_item2_tax + discount_amount1 + discount_amount2).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              before do
                request = {
                  items:    [
                              { order_line_item_id: line_item1.id, units: units1 },
                              { order_line_item_id: line_item2.id, units: units2 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
              end

              context '1 unit for lower item' do
                let!(:units1) { 1 }
                let!(:discount_amount1) { -0.82 }

                context '1 unit for higher item' do
                  let!(:units2) { 1 }
                  let!(:discount_amount2) { -1.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '2 units for higher item' do
                  let!(:units2) { 2 }
                  let!(:discount_amount2) { -3 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '3 units for higher item' do
                  let!(:units2) { 3 }
                  let!(:discount_amount2) { -4.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 4 }
                  let!(:discount_amount2) { -6 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 5 }
                  let!(:discount_amount2) { -7.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end
              end

              context '2 units for lower item' do
                let!(:units1) { 2 }
                let!(:discount_amount1) { -1.65 }

                context '1 unit for higher item' do
                  let!(:units2) { 1 }
                  let!(:discount_amount2) { -1.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '2 units for higher item' do
                  let!(:units2) { 2 }
                  let!(:discount_amount2) { -3 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '3 units for higher item' do
                  let!(:units2) { 3 }
                  let!(:discount_amount2) { -4.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 4 }
                  let!(:discount_amount2) { -6 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 5 }
                  let!(:discount_amount2) { -7.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end
              end

              context '3 units for lower item' do
                let!(:units1) { 3 }
                let!(:discount_amount1) { -2.5 }

                context '1 unit for higher item' do
                  let!(:units2) { 1 }
                  let!(:discount_amount2) { -1.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '2 units for higher item' do
                  let!(:units2) { 2 }
                  let!(:discount_amount2) { -3 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '3 units for higher item' do
                  let!(:units2) { 3 }
                  let!(:discount_amount2) { -4.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 4 }
                  let!(:discount_amount2) { -6 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end

                context '4 units for higher item' do
                  let!(:units2) { 5 }
                  let!(:discount_amount2) { -7.5 }

                  it 'Estimate refund has the discount of the order properly calculated' do
                    json_response = JSON.parse(response.body)
                    json_transactions = json_response.delete('transactions')
                    expect(json_response).to eq estimate_response
                    json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                      expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                    end
                    expect(json_transactions).to eq transactions
                  end
                end
              end
            end
          end
        end

        context 'When promotion is a line item adjustment' do
          let!(:promotion) { create(:promotion_with_item_adjustment, :with_item_total_rule) }

          context 'And order has 2 line items with same price' do
            let!(:line_items_attributes) { [{ quantity: 1, price: 24.99 }] * 2 }

            context 'When estimating refund for 1 line item' do
              let(:line_item) { order_with_promotion.line_items.first }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item.price + line_item_tax - 10 + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(line_item.price).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => '10.00',
                  'total_refund_amount' => ::Money.from_amount(line_item.price + line_item_tax + -10.0).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimating refund has the discount of the order' do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: 1 }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end
          end

          context 'And order has 2 line items with different quantities and prices' do
            let!(:line_items_attributes) { [{ quantity: 3, price: 25 }, { quantity: 5, price: 75 }] }

            context 'When estimating refund for lower price line item' do
              let(:line_item) { order_with_promotion.line_items.first }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:product_amount) { line_item.price * units }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (product_amount + line_item_tax + discount_amount + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(product_amount).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => ::Money.from_amount(discount_amount * -1).to_s,
                  'total_refund_amount' => ::Money.from_amount(product_amount + line_item_tax + discount_amount).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              before do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: units }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
              end

              context 'For 1 unit' do
                let!(:units) { 1 }
                let!(:discount_amount) { -3.3 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 2 units' do
                let!(:units) { 2 }
                let!(:discount_amount) { -6.6 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 3 units' do
                let!(:units) { 3 }
                let!(:discount_amount) { -10 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end
            end

            context 'When estimating refund for higher price line item' do
              let(:line_item) { order_with_promotion.line_items.last }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:product_amount) { line_item.price * units }
              let(:transactions) do
                order_with_promotion.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (product_amount + line_item_tax + discount_amount + order_with_promotion.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => ::Money.from_amount(product_amount).to_s,
                  'tax_amount' => ::Money.from_amount(line_item_tax).to_s,
                  'discount_amount' => ::Money.from_amount(discount_amount * -1).to_s,
                  'total_refund_amount' => ::Money.from_amount(product_amount + line_item_tax + discount_amount).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotion.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => ::Money.from_amount(shipment_tax).to_s,
                  'max_refundable_amount' => ::Money.from_amount(order_with_promotion.total - order_with_promotion.refunds.sum(&:amount)).to_s
                }
              end

              before do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: units }
                            ],
                  order_id: order_with_promotion.number
                }

                api_post :estimate, request
              end

              context 'For 1 unit' do
                let!(:units) { 1 }
                let!(:discount_amount) { -2 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 2 units' do
                let!(:units) { 2 }
                let!(:discount_amount) { -4 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 3 units' do
                let!(:units) { 3 }
                let!(:discount_amount) { -6 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 4 units' do
                let!(:units) { 4 }
                let!(:discount_amount) { -8 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end

              context 'For 5 units' do
                let!(:units) { 5 }
                let!(:discount_amount) { -10 }

                it 'Estimate refund has the discount of the order properly calculated' do
                  json_response = JSON.parse(response.body)
                  json_transactions = json_response.delete('transactions')
                  expect(json_response).to eq estimate_response
                  json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                    expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                  end
                  expect(json_transactions).to eq transactions
                end
              end
            end
          end
        end
      end

      context 'Given an order with two promotions' do
        let(:order_with_promotions) { create(:order_with_line_items, line_items_count: 2, line_items_attributes: line_items_attributes, shipment_cost: 5) }
        let(:shipment_tax) { order_with_promotions.all_adjustments.where(source_type: 'Spree::TaxRate', adjustable_type: 'Spree::Shipment').sum(&:amount) }

        before do
          [promotion1, promotion2].each do |promotion|
            promotion_code = promotion.codes.first
            promotion.activate(order: order_with_promotions, promotion_code: promotion_code)
            order_with_promotions.order_promotions.create!(promotion: promotion, promotion_code: promotion_code)
          end

          order.update_column(:completed_at, Time.current)
          order.update_column(:state, 'complete')
        end

        context 'When promotions are at order and line item level' do
          let!(:promotion1) { create(:promotion_with_order_adjustment, :with_item_total_rule) }
          let!(:promotion2) { create(:promotion_with_item_adjustment, :with_item_total_rule) }

          context 'And order has 2 line items with same price' do
            let!(:line_items_attributes) { [{ quantity: 1, price: 24.99 }] * 2 }

            context 'When estimating refund for 1 line item' do
              let(:line_item) { order_with_promotions.line_items.first }
              let(:line_item_tax) { line_item.adjustments.where(source_type: 'Spree::TaxRate').sum(&:amount) }
              let(:transactions) do
                order_with_promotions.payments.completed.each_with_object([]) do |payment, payments|
                  payments << {
                    'id' => payment.id.to_s,
                    'amount' => (line_item.price + line_item_tax + order_with_promotions.ship_total).to_s,
                    'status' => 'PENDING',
                    'type' => 'REFUND',
                    'gateway' => 'BogusCreditCard',
                    'is_online' => true,
                    'is_test' => true,
                    'payment_details' => {

                    }
                  }
                end
              end
              let(:estimate_response) do
                {
                  'product_amount' => line_item.price.to_s,
                  'tax_amount' => '0.00',
                  'discount_amount' => '15.00',
                  'total_refund_amount' => (line_item.price + line_item_tax + -15.0).to_s,
                  'refundable_shipping_amount' => ::Money.from_amount(order_with_promotions.shipment_total).to_s,
                  'refundable_shipping_tax_amount' => '0.00',
                  'max_refundable_amount' => (order_with_promotions.total - order_with_promotions.refunds.sum(&:amount)).to_s
                }
              end

              it 'Estimating refund has the discount of the order' do
                request = {
                  items:    [
                              { order_line_item_id: line_item.id, units: 1 }
                            ],
                  order_id: order_with_promotions.number
                }

                api_post :estimate, request
                json_response = JSON.parse(response.body)
                json_transactions = json_response.delete('transactions')
                expect(json_response).to eq estimate_response
                json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
                  expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
                end
                expect(json_transactions).to eq transactions
              end
            end
          end
        end
      end

      context 'Given an order paid by credit card and store credit' do
        let(:order) { create(:order_with_line_items, line_items_count: 2, line_items_attributes: line_items_attributes, shipment_cost: 5.25) }
        let!(:line_items_attributes) { [{ quantity: 2, price: 21.49 }, { quantity: 4, price: 19.99 }] }
        let!(:credit_card_payment) { create(:credit_card_payment, :completed, order: order, amount: 78.19) }
        let!(:store_credit_payment) { create(:store_credit_payment, order: order, amount: 50, state: 'completed') }

        let(:response_estimate_refund) do
          {
            'product_amount' => product_amount.to_s,
            'tax_amount' => '0.00',
            'discount_amount' => '0.00',
            'total_refund_amount' => product_amount.to_s,
            'refundable_shipping_amount' => '5.25',
            'refundable_shipping_tax_amount' => '0.00',
            'max_refundable_amount' => order.total.to_s
          }
        end

        before do
          order.shipments.each do |shipment|
            shipment.inventory_units.update_all state: 'shipped'
            shipment.update_columns(state: 'shipped', shipped_at: Time.current)
          end

          order.update_columns(shipment_state: 'shipped')
          order.reload

          allow(RefundPayments).to receive(:eligible_refund_methods) {
            ['Spree::PaymentMethod::BogusCreditCard', 'Spree::PaymentMethod::StoreCredit'].map(&:constantize)
          }

          api_post :estimate, line_items_request
        end

        context 'When estimating refund an amount lower than credit card payment' do
          let(:line_item) { order.line_items.first }
          let!(:product_amount) { line_item.price * 2 }
          let!(:line_items_request) do
            {
              items:    [
                          {
                            order_line_item_id: line_item.id,
                            units:              2
                          }
                        ],
              order_id: order.number
            }
          end

          let(:transactions) do
            [
              {
                'id' => '1',
                'amount' => '48.23',
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'BogusCreditCard',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {

                }
              }
            ]
          end

          it 'transactions only includes Credit Card' do
            json = JSON.parse(response.body)
            json_transactions = json.delete('transactions')
            expect(json).to eq response_estimate_refund
            json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
              expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
            end
            expect(json).to eq response_estimate_refund
            expect(json_transactions).to eq transactions
          end
        end

        context 'When estimating refund an amount higher than credit card payment' do
          let(:line_item1) { order.line_items.first }
          let(:line_item2) { order.line_items.last }
          let!(:product_amount) { line_item1.price * 2 + line_item2.price * 2 }
          let!(:line_items_request) do
            {
              items:    [
                          {
                            order_line_item_id: line_item1.id,
                            units:              2
                          },
                          {
                            order_line_item_id: line_item2.id,
                            units:              2
                          }
                        ],
              order_id: order.number
            }
          end

          let(:transactions) do
            [
              {
                'id' => '1',
                'amount' => credit_card_payment.amount.to_s,
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'BogusCreditCard',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {

                }
              },
              {
                'id' => '2',
                'amount' => '10.02',
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'StoreCredit',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {}
              }
            ]
          end

          it 'transactions includes full Credit Card and Store Credit' do
            json = JSON.parse(response.body)
            json_transactions = json.delete('transactions')
            expect(json).to eq response_estimate_refund
            json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
              expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
            end
            expect(json).to eq response_estimate_refund
            expect(json_transactions).to eq transactions
          end
        end

        context 'When estimating refund total order amount' do
          let(:line_item1) { order.line_items.first }
          let(:line_item2) { order.line_items.last }
          let!(:product_amount) { line_item1.price * 2 + line_item2.price * 4 }
          let!(:line_items_request) do
            {
              items:    [
                          {
                            order_line_item_id: line_item1.id,
                            units:              2
                          },
                          {
                            order_line_item_id: line_item2.id,
                            units:              4
                          }
                        ],
              order_id: order.number
            }
          end

          let(:transactions) do
            [
              {
                'id' => '1',
                'amount' => credit_card_payment.amount.to_s,
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'BogusCreditCard',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {

                }
              },
              {
                'id' => '2',
                'amount' => store_credit_payment.amount.to_s,
                'status' => 'PENDING',
                'type' => 'REFUND',
                'gateway' => 'StoreCredit',
                'is_online' => true,
                'is_test' => true,
                'payment_details' => {}
              }
            ]
          end

          it 'transactions includes full Credit Card and full Store Credit' do
            json = JSON.parse(response.body)
            json_transactions = json.delete('transactions')
            expect(json).to eq response_estimate_refund
            json_transactions.map { |t| t.delete('created_at') }.each do |created_at|
              expect(created_at).to match /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$/
            end
            expect(json).to eq response_estimate_refund
            expect(json_transactions).to eq transactions
          end
        end
      end
    end
  end
end

redpajama_set_name: RedPajamaGithub