lib/xero_gateway/report.rb in xero_gateway-2.5.0 vs lib/xero_gateway/report.rb in xero_gateway-2.6.0

- old
+ new

@@ -1,95 +1,116 @@ +require_relative './report/cell' +require_relative './report/row' + module XeroGateway class Report + include Money include Dates attr_reader :errors attr_accessor :report_id, :report_name, :report_type, :report_titles, :report_date, :updated_at, :body, :column_names + alias :rows :body + def initialize(params={}) @errors ||= [] @report_titles ||= [] @body ||= [] params.each do |k,v| self.send("#{k}=", v) 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 |row| + report.body << row + 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).values + report_sections = REXML::XPath.each(xml_element, "//RowType[text()='Section']/parent::Row") - 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 + report_sections.each do |section_row| + section_name = section_row.get_elements("Title").first.try(:text) + section_row.elements.each("Rows/Row") do |xpath_cells| + values = find_body_cell_values(xpath_cells) + yield Row.new(column_names, values, section_name) + end + 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 <Value>...</Value> - values << value.text.try(:strip) - next + 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 - 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 <Value>...</Value> - names_map[column_key] = column_name - end - names_map + def find_body_cell_values(xml_cells) + values = [] + xml_cells.elements.each("Cells/Cell") do |xml_cell| + if value = xml_cell.children.first # finds <Value>...</Value> + values << Cell.new(value.text.try(:strip), collect_attributes(xml_cell)) + next + end + values << nil + end + values + end + + # Collects "<Attribute>" 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 <Value>...</Value> + names_map[column_key] = column_name + end + names_map + end end end end