Permalink
Browse files

added support for manual journals

  • Loading branch information...
1 parent e4c58f5 commit a73317d06f65143e54dec899c200080b16b20cb4 claudiocontin committed May 29, 2012
View
@@ -1,3 +1,7 @@
+h2. 2.0.15, released 28/05/2012
+
+* Add support for manual journals
+
h2. 2.0.14, released 07/05/2012
* Add support for bank transactions
View
@@ -26,6 +26,8 @@
require File.join(File.dirname(__FILE__), 'xero_gateway', 'invoice')
require File.join(File.dirname(__FILE__), 'xero_gateway', 'bank_transaction')
require File.join(File.dirname(__FILE__), 'xero_gateway', 'credit_note')
+require File.join(File.dirname(__FILE__), 'xero_gateway', 'journal_line')
+require File.join(File.dirname(__FILE__), 'xero_gateway', 'manual_journal')
require File.join(File.dirname(__FILE__), 'xero_gateway', 'address')
require File.join(File.dirname(__FILE__), 'xero_gateway', 'phone')
require File.join(File.dirname(__FILE__), 'xero_gateway', 'organisation')
@@ -43,4 +43,5 @@ def message
class InvoiceNotFoundError < StandardError; end
class BankTransactionNotFoundError < StandardError; end
class CreditNoteNotFoundError < StandardError; end
+ class ManualJournalNotFoundError < StandardError; end
end
@@ -418,6 +418,56 @@ def get_bank_transaction(bank_transaction_id)
parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/BankTransaction'})
end
+ # Creates a manual journal in Xero based on a manual journal object.
+ #
+ # Manual journal and line item totals are calculated automatically.
+ #
+ # Usage : # TODO
+
+ def create_manual_journal(manual_journal)
+ save_manual_journal(manual_journal)
+ end
+
+ #
+ # Updates an existing Xero manual journal
+ #
+ # Usage :
+ #
+ # manual_journal = xero_gateway.get_manual_journal(some_manual_journal_id)
+ #
+ # xero_gateway.update_manual_journal(manual_journal)
+ def update_manual_journal(manual_journal)
+ raise "manual_journal_id is required for updating manual journals" if manual_journal.manual_journal_id.nil?
+ save_manual_journal(manual_journal)
+ end
+
+ # Retrieves all manual journals from Xero
+ #
+ # Usage : get_manual_journal
+ # getmanual_journal(:manual_journal_id => " 297c2dc5-cc47-4afd-8ec8-74990b8761e9")
+ #
+ # Note : modified_since is in UTC format (i.e. Brisbane is UTC+10)
+ def get_manual_journals(options = {})
+ request_params = {}
+ request_params[:ManualJournalID] = options[:manual_journal_id] if options[:manual_journal_id]
+ request_params[:ModifiedAfter] = options[:modified_since] if options[:modified_since]
+
+ response_xml = http_get(@client, "#{@xero_url}/ManualJournals", request_params)
+
+ parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/ManualJournals'})
+ end
+
+ # Retrieves a single manual journal
+ #
+ # Usage : get_manual_journal("297c2dc5-cc47-4afd-8ec8-74990b8761e9") # By ID
+ # get_manual_journal("OIT-12345") # By number
+ def get_manual_journal(manual_journal_id)
+ request_params = {}
+ url = "#{@xero_url}/ManualJournals/#{URI.escape(manual_journal_id)}"
+ response_xml = http_get(@client, url, request_params)
+ parse_response(response_xml, {:request_params => request_params}, {:request_signature => 'GET/ManualJournal'})
+ end
+
#
# Gets all accounts for a specific organization in Xero.
#
@@ -555,6 +605,33 @@ def save_bank_transaction(bank_transaction)
response
end
+ # Create or update a manual journal record based on if it has an manual_journal_id.
+ def save_manual_journal(manual_journal)
+ request_xml = manual_journal.to_xml
+ response_xml = nil
+ create_or_save = nil
+
+ if manual_journal.manual_journal_id.nil?
+ # Create new manual journal record.
+ response_xml = http_put(@client, "#{@xero_url}/ManualJournals", request_xml, {})
+ create_or_save = :create
+ else
+ # Update existing manual journal record.
+ response_xml = http_post(@client, "#{@xero_url}/ManualJournals", request_xml, {})
+ create_or_save = :save
+ end
+
+ response = parse_response(response_xml, {:request_xml => request_xml}, {:request_signature => "#{create_or_save == :create ? 'PUT' : 'POST'}/ManualJournals"})
+
+ # Xero returns manual journals inside an <ManualJournals> tag, even though there's only ever
+ # one for this request
+ response.response_item = response.manual_journals.first
+
+ manual_journal.manual_journal_id = response.manual_journal.manual_journal_id if response.success? && response.manual_journal && response.manual_journal.manual_journal_id
+
+ response
+ end
+
def parse_response(raw_response, request = {}, options = {})
response = XeroGateway::Response.new
@@ -576,12 +653,18 @@ def parse_response(raw_response, request = {}, options = {})
when "Invoice" then response.response_item = Invoice.from_xml(element, self, {:line_items_downloaded => options[:request_signature] != "GET/Invoices"})
when "BankTransaction"
response.response_item = BankTransaction.from_xml(element, self, {:line_items_downloaded => options[:request_signature] != "GET/BankTransactions"})
+ when "ManualJournal"
+ response.response_item = ManualJournal.from_xml(element, self, {:journal_lines_downloaded => options[:request_signature] != "GET/ManualJournals"})
when "Contacts" then element.children.each {|child| response.response_item << Contact.from_xml(child, self) }
when "Invoices" then element.children.each {|child| response.response_item << Invoice.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/Invoices"}) }
when "BankTransactions"
element.children.each do |child|
response.response_item << BankTransaction.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/BankTransactions"})
end
+ when "ManualJournals"
+ element.children.each do |child|
+ response.response_item << ManualJournal.from_xml(child, self, {:journal_lines_downloaded => options[:request_signature] != "GET/ManualJournals"})
+ end
when "CreditNotes" then element.children.each {|child| response.response_item << CreditNote.from_xml(child, self, {:line_items_downloaded => options[:request_signature] != "GET/CreditNotes"}) }
when "Accounts" then element.children.each {|child| response.response_item << Account.from_xml(child) }
when "TaxRates" then element.children.each {|child| response.response_item << TaxRate.from_xml(child) }
@@ -0,0 +1,102 @@
+require File.join(File.dirname(__FILE__), 'account')
+
+module XeroGateway
+ class JournalLine
+ include Money
+
+ TAX_TYPE = Account::TAX_TYPE unless defined?(TAX_TYPE)
+
+ # Any errors that occurred when the #valid? method called.
+ attr_reader :errors
+
+ # All accessible fields
+ attr_accessor :journal_line_id, :line_amount, :account_code, :description, :tax_type, :tracking
+
+ def initialize(params = {})
+ @errors ||= []
+ @tracking ||= []
+
+ params.each do |k,v|
+ self.send("#{k}=", v)
+ end
+ end
+
+ # Validate the JournalLineItem record according to what will be valid by the gateway.
+ #
+ # Usage:
+ # journal_line_item.valid? # Returns true/false
+ #
+ # Additionally sets journal_line_item.errors array to an array of field/error.
+ def valid?
+ @errors = []
+
+ if !journal_line_id.nil? && journal_line_id !~ GUID_REGEX
+ @errors << ['journal_line_id', 'must be blank or a valid Xero GUID']
+ end
+
+ unless line_amount
+ @errors << ['line_amount', "can't be blank"]
+ end
+
+ unless account_code
+ @errors << ['account_code', "can't be blank"]
+ end
+
+ @errors.size == 0
+ end
+
+ def has_tracking?
+ return false if tracking.nil?
+
+ if tracking.is_a?(Array)
+ return tracking.any?
+ else
+ return tracking.is_a?(TrackingCategory)
+ end
+ end
+
+ def to_xml(b = Builder::XmlMarkup.new)
+ b.JournalLine {
+ b.LineAmount line_amount # mandatory
+ b.AccountCode account_code # mandatory
+ b.Description description if description # optional
+ b.TaxType tax_type if tax_type # optional
+ if has_tracking?
+ b.Tracking { # optional
+ # Due to strange retardness in the Xero API, the XML structure for a tracking category within
+ # an invoice is different to a standalone tracking category.
+ # This means rather than going category.to_xml we need to call the special category.to_xml_for_invoice_messages
+ (tracking.is_a?(TrackingCategory) ? [tracking] : tracking).each do |category|
+ category.to_xml_for_invoice_messages(b)
+ end
+ }
+ end
+ }
+ end
+
+ def self.from_xml(journal_line_element)
+ journal_line = JournalLine.new
+ journal_line_element.children.each do |element|
+ case(element.name)
+ when "LineAmount" then journal_line.line_amount = BigDecimal.new(element.text)
+ when "AccountCode" then journal_line.account_code = element.text
+ when "JournalLineID" then journal_line.journal_line_id = element.text
+ when "Description" then journal_line.description = element.text
+ when "TaxType" then journal_line.tax_type = element.text
+ when "Tracking" then
+ element.children.each do | tracking_element |
+ journal_line.tracking << TrackingCategory.from_xml(tracking_element)
+ end
+ end
+ end
+ journal_line
+ end
+
+ def ==(other)
+ [:description, :line_amount, :account_code, :tax_type].each do |field|
+ return false if send(field) != other.send(field)
+ end
+ return true
+ end
+ end
+end
Oops, something went wrong.

0 comments on commit a73317d

Please sign in to comment.