diff --git a/lib/xero_gateway/report.rb b/lib/xero_gateway/report.rb
index b5593045..f197ab95 100644
--- a/lib/xero_gateway/report.rb
+++ b/lib/xero_gateway/report.rb
@@ -1,5 +1,8 @@
+require_relative './report/cell'
+
module XeroGateway
class Report
+
include Money
include Dates
@@ -17,78 +20,93 @@ def initialize(params={})
end
end
- def self.from_xml(report_element)
- report = Report.new
- report_element.children.each do | element |
- case element.name
- when 'ReportID' then report.report_id = element.text
- when 'ReportName' then report.report_name = element.text
- when 'ReportType' then report.report_type = element.text
- when 'ReportTitles'
- each_title(element) do |title|
- report.report_titles << title
- end
- when 'ReportDate' then report.report_date = Date.parse(element.text)
- when 'UpdatedDateUTC' then report.updated_at = parse_date_time_utc(element.text)
- when 'Rows'
- report.column_names ||= find_body_column_names(element)
- each_row_content(element) do |content_hash|
- report.body << OpenStruct.new(content_hash)
- end
+ class << self
+
+ def from_xml(report_element)
+ report = Report.new
+ report_element.children.each do | element |
+ case element.name
+ when 'ReportID' then report.report_id = element.text
+ when 'ReportName' then report.report_name = element.text
+ when 'ReportType' then report.report_type = element.text
+ when 'ReportTitles'
+ each_title(element) do |title|
+ report.report_titles << title
+ end
+ when 'ReportDate' then report.report_date = Date.parse(element.text)
+ when 'UpdatedDateUTC' then report.updated_at = parse_date_time_utc(element.text)
+ when 'Rows'
+ report.column_names ||= find_body_column_names(element)
+ each_row_content(element) do |content_hash|
+ report.body << OpenStruct.new(content_hash)
+ end
+ end
end
+ report
end
- report
- end
- private
+ private
- def self.each_row_content(xml_element, &block)
- column_names = find_body_column_names(xml_element).keys
- xpath_body = REXML::XPath.first(xml_element, "//RowType[text()='Section']").parent
- rows_contents = []
- xpath_body.elements.each("Rows/Row") do |xpath_cells|
- values = find_body_cell_values(xpath_cells)
- content_hash = Hash[column_names.zip values]
- rows_contents << content_hash
- yield content_hash if block_given?
- end
- rows_contents
- end
+ def each_row_content(xml_element, &block)
+ column_names = find_body_column_names(xml_element).keys
+ xpath_body = REXML::XPath.first(xml_element, "//RowType[text()='Section']").parent
+ rows_contents = []
+ xpath_body.elements.each("Rows/Row") do |xpath_cells|
+ values = find_body_cell_values(xpath_cells)
+ content_hash = Hash[column_names.zip values]
+ rows_contents << content_hash
+ yield content_hash if block_given?
+ end
+ rows_contents
+ end
- def self.each_title(xml_element, &block)
- xpath_titles = REXML::XPath.first(xml_element, "//ReportTitles")
- xpath_titles.elements.each("//ReportTitle") do |xpath_title|
- title = xpath_title.text.strip
- yield title if block_given?
- end
- end
+ def each_title(xml_element, &block)
+ xpath_titles = REXML::XPath.first(xml_element, "//ReportTitles")
+ xpath_titles.elements.each("//ReportTitle") do |xpath_title|
+ title = xpath_title.text.strip
+ yield title if block_given?
+ end
+ end
- def self.find_body_cell_values(xml_cells)
- values = []
- xml_cells.elements.each("Cells/Cell") do |xml_cell|
- if value = xml_cell.children.first # finds ...
- values << value.text.try(:strip)
- next
+ def find_body_cell_values(xml_cells)
+ values = []
+ xml_cells.elements.each("Cells/Cell") do |xml_cell|
+ if value = xml_cell.children.first # finds ...
+ values << Cell.new(value.text.try(:strip), collect_attributes(xml_cell))
+ next
+ end
+ values << nil
+ end
+ values
end
- values << nil
- end
- values
- end
- # returns something like { column_1: "Amount", column_2: "Description", ... }
- def self.find_body_column_names(body)
- header = REXML::XPath.first(body, "//RowType[text()='Header']")
- names_map = {}
- column_count = 0
- header.parent.elements.each("Cells/Cell") do |header_cell|
- column_count += 1
- column_key = "column_#{column_count}".to_sym
- column_name = nil
- name_value = header_cell.children.first
- column_name = name_value.text.strip unless name_value.blank? # finds ...
- names_map[column_key] = column_name
- end
- names_map
+ # Collects "" elements into a hash
+ def collect_attributes(xml_cell)
+ Array.wrap(xml_cell.elements["Attributes/Attribute"]).inject({}) do |hash, xml_attribute|
+ if (key = xml_attribute.elements["Id"].try(:text)) &&
+ (value = xml_attribute.elements["Value"].try(:text))
+
+ hash[key] = value
+ end
+ hash
+ end.symbolize_keys
+ end
+
+ # returns something like { column_1: "Amount", column_2: "Description", ... }
+ def find_body_column_names(body)
+ header = REXML::XPath.first(body, "//RowType[text()='Header']")
+ names_map = {}
+ column_count = 0
+ header.parent.elements.each("Cells/Cell") do |header_cell|
+ column_count += 1
+ column_key = "column_#{column_count}".to_sym
+ column_name = nil
+ name_value = header_cell.children.first
+ column_name = name_value.text.strip unless name_value.blank? # finds ...
+ names_map[column_key] = column_name
+ end
+ names_map
+ end
end
end
diff --git a/lib/xero_gateway/report/cell.rb b/lib/xero_gateway/report/cell.rb
new file mode 100644
index 00000000..b1a95fe0
--- /dev/null
+++ b/lib/xero_gateway/report/cell.rb
@@ -0,0 +1,28 @@
+module XeroGateway
+ class Report
+
+ # Adds #attributes to the cells we're grabbing, since Xero Report Cells use XML like:
+ #
+ # Interest Income (270)
+ #
+ #
+ # e9482110-7245-4a76-bfe2-14500495a076
+ # account
+ #
+ #
+ # |
+ #
+ # We delegate to the topmost "" class and decorate with an "attributes" hash
+ # for the "attribute: value" pairs
+ class Cell < SimpleDelegator
+ attr_reader :attributes, :value
+
+ def initialize(value, new_attributes = {})
+ @value = value
+ @attributes = new_attributes
+ super(value)
+ end
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/test/stub_responses/reports/bank_statement.xml b/test/stub_responses/reports/bank_statement.xml
index d8cf0b8f..ef53452d 100644
--- a/test/stub_responses/reports/bank_statement.xml
+++ b/test/stub_responses/reports/bank_statement.xml
@@ -97,10 +97,12 @@
2014-05-01T00:00:00
-
- asasdas-asdf-asdf-asdf-asdfasdfasdfas
- statementID
-
+
+
+ asasdas-asdf-asdf-asdf-asdfasdfasdfas
+ statementID
+
+
|
Eft
diff --git a/test/unit/report_test.rb b/test/unit/report_test.rb
index 2abe6006..e46eee16 100644
--- a/test/unit/report_test.rb
+++ b/test/unit/report_test.rb
@@ -19,60 +19,81 @@ class ReportTest < Test::Unit::TestCase
end
context :from_xml do
- setup do
- xml_response = get_file("reports/bank_statement.xml")
- xml_response.gsub!(/\n +/,'')
- xml_doc = REXML::Document.new(xml_response)
- xpath_report = XPath.first(xml_doc, "//Report")
- @report = XeroGateway::Report.from_xml(xpath_report)
- end
+ context "with a bank statement report" do
+ setup do
+ @report = make_report_from_xml("bank_statement")
+ end
+
+ should "create a bank statement report" do
+ assert @report.is_a?(XeroGateway::Report)
+ assert_equal [], @report.errors
+ assert_equal Date.parse("27 May 2014"), @report.report_date
+ assert_equal "BankStatement", @report.report_id
+ assert_equal "Bank Statement", @report.report_name
+ expected_titles = ["Bank Statement", "Business Bank Account", "Demo Company (NZ)", "From 1 May 2014 to 27 May 2014"]
+ assert_equal expected_titles, @report.report_titles
+ assert_equal "BankStatement", @report.report_type
+ assert_equal Time.parse("2014-05-26 22:36:07 +0000").to_i, @report.updated_at.to_i
+ expected_names = { :column_1=>"Date", :column_2=>"Description", :column_3=>"Reference", :column_4=>"Reconciled", :column_5=>"Source", :column_6=>"Amount", :column_7=>"Balance" }
+ assert_equal expected_names, @report.column_names
- should "create a bank statement report" do
- assert @report.is_a?(XeroGateway::Report)
- assert_equal [], @report.errors
- assert_equal Date.parse("27 May 2014"), @report.report_date
- assert_equal "BankStatement", @report.report_id
- assert_equal "Bank Statement", @report.report_name
- expected_titles = ["Bank Statement", "Business Bank Account", "Demo Company (NZ)", "From 1 May 2014 to 27 May 2014"]
- assert_equal expected_titles, @report.report_titles
- assert_equal "BankStatement", @report.report_type
- assert_equal Time.parse("2014-05-26 22:36:07 +0000").to_i, @report.updated_at.to_i
- expected_names = { :column_1=>"Date", :column_2=>"Description", :column_3=>"Reference", :column_4=>"Reconciled", :column_5=>"Source", :column_6=>"Amount", :column_7=>"Balance" }
- assert_equal expected_names, @report.column_names
+ ###
+ # REPORT BODY
+ assert @report.body.is_a?(Array)
- ###
- # REPORT BODY
- assert @report.body.is_a?(Array)
+ # First = Opening Balance
+ first_statement = @report.body.first
+ assert_equal "2014-05-01T00:00:00", first_statement.column_1
+ assert_equal "Opening Balance", first_statement.column_2
+ assert_equal nil, first_statement.column_3
+ assert_equal nil, first_statement.column_4
+ assert_equal nil, first_statement.column_5
+ assert_equal nil, first_statement.column_6
+ assert_equal "15461.97", first_statement.column_7
- # First = Opening Balance
- first_statement = @report.body.first
- assert_equal "2014-05-01T00:00:00", first_statement.column_1
- assert_equal "Opening Balance", first_statement.column_2
- assert_equal nil, first_statement.column_3
- assert_equal nil, first_statement.column_4
- assert_equal nil, first_statement.column_5
- assert_equal nil, first_statement.column_6
- assert_equal "15461.97", first_statement.column_7
+ # Second = Bank Transaction/Statement
+ second_statement = @report.body.second
+ assert_equal "2014-05-01T00:00:00", second_statement.column_1
+ assert_equal "Ridgeway Banking Corporation", second_statement.column_2
+ assert_equal "Fee", second_statement.column_3
+ assert_equal "No", second_statement.column_4
+ assert_equal "Import", second_statement.column_5
+ assert_equal "-15.00", second_statement.column_6
+ assert_equal "15446.97", second_statement.column_7
+
+ # Third
+ third_statement = @report.body.third
+ assert_equal nil, third_statement.column_2.value # no description, but other attributes
+ assert_equal "Eft", third_statement.column_3
+ assert_equal "No", third_statement.column_4
+ assert_equal "Import", third_statement.column_5
+ assert_equal "-15.75", third_statement.column_6
+ assert_equal "15431.22", third_statement.column_7
+ end
+ end
- # Second = Bank Transaction/Statement
- second_statement = @report.body.second
- assert_equal "2014-05-01T00:00:00", second_statement.column_1
- assert_equal "Ridgeway Banking Corporation", second_statement.column_2
- assert_equal "Fee", second_statement.column_3
- assert_equal "No", second_statement.column_4
- assert_equal "Import", second_statement.column_5
- assert_equal "-15.00", second_statement.column_6
- assert_equal "15446.97", second_statement.column_7
+ context "with a trial balance report" do
+ setup do
+ @report = make_report_from_xml("trial_balance")
+ end
- # Third
- third_statement = @report.body.third
- assert_equal nil, third_statement.column_2 # no description, but other attributes
- assert_equal "Eft", third_statement.column_3
- assert_equal "No", third_statement.column_4
- assert_equal "Import", third_statement.column_5
- assert_equal "-15.75", third_statement.column_6
- assert_equal "15431.22", third_statement.column_7
+ should "set attributes on individual cells" do
+ first_statement = @report.body.first
+ assert_equal "Sales (200)", first_statement.column_1.value
+ assert_equal({ account: "7d05a53d-613d-4eb2-a2fc-dcb6adb80b80" }, first_statement.column_1.attributes)
+ end
end
+
+ end
+
+ private
+
+ def make_report_from_xml(report_name = "bank_statement")
+ xml_response = get_file("reports/#{report_name}.xml")
+ xml_response.gsub!(/\n +/,'')
+ xml_doc = REXML::Document.new(xml_response)
+ xpath_report = XPath.first(xml_doc, "//Report")
+ XeroGateway::Report.from_xml(xpath_report)
end
end
|