lib/xeroizer/models/invoice.rb in xeroizer-2.15.5 vs lib/xeroizer/models/invoice.rb in xeroizer-2.15.6
- old
+ new
@@ -1,34 +1,38 @@
+require "xeroizer/models/attachment"
+
module Xeroizer
module Record
-
+
class InvoiceModel < BaseModel
# To create a new invoice, use the folowing
# $xero_client.Invoice.build(type: 'ACCREC', ..., contact: {name: 'Foo Bar'},...)
# Note that we are not making an api request to xero just to get the contact
-
+
set_permissions :read, :write, :update
-
+
+ include AttachmentModel::Extensions
+
public
-
+
# Retrieve the PDF version of the invoice matching the `id`.
# @param [String] id invoice's ID.
# @param [String] filename optional filename to store the PDF in instead of returning the data.
def pdf(id, filename = nil)
pdf_data = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", :response => :pdf)
if filename
- File.open(filename, "w") { | fp | fp.write pdf_data }
+ File.open(filename, "wb") { | fp | fp.write pdf_data }
nil
else
pdf_data
end
end
-
+
end
-
+
class Invoice < Base
-
+
INVOICE_TYPE = {
'ACCREC' => 'Accounts Receivable',
'ACCPAY' => 'Accounts Payable'
} unless defined?(INVOICE_TYPE)
INVOICE_TYPES = INVOICE_TYPE.keys.sort
@@ -40,15 +44,17 @@
'PAID' => 'Invoices approved and fully paid',
'SUBMITTED' => 'Invoices entered by an employee awaiting approval',
'VOIDED' => 'Approved invoices that are voided'
} unless defined?(INVOICE_STATUS)
INVOICE_STATUSES = INVOICE_STATUS.keys.sort
-
+
+ include Attachment::Extensions
+
set_primary_key :invoice_id
set_possible_primary_keys :invoice_id, :invoice_number
list_contains_summary_only true
-
+
guid :invoice_id
string :invoice_number
string :reference
guid :branding_theme_id
string :url
@@ -63,28 +69,30 @@
decimal :amount_due
decimal :amount_paid
decimal :amount_credited
datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
string :currency_code
+ decimal :currency_rate
datetime :fully_paid_on_date
boolean :sent_to_contact
-
+ boolean :has_attachments
+
belongs_to :contact
has_many :line_items
has_many :payments
has_many :credit_notes
-
+
validates_presence_of :date, :due_date, :unless => :new_record?
validates_inclusion_of :type, :in => INVOICE_TYPES
validates_inclusion_of :status, :in => INVOICE_STATUSES, :unless => :new_record?
validates_inclusion_of :line_amount_types, :in => LINE_AMOUNT_TYPES, :unless => :new_record?
validates_associated :contact
validates_associated :line_items, :allow_blanks => true, :unless => :approved?
validates_associated :line_items, :if => :approved?
-
+
public
-
+
# Access the contact name without forcing a download of
# an incomplete, summary invoice.
def contact_name
attributes[:contact] && attributes[:contact][:name]
end
@@ -92,31 +100,31 @@
# Access the contact ID without forcing a download of an
# incomplete, summary invoice.
def contact_id
attributes[:contact] && attributes[:contact][:contact_id]
end
-
+
# Helper method to check if the invoice has been approved.
def approved?
[ 'AUTHORISED', 'PAID', 'VOIDED' ].include? status
end
-
+
# Helper method to check if the invoice is accounts payable.
def accounts_payable?
type == 'ACCPAY'
end
# Helper method to check if the invoice is accounts receivable.
def accounts_receivable?
type == 'ACCREC'
end
-
+
def sub_total=(sub_total)
@sub_total_is_set = true
attributes[:sub_total] = sub_total
end
-
+
def total_tax=(total_tax)
@total_tax_is_set = true
attributes[:total_tax] = total_tax
end
@@ -127,13 +135,13 @@
# Calculate sub_total from line_items.
def sub_total(always_summary = false)
if !@sub_total_is_set && not_summary_or_loaded_record(always_summary)
sum = (line_items || []).inject(BigDecimal.new('0')) { | sum, line_item | sum + line_item.line_amount }
-
+
# If the default amount types are inclusive of 'tax' then remove the tax amount from this sub-total.
- sum -= total_tax if line_amount_types == 'Inclusive'
+ sum -= total_tax if line_amount_types == 'Inclusive'
sum
else
attributes[:sub_total]
end
end
@@ -153,43 +161,48 @@
sub_total + total_tax
else
attributes[:total]
end
end
-
+
def not_summary_or_loaded_record(always_summary)
!always_summary && loaded_record?
end
def loaded_record?
- new_record? ||
+ new_record? ||
(!new_record? && line_items && line_items.size > 0)
end
-
+
# Retrieve the PDF version of this invoice.
# @param [String] filename optional filename to store the PDF in instead of returning the data.
def pdf(filename = nil)
parent.pdf(id, filename)
end
# Delete an approved invoice with no payments.
def delete!
- delete_or_void_invoice!('DELETED')
+ change_status!('DELETED')
end
# Void an approved invoice with no payments.
def void!
- delete_or_void_invoice!('VOIDED')
+ change_status!('VOIDED')
end
+ # Approve a draft invoice
+ def approve!
+ change_status!('AUTHORISED')
+ end
+
protected
- def delete_or_void_invoice!(new_status)
- raise CannotChangeInvoiceStatus.new(record, new_status) unless self.payments.size == 0
+ def change_status!(new_status)
+ raise CannotChangeInvoiceStatus.new(self, new_status) unless self.payments.size == 0
self.status = new_status
self.save
end
-
+
end
-
+
end
end