diff --git a/app/jobs/update_user_invoices.rb b/app/jobs/update_user_invoices.rb new file mode 100644 index 0000000000..fe2ee6edcc --- /dev/null +++ b/app/jobs/update_user_invoices.rb @@ -0,0 +1,59 @@ +UpdateUserInvoices = Struct.new("UpdateUserInvoices") do + def perform + # If it is the first of the month, update invoices for the previous month up until midnight last night + # Otherwise, update invoices for the current month + start_date = (Time.now - 1.day).beginning_of_month + end_date = Time.now.beginning_of_day + + # Find all users that have owned an enterprise at some point in the current billing period (this month) + enterprise_users = Spree::User.joins(:billable_periods) + .where('billable_periods.begins_at >= (?) AND billable_periods.ends_at <= (?)', start_date, end_date) + .select('DISTINCT spree_users.*') + + enterprise_users.each do |user| + update_invoice_for(user, user.billable_periods.where('begins_at >= (?) AND ends_at <= (?)', start_date, end_date)) + end + end + + def update_invoice_for(user, billable_periods) + invoice = user.current_invoice + + billable_periods.each do |billable_period| + adjustment = invoice.adjustments.where(source: billable_period).first + adjustment ||= invoice.adjustments.new( adjustment_attrs_from(billable_period) ) + adjustment.label = adjustment_label_from(billable_period) + adjustment.amount = billable_period.bill + adjustment.save + end + + finalize(invoice) + end + + def adjustment_attrs_from(billable_period) + { :source => billable_period, + :originator => billable_period, + :mandatory => mandatory, + :locked => true } + end + + def adjustment_label_from(billable_period) + category = enterprise.version_at(billable_period.begins_at).reify.category.to_s.titleize + category += (billable_period.trial ? " Trial" : "") + begins = billable_period.begins_at.strftime("%d/%m") + ends = billable_period.begins_at.strftime("%d/%m") + + "#{enterprise.name} (#{category}) [#{begins}-#{ends}]" + end + + def finalize(invoice) + if Date.today.day == 1 + while @order.state != "complete" + @order.next + end + user.current_invoice.process + # Mark current invoice as completed + # Create a new invoice + user.current_invoice = new_invoice_for(user) + end + end +end diff --git a/app/models/spree/user_decorator.rb b/app/models/spree/user_decorator.rb index 13ab56c129..080b1e5251 100644 --- a/app/models/spree/user_decorator.rb +++ b/app/models/spree/user_decorator.rb @@ -7,6 +7,7 @@ Spree.user_class.class_eval do has_many :enterprises, through: :enterprise_roles has_many :owned_enterprises, class_name: 'Enterprise', foreign_key: :owner_id, inverse_of: :owner has_many :owned_groups, class_name: 'EnterpriseGroup', foreign_key: :owner_id, inverse_of: :owner + has_many :billable_periods, foreign_key: :owner_id, inverse_of: :owner has_one :cart has_many :customers diff --git a/spec/jobs/update_user_invoices_spec.rb b/spec/jobs/update_user_invoices_spec.rb new file mode 100644 index 0000000000..a059af41a6 --- /dev/null +++ b/spec/jobs/update_user_invoices_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +def travel_to(time) + around { |example| Timecop.travel(start_of_july + time) { example.run } } +end + +describe UpdateUserInvoices do + describe "units specs" do + let!(:start_of_july) { Time.now.beginning_of_year + 6.months } + + let!(:updater) { UpdateUserInvoices.new } + + describe "perform" do + let!(:user) { create(:user) } + let!(:old_billable_period) { create(:billable_period, owner: user, begins_at: start_of_july - 1.month, ends_at: start_of_july) } + let!(:billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } + let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } + + before do + allow(updater).to receive(:update_invoice_for) + end + + context "on the first of the month" do + travel_to(3.hours) + + it "updates the user's current invoice with billable_periods from the previous month" do + updater.perform + expect(updater).to have_received(:update_invoice_for).once + .with(user, [old_billable_period]) + end + end + + context "on other days" do + travel_to(20.days) + + it "updates the user's current invoice with billable_periods from the current month" do + updater.perform + expect(updater).to have_received(:update_invoice_for).once + .with(user, [billable_period1, billable_period2]) + end + end + end + + describe "update_invoice_for" do + let!(:user) { create(:user) } + let!(:billable_period1) { create(:billable_period, owner: user, begins_at: start_of_july, ends_at: start_of_july + 12.days) } + let!(:billable_period2) { create(:billable_period, owner: user, begins_at: start_of_july + 12.days, ends_at: start_of_july + 20.days) } + + + end + end +end