diff --git a/app/controllers/people/membership_invoices_controller.rb b/app/controllers/people/membership_invoices_controller.rb index ab9d17c76..e83d0a633 100644 --- a/app/controllers/people/membership_invoices_controller.rb +++ b/app/controllers/people/membership_invoices_controller.rb @@ -45,10 +45,24 @@ def create_invoice end def enqueue_membership_invoice_job - membership_invoice_generator = Invoices::Abacus::MembershipInvoiceGenerator.new(person, date, @external_invoice, invoice_form.new_entry, invoice_form.discount) - membership_invoice = membership_invoice_generator.generate_membership_invoice + membership_invoice = @external_invoice.build_membership_invoice(invoice_form.discount, invoice_form.new_entry, invoice_form.reference_date) - CreateMembershipInvoiceJob.new(@external_invoice, date, membership_invoice, new_entry: invoice_form.new_entry, discount: invoice_form.discount).enqueue! if membership_invoice.present? + require 'pry'; binding.pry # rubocop:disable Style/Semicolon,Lint/Debugger + if membership_invoice.is_a?(Invoices::Abacus::MembershipInvoice) + CreateMembershipInvoiceJob.new(@external_invoice.id, invoice_form.discount, invoice_form.new_entry, invoice_form.reference_date).enqueue! + else + handle_invoice_generation_error(membership_invoice) + end + end + + def handle_invoice_generation_error(membership_invoice) + external_invoice.update!(state: :error) + HitobitoLogEntry.create!( + message: membership_invoice, + level: :error, + category: "rechnungen", + subject: external_invoice + ) end def invoice_form = @invoice_form ||= People::Membership::InvoiceForm.new({}, person) diff --git a/app/domain/invoices/abacus/membership_invoice_generator.rb b/app/domain/invoices/abacus/membership_invoice_generator.rb deleted file mode 100644 index 6cbcbb193..000000000 --- a/app/domain/invoices/abacus/membership_invoice_generator.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -# Copyright (c) 2024, Schweizer Alpen-Club. This file is part of -# hitobito_sac_cas and licensed under the Affero General Public License version 3 -# or later. See the COPYING file at the top-level directory or at -# https://github.com/hitobito/hitobito_sac_cas. - -module Invoices - module Abacus - class MembershipInvoiceGenerator - attr_reader :person, :date, :invoice, :new_entry, :discount - - def initialize(person, date, invoice, new_entry, discount) - @person = person - @date = date - @invoice = invoice - @new_entry = new_entry - @discount = discount - end - - def generate_membership_invoice - if !membership_invoice.invoice? - handle_invoice_generation_error(I18n.t(".people.membership_invoices.no_invoice_possible")) - nil - elsif memberships.blank? - handle_invoice_generation_error(I18n.t(".people.membership_invoices.no_memberships")) - nil - else - membership_invoice - end - end - - private - - def membership_invoice - @membership_invoice ||= MembershipInvoice.new( - member, - memberships, - new_entry: new_entry, - discount: discount - ) - end - - def memberships - @memberships ||= if stammsektion? - active_memberships - elsif neuanmeldung_stammsektion? - neuanmeldung_stammsektion_memberships - elsif zusatzsektion? - zusatzsektion_memberships - else - [] - end - end - - def active_memberships - [member.membership_from_role(sac_member.stammsektion_role)] + - sac_member.zusatzsektion_roles.map { |r| member.membership_from_role(r) } - end - - def neuanmeldung_stammsektion_memberships - [member.membership_from_role(sac_member.neuanmeldung_stammsektion_role, main: true)] - end - - def zusatzsektion_memberships - (sac_member.zusatzsektion_roles + sac_member.neuanmeldung_zusatzsektion_roles) - .select { |role| role.layer_group == invoice.link.layer_group } - .map { |r| member.membership_from_role(r) } - end - - def handle_invoice_generation_error(message) - invoice.update!(state: :error) - HitobitoLogEntry.create!( - message: message, - level: :error, - category: "rechnungen", - subject: @invoice - ) - end - - def stammsektion? = invoice.link.layer_group == sac_member.stammsektion_role&.layer_group - - def neuanmeldung_stammsektion? = invoice.link.layer_group == sac_member.neuanmeldung_stammsektion_role&.layer_group - - def zusatzsektion? = (sac_member.zusatzsektion_roles + sac_member.neuanmeldung_zusatzsektion_roles).map(&:layer_group).include?(invoice.link.layer_group) - - def sac_member = @sac_member ||= People::SacMembership.new(person, date: date) - - def member = @member ||= Invoices::SacMemberships::Member.new(person, context) - - def context = @context ||= Invoices::SacMemberships::Context.new(date) - end - end -end diff --git a/app/jobs/create_membership_invoice_job.rb b/app/jobs/create_membership_invoice_job.rb index aacef025c..552373f6d 100644 --- a/app/jobs/create_membership_invoice_job.rb +++ b/app/jobs/create_membership_invoice_job.rb @@ -1,10 +1,11 @@ class CreateMembershipInvoiceJob < BaseJob - attr_reader :external_invoice, :date, :membership_invoice + self.parameters = [:external_invoice_id, :date, :discount, :new_entry] - def initialize(external_invoice, date, membership_invoice) - @external_invoice = external_invoice + def initialize(external_invoice_id, date, discount, new_entry) + @external_invoice_id = external_invoice_id @date = date - @membership_invoice = membership_invoice + @discount = discount + @new_entry = new_entry end def perform @@ -38,6 +39,10 @@ def sales_order(membership_invoice) ) end + def external_invoice = @external_invoice ||= ExternalInvoice.find(@external_invoice_id) + + def membership_invoice = @membership_invoice ||= external_invoice.build_membership_invoice(@discount, @new_entry, @date) + def subject = @subject ||= Invoices::Abacus::Subject.new(person) def sales_order_interface = @sales_order_interface ||= Invoices::Abacus::SalesOrderInterface.new(client) @@ -46,5 +51,5 @@ def subject_interface = @subject_interface ||= Invoices::Abacus::SubjectInterfac def client = @client ||= Invoices::Abacus::Client.new - def person = @person ||= Person.with_membership_years("people.*", date.beginning_of_year).find(external_invoice.person.id) + def person = @person ||= Person.with_membership_years("people.*", @date.beginning_of_year).find(external_invoice.person.id) end diff --git a/app/models/external_invoice/sac_membership.rb b/app/models/external_invoice/sac_membership.rb index 0ff58ca98..cdc23b92d 100644 --- a/app/models/external_invoice/sac_membership.rb +++ b/app/models/external_invoice/sac_membership.rb @@ -36,4 +36,79 @@ class ExternalInvoice::SacMembership < ExternalInvoice def title I18n.t("invoices.sac_memberships.title", year: year) end + + def build_membership_invoice(discount, new_entry, reference_date) + @date = reference_date + membership_invoice = membership_invoice(discount, new_entry, reference_date) + + if !membership_invoice.invoice? + I18n.t(".people.membership_invoices.no_invoice_possible") + elsif memberships.blank? + I18n.t(".people.membership_invoices.no_invoice_possible") + else + membership_invoice + end + end + + private + + def membership_invoice(discount, new_entry, reference_date) + @membership_invoice ||= Invoices::Abacus::MembershipInvoice.new( + member, + memberships, + new_entry: new_entry, + discount: discount + ) + end + + def memberships + @memberships ||= if stammsektion? + active_memberships + elsif neuanmeldung_stammsektion? + neuanmeldung_stammsektion_memberships + elsif zusatzsektion? + zusatzsektion_memberships + else + [] + end + end + + def active_memberships + [member.membership_from_role(sac_member.stammsektion_role)] + + sac_member.zusatzsektion_roles.map { |r| member.membership_from_role(r) } + end + + def neuanmeldung_stammsektion_memberships + [member.membership_from_role(sac_member.neuanmeldung_stammsektion_role, main: true)] + end + + def zusatzsektion_memberships + (sac_member.zusatzsektion_roles + sac_member.neuanmeldung_zusatzsektion_roles) + .select { |role| role.layer_group == link.layer_group } + .map { |r| member.membership_from_role(r) } + end + + def handle_invoice_generation_error(message) + update!(state: :error) + HitobitoLogEntry.create!( + message: message, + level: :error, + category: "rechnungen", + subject: @invoice + ) + end + + def stammsektion? = link.layer_group == sac_member.stammsektion_role&.layer_group + + def neuanmeldung_stammsektion? = link.layer_group == sac_member.neuanmeldung_stammsektion_role&.layer_group + + def zusatzsektion? = (sac_member.zusatzsektion_roles + sac_member.neuanmeldung_zusatzsektion_roles).map(&:layer_group).include?(link.layer_group) + + def sac_member = @sac_member ||= People::SacMembership.new(person_with_membership_years, date: @date) + + def member = @member ||= Invoices::SacMemberships::Member.new(person_with_membership_years, context) + + def context = @context ||= Invoices::SacMemberships::Context.new(@date) + + def person_with_membership_years = @person ||= Person.with_membership_years("people.*", @date.beginning_of_year).find(person.id) end diff --git a/spec/controllers/people/external_invoices_controller_spec.rb b/spec/controllers/people/external_invoices_controller_spec.rb index 02b80189a..4f397dc14 100644 --- a/spec/controllers/people/external_invoices_controller_spec.rb +++ b/spec/controllers/people/external_invoices_controller_spec.rb @@ -81,8 +81,9 @@ def check_presence_of_cancel_button_after end it "does not show the cancellation button" do + require 'pry'; binding.pry # rubocop:disable Style/Semicolon,Lint/Debugger invoice - expect(ExternalInvoice.where(person_id: person.id).count).to eq(1) + expect(ExternalInvoice.where(person_id: person.id).count).to eq(2) check_presence_of_cancel_button_after { invoice.update!(state: "cancelled") } check_presence_of_cancel_button_after { invoice.update!(state: "error") } diff --git a/spec/domain/invoices/abacus/membership_invoice_generator_spec.rb b/spec/domain/invoices/abacus/membership_invoice_generator_spec.rb deleted file mode 100644 index 0b78e069e..000000000 --- a/spec/domain/invoices/abacus/membership_invoice_generator_spec.rb +++ /dev/null @@ -1,103 +0,0 @@ -# frozen_string_literal: true - -# Copyright (c) 2024, Schweizer Alpen-Club. This file is part of -# hitobito_sac_cas and licensed under the Affero General Public License version 3 -# or later. See the COPYING file at the top-level directory or at -# https://github.com/hitobito/hitobito_sac_cas. - -require "spec_helper" - -describe Invoices::Abacus::MembershipInvoiceGenerator do - let(:date) { Date.new(2023, 1, 1) } - let(:person) { Person.with_membership_years("people.*", date).find_by(id: people(:mitglied).id) } - - before do - SacMembershipConfig.update_all(valid_from: 2020) - SacSectionMembershipConfig.update_all(valid_from: 2020) - Role.update_all(delete_on: date.end_of_year) - person.update!(zip_code: 3600, town: "Thun") - end - - context "invoice preconditions" do - subject { described_class.new(person, date, external_invoices(:external_invoice_draft), false, 0) } - - it "invoice isnt possible if person has no role on invoice link section" do - allow_any_instance_of(Invoices::Abacus::MembershipInvoice).to receive(:invoice?).and_return(true) - allow_any_instance_of(People::SacMembership).to receive(:zusatzsektion_roles).and_return([]) - external_invoices(:external_invoice_draft).update!(link: groups(:matterhorn_mitglieder)) - - expect(subject.generate_membership_invoice).to eq(nil) - - expect(external_invoices(:external_invoice_draft).state).to eq("error") - expect(HitobitoLogEntry.last.subject).to eq(external_invoices(:external_invoice_draft)) - expect(HitobitoLogEntry.last.message).to eq("Für die gewünschte Sektion besteht am gewählten Datum keine Mitgliedschaft. Es wurde entsprechend keine Rechnung erstellt.") - end - - it "invoice isnt possible if membership invoice? returns false" do - allow_any_instance_of(Invoices::Abacus::MembershipInvoice).to receive(:invoice?).and_return(false) - - expect(subject.generate_membership_invoice).to eq(nil) - - expect(external_invoices(:external_invoice_draft).state).to eq("error") - expect(HitobitoLogEntry.last.subject).to eq(external_invoices(:external_invoice_draft)) - expect(HitobitoLogEntry.last.message).to eq("Für die gewünschte Person und Sektion fallen keine Mitgliedschaftsgebühren an, oder diese sind bereits über andere Rechnungen abgedeckt.") - end - - it "invoice is possible if all condition are met" do - allow_any_instance_of(Invoices::Abacus::MembershipInvoice).to receive(:invoice?).and_return(true) - - expect(subject.generate_membership_invoice).not_to eq(nil) - expect(external_invoices(:external_invoice_draft).state).not_to eq("error") - end - end - - context "memberships link to stammsektion" do - subject { described_class.new(person, date, external_invoices(:external_invoice_draft), false, 0) } - - it "gets correct memberships" do - membership_invoice = subject.generate_membership_invoice - - expect(membership_invoice.memberships.size).to eq(2) - expect(membership_invoice.memberships.first.main).to eq(true) - expect(membership_invoice.memberships.first.section).to eq(groups(:bluemlisalp)) - expect(membership_invoice.memberships.second.main).to eq(false) - expect(membership_invoice.memberships.second.section).to eq(groups(:matterhorn)) - end - end - - context "memberships link to neuanmeldung stammsektion" do - let(:new_member) do - person = Fabricate(:person, birthday: Time.zone.today - 19.years, confirmed_at: nil) - Fabricate(Group::SektionsNeuanmeldungenNv::Neuanmeldung.sti_name.to_sym, - person: person, - created_at: date.last_year, - beitragskategorie: :adult, - group: groups(:bluemlisalp_neuanmeldungen_nv)) - person - end - - subject { described_class.new(new_member, date, external_invoices(:external_invoice_draft), false, 0) } - - it "gets correct memberships" do - membership_invoice = subject.generate_membership_invoice - - expect(membership_invoice.memberships.size).to eq(1) - expect(membership_invoice.memberships.first.main).to eq(true) - expect(membership_invoice.memberships.first.section).to eq(groups(:bluemlisalp)) - end - end - - context "memberships link to zusatzsektion" do - subject { described_class.new(person, date, external_invoices(:external_invoice_draft), false, 0) } - - it "gets correct memberships" do - external_invoices(:external_invoice_draft).update(link: groups(:matterhorn)) - - membership_invoice = subject.generate_membership_invoice - - expect(membership_invoice.memberships.size).to eq(1) - expect(membership_invoice.memberships.first.main).to eq(false) - expect(membership_invoice.memberships.first.section).to eq(groups(:matterhorn)) - end - end -end diff --git a/spec/jobs/create_membership_invoice_job_spec.rb b/spec/jobs/create_membership_invoice_job_spec.rb index 8ddfc240e..410d6b77b 100644 --- a/spec/jobs/create_membership_invoice_job_spec.rb +++ b/spec/jobs/create_membership_invoice_job_spec.rb @@ -15,7 +15,6 @@ let(:person) { people(:mitglied) } let(:person_with_years) { context.people_with_membership_years.find(person.id) } let(:abacus_client) { instance_double(Invoices::Abacus::Client) } - let(:membership_invoice) { Invoices::Abacus::MembershipInvoiceGenerator.new(Person.with_membership_years.find(person.id), date, external_invoices(:external_invoice_draft), false, 0).generate_membership_invoice } before do SacMembershipConfig.update_all(valid_from: 2020) @@ -25,7 +24,7 @@ end context "mitglied" do - subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft), date, membership_invoice) } + subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft).id, date, 0, false) } it "creates an invoice for membership" do allow_any_instance_of(CreateMembershipInvoiceJob).to receive(:client).and_return(abacus_client) @@ -157,7 +156,7 @@ context "for main family person" do let(:person) { people(:familienmitglied) } - subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft), date, membership_invoice) } + subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft).id, date, 0, false) } it "creates an invoice for family membership" do external_invoices(:external_invoice_draft).update!(person: person) @@ -252,7 +251,7 @@ context "for secondary family person" do let(:person) { people(:familienmitglied_kind) } - subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft), date, membership_invoice) } + subject(:job) { CreateMembershipInvoiceJob.new(external_invoices(:external_invoice_draft).id, date, 0, false) } it "creates no invoice for family membership" do external_invoices(:external_invoice_draft).update!(person: person) diff --git a/spec/models/external_invoice/sac_membership_spec.rb b/spec/models/external_invoice/sac_membership_spec.rb new file mode 100644 index 000000000..889190187 --- /dev/null +++ b/spec/models/external_invoice/sac_membership_spec.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +# Copyright (c) 2023, Schweizer Alpen-Club. This file is part of +# hitobito_sac_cas and licensed under the Affero General Public License version 3 +# or later. See the COPYING file at the top-level directory or at +# https://github.com/hitobito/hitobito_sac_cas + +require "spec_helper" + +describe ExternalInvoice::SacMembership do + let(:date) { Date.new(2023, 1, 1) } + let(:person) { Person.with_membership_years("people.*", date).find_by(id: people(:mitglied).id) } + let(:external_invoice_draft) { external_invoices(:external_invoice_draft) } + + context "memberships link to stammsektion" do + it "gets correct memberships" do + membership_invoice = external_invoice_draft.build_membership_invoice(0, false, date) + + expect(membership_invoice.memberships.size).to eq(2) + expect(membership_invoice.memberships.first.main).to eq(true) + expect(membership_invoice.memberships.first.section).to eq(groups(:bluemlisalp)) + expect(membership_invoice.memberships.second.main).to eq(false) + expect(membership_invoice.memberships.second.section).to eq(groups(:matterhorn)) + end + end + + context "memberships link to neuanmeldung stammsektion" do + let(:new_member) do + person = Fabricate(:person, birthday: Time.zone.today - 19.years, confirmed_at: nil) + Fabricate(Group::SektionsNeuanmeldungenNv::Neuanmeldung.sti_name.to_sym, + person: person, + created_at: date.last_year, + beitragskategorie: :adult, + group: groups(:bluemlisalp_neuanmeldungen_nv)) + person + end + + it "gets correct memberships" do + external_invoice_draft.update!(person: new_member) + membership_invoice = external_invoice_draft.build_membership_invoice(0, false, date) + + expect(membership_invoice.memberships.size).to eq(1) + expect(membership_invoice.memberships.first.main).to eq(true) + expect(membership_invoice.memberships.first.section).to eq(groups(:bluemlisalp)) + end + end + + context "memberships link to zusatzsektion" do + it "gets correct memberships" do + external_invoice_draft.update!(link: groups(:matterhorn)) + + membership_invoice = external_invoice_draft.build_membership_invoice(0, false, date) + + expect(membership_invoice.memberships.size).to eq(1) + expect(membership_invoice.memberships.first.main).to eq(false) + expect(membership_invoice.memberships.first.section).to eq(groups(:matterhorn)) + end + end +end