0

So I'm trying to build out on an Invoice page the past_due_amount where I'm trying to find only the invoices for the current account, that are not paid off, and should be in the past.

So roughly I have:

past_due_amount = Invoice.where(account: invoice.account, status: :unpaid).where('date < ? ', invoice.date).map(&:due).sum

For additional context here are the models involved:

Invoice:

class Invoice < ApplicationRecord
 belongs_to :account

 has_many :line_items, dependent: :destroy
 has_many :payment_destinations, dependent: :destroy
 has_many :prorated_fees, dependent: :nullify

 enum status: [:unpaid, :paid]

 validates :date, presence: true
 validates :period_start, :period_end,
   uniqueness: { scope: :account, allow_blank: true }, on: :create
 validate :start_is_before_end

 DAYS_DUE_AFTER_DATE = 14.days

 scope :descending, -> { order(date: :desc) }
 scope :ascending, -> { order(date: :asc) }
 scope :due, -> { unpaid.where(arel_table[:date].lteq(Time.zone.today - DAYS_DUE_AFTER_DATE)) }

 def total
  if persisted?
    line_items.sum(:amount)
  else
    line_items.map(&:amount).sum
  end

end end

Account:

 class Account < ApplicationRecord
 belongs_to :customer
 belongs_to :property_address,
   class_name: Address.to_s,
   dependent: :destroy,
   required: false

 [:products, :account_changes, :equipments,
  :payments, :invoices].each do |assoc|
   has_many assoc, dependent: :destroy
 end

 accepts_nested_attributes_for :property_address
 delegate :street, :city, :state, :zip,
   to: :property_address, allow_nil: true
 delegate :email, :full_name, to: :customer

 enum status: [:staged, :active, :inactive]

 scope :active_or_staged, -> { where(status: [:staged, :active]) }
 scope :past_due, lambda {
   joins(:invoices)
     .where(
       Invoice.arel_table[:status].eq(:unpaid)
       .and(Invoice.arel_table[:date].lt(Time.zone.today - 14.days))
     ).distinct
 }

 scope :search, lambda { |term|
   joins(:customer)
     .where(
       arel_table[:account_num].matches("%#{term}%")
       .or(Customer.arel_search(term))
     )
 }
end

With the rough code in place I decided to build out a instance variable on the InvoicesController within the show method as below:

 def show
  @invoice = Invoice.find_by!(id: params[:id], account: current_customer.account_ids)
  @account = @invoice.account
  @past_due_amount = Invoice.where(account: @account, status: :unpaid).where('date < ?', @invoice.date).map(&:due).sum
 end

No errors appear but that's not saying much since the examples I have are poor, at best. But my question is...should I actually be putting this in a helper instead of the show method on an InvoicesController or even in the model?

EDIT:

I've also tried putting in my Invoice model:

def self.past_due_amount
 Invoice.where(account: @account, status: :unpaid).where('date < ?', @invoice.date).map(&:due).sum
end

Then in my InvoicesController:

def show
 @invoice = Invoice.find_by!(id: params[:id], account: current_customer.account_ids)
 @account = @invoice.account
 @past_due_amount = Invoice.past_due_amount
end 

End up getting undefined method `date' for @invoice.date.

3 Answers 3

1

The best way is to create a method past_due_amount in the InvoicesHelper

module InvoicesHelper

  def past_due_amount
    Invoice.where(account: @account, status: :unpaid).where('date  <?', @invoice.date).map(&:due).sum
   end
end

In you controller just initialize all the instance variables

def show
 @invoice = Invoice.find_by!(id: params[:id], account: current_customer.account_ids)
 @account = @invoice.account
end 

In the view you should use: <%= past_due_amount > to show your data

Sign up to request clarification or add additional context in comments.

Comments

1

Create an instance method in Account model

def past_due_amount
  invoices.map(&:due).sum
end

and then from view you can all it @account.past_due_amount. no need to create extra instance variable in controller action

Comments

0

So I sort of used Patrick's answer but it was actually failing so I switched to passing invoice as params.

Helper

module InvoicesHelper
 def past_due_amount(invoice)
  Invoice.where(account: invoice.account, status: :unpaid).where('date < ?', invoice.date).map(&:due).sum
 end
end

Then in my view:

<% if past_due_amount(invoice).positive? %>
 <p><%= number_to_currency past_due_amount(invoice) %></p>
<% end %>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.