# frozen_string_literal: true require 'spec_helper' RSpec.shared_examples "attribute changes - payment total" do |boolean, type| before do Spree::Order.where(id: order.id).update_all(payment_total: order.payment_total + 10) end it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end RSpec.shared_examples "attribute changes - order total" do |boolean, type| before do Spree::Order.where(id: order.id).update_all(total: order.total + 10) end it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end RSpec.shared_examples "attribute changes - order state: cancelled" do |boolean, type| before do order.cancel! end it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end RSpec.shared_examples "attribute changes - tax total changes" do |boolean, type, included_boolean| let(:order) do create(:order_with_taxes, product_price: 110, tax_rate_amount: 0.1, included_in_price: included_boolean) .tap do |order| order.create_tax_charge! order.update_shipping_fees! end end context "if included_in_price is #{included_boolean}" do before do Spree::TaxRate.first.update!(amount: 0.15) order.create_tax_charge! end it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be true end end end RSpec.shared_examples "attribute changes - shipping method" do |boolean, type| let(:shipping_method) { create(:shipping_method) } before do Spree::ShippingRate.first.update(shipping_method_id: shipping_method.id) end it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end RSpec.shared_examples "no attribute changes" do it "returns false if no attribute has changed" do expect(subject).to be false end end RSpec.shared_examples "attribute changes - special insctructions" do |boolean, type| before do order.update!(special_instructions: "A very special insctruction.") end it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end RSpec.shared_examples "attribute changes - note" do |boolean, type| before do order.update!(note: "THIS IS A NEW NOTE") end it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end RSpec.shared_examples "associated attribute changes - adjustments (create/delete)" do |boolean, type| context "creating an adjustment" do before { order.adjustments << create(:adjustment, order:) } it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end context "deleting an adjustment" do before { order.all_adjustments.destroy_all } it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end end RSpec.shared_examples "associated attribute changes - adjustments (edit amount)" do |boolean, type| context "with an existing adjustments" do context "editing the amount" do before { order.all_adjustments.first.update!(amount: 123) } it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end end end RSpec.shared_examples "associated attribute changes - adjustments (edit label)" do |boolean, type| context "adjustment changes" do context "editing the label" do before { order.all_adjustments.first.update!(label: "It's a new label") } it "returns #{boolean} if a #{type} attribute changes" do expect(subject).to be boolean end end end end RSpec.shared_examples "associated attribute changes - line items" do |boolean, type| context "line item changes" do let(:line_item) { order.line_items.first } let(:variant) { create(:variant) } context "on quantitity" do before { line_item.update!(quantity: line_item.quantity + 1) } it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end context "on variant id" do before { line_item.update!(variant_id: variant.id) } it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end end end RSpec.shared_examples "associated attribute changes - bill address" do |boolean, type| context "bill address - a #{type}" do let(:bill_address) { Spree::Address.where(id: order.bill_address_id) } it "first name" do bill_address.update!(firstname: "Jane") order.reload expect(subject).to be boolean end it "last name" do bill_address.update!(lastname: "Jones") order.reload expect(subject).to be boolean end it "address (1)" do bill_address.update!(address1: "Rue du Fromage 66") order.reload expect(subject).to be boolean end it "address (2)" do bill_address.update!(address2: "South by Southwest") order.reload expect(subject).to be boolean end it "city" do bill_address.update!(city: "Antibes") order.reload expect(subject).to be boolean end it "zipcode" do bill_address.update!(zipcode: "04229") order.reload expect(subject).to be boolean end it "phone" do bill_address.update!(phone: "111-222-333") order.reload expect(subject).to be boolean end it "company" do bill_address.update!(company: "A Company Name") order.reload expect(subject).to be boolean end end end RSpec.shared_examples "associated attribute changes - ship address" do |boolean, type| context "ship address - a #{type}" do let(:ship_address) { Spree::Address.where(id: order.ship_address_id) } it "first name" do ship_address.update!(firstname: "Jane") order.reload expect(subject).to be boolean end it "last name" do ship_address.update!(lastname: "Jones") order.reload expect(subject).to be boolean end it "address (1)" do ship_address.update!(address1: "Rue du Fromage 66") order.reload expect(subject).to be boolean end it "address (2)" do ship_address.update!(address2: "South by Southwest") order.reload expect(subject).to be boolean end it "city" do ship_address.update!(city: "Antibes") order.reload expect(subject).to be boolean end it "zipcode" do ship_address.update!(zipcode: "04229") order.reload expect(subject).to be boolean end it "phone" do ship_address.update!(phone: "111-222-333") order.reload expect(subject).to be boolean end it "company" do ship_address.update!(company: "A Company Name") order.reload expect(subject).to be boolean end end end RSpec.shared_examples "associated attribute changes - payments" do |boolean, type| context "payment changes on" do let(:payment) { create(:payment, order_id: order.id) } context "amount" do before { payment.update!(amount: 222) } it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end context "payment changes on" do let(:payment_method) { create(:payment_method) } context "payment method" do before { payment.update!(payment_method_id: payment_method.id) } it "returns #{boolean} if a #{type} attribute changes" do order.reload expect(subject).to be boolean end end end end end RSpec.shared_examples "attribute changes - payment state" do |boolean, type| let(:payment) { order.payments.first } context "payment changes on" do context "state" do it "returns #{boolean} if a #{type} attribute changes" do expect { payment.started_processing }.to change { payment.state }.from("checkout").to("processing") order.reload expect(subject).to be boolean end end end end RSpec.describe Orders::CompareInvoiceService do let!(:invoice){ create(:invoice, order:, data: invoice_data_generator.serialize_for_invoice) } describe '#can_generate_new_invoice?' do # this passes 'order' as argument to the invoice comparator let(:order) { create(:completed_order_with_fees) } let!(:invoice_data_generator){ InvoiceDataGenerator.new(order) } let(:subject) { Orders::CompareInvoiceService.new(order).can_generate_new_invoice? } context "changes on the order object" do describe "detecting relevant" do it_behaves_like "attribute changes - order total", true, "relevant" it_behaves_like "attribute changes - tax total changes", true, "relevant", false it_behaves_like "attribute changes - tax total changes", true, "relevant", true it_behaves_like "attribute changes - shipping method", true, "relevant" # creating and deleting adjustments is relevant both for generate and update comparator it_behaves_like "associated attribute changes - adjustments (create/delete)", true, "relevant" it_behaves_like "associated attribute changes - adjustments (edit amount)", true, "relevant" it_behaves_like "associated attribute changes - bill address", true, "relevant" it_behaves_like "associated attribute changes - ship address", true, "relevant" it_behaves_like "associated attribute changes - line items", true, "relevant" it_behaves_like "associated attribute changes - payments", true, "relevant" # order-state change is relevant both for generate and update comparator it_behaves_like "attribute changes - order state: cancelled", true, "relevant" end describe "detecting non-relevant" do it_behaves_like "attribute changes - payment total", false, "relevant" do before { pending("a payment capture shouldn't trigger a new invoice - issue #11350") } end it_behaves_like "no attribute changes" it_behaves_like "attribute changes - special insctructions", false, "non-relevant" it_behaves_like "attribute changes - note", false, "non-relevant" it_behaves_like "associated attribute changes - adjustments (edit label)", false, "non-relevant" it_behaves_like "attribute changes - payment state", false, "non-relevant" end end end describe '#can_update_latest_invoice?' do let!(:order) { create(:completed_order_with_fees) } let!(:invoice_data_generator){ InvoiceDataGenerator.new(order) } let(:subject) { Orders::CompareInvoiceService.new(order).can_update_latest_invoice? } context "changes on the order object" do describe "detecting relevant" do it_behaves_like "attribute changes - payment total", true, "relevant" do before { pending("a payment capture shouldn't trigger a new invoice - issue #11350") } end # order-state change is relevant both for generate and update comparator it_behaves_like "attribute changes - order state: cancelled", true, "relevant" it_behaves_like "attribute changes - special insctructions", true, "relevant" it_behaves_like "attribute changes - note", true, "relevant" it_behaves_like "associated attribute changes - adjustments (edit label)", true, "relevant" it_behaves_like "attribute changes - payment state", true, "relevant" # creating and deleting adjustments is relevant both for generate and update comparator it_behaves_like "associated attribute changes - adjustments (create/delete)", true, "relevant" end describe "detecting non-relevant" do it_behaves_like "attribute changes - order total", false, "non-relevant" it_behaves_like "attribute changes - tax total changes", false, "non-relevant", false it_behaves_like "attribute changes - tax total changes", false, "non-relevant", true it_behaves_like "attribute changes - shipping method", false, "non-relevant" it_behaves_like "no attribute changes" it_behaves_like "associated attribute changes - line items", false, "non-relevant" it_behaves_like "associated attribute changes - bill address", false, "non-relevant" it_behaves_like "associated attribute changes - ship address", false, "non-relevant" it_behaves_like "associated attribute changes - payments", false, "non-relevant" do before { pending("also related to issue #11350") } end it_behaves_like "associated attribute changes - adjustments (edit amount)", false, "non-relevant" end end end end