qbxml 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 19cc9c6c600d5ad6185008a0c4d7c273a928844f
4
- data.tar.gz: 5a2ef8033e05136789d019a7c30b38eae2dbefbb
3
+ metadata.gz: 11572014f70b00b684710498b5f2072e4c716a34
4
+ data.tar.gz: d8d4be361ed0a330345637c270221fb06f1f03c0
5
5
  SHA512:
6
- metadata.gz: 51ddbdc7b3f078649fd0ac8373d2f598779d1b403dde03b0cc932876dc3378ca0447802874d8f5b52f4285c0674424d2a91d52c82474181ded3690b7b38f0d0a
7
- data.tar.gz: 6a6a08ae03de03de815ca57d995e6e8e1027d7f3ef1baf7e9d8dd561628a202dc094dd1bfc96c6af0e5874d4bb2dcbd0979a743c4a433bd2fd104ebc4c9f69f9
6
+ metadata.gz: bfac0afceeca6efd56a5459f0c21b1180d76c6705ff070a30d254db62c35255015e66d1be3ecc6fe88272b0f351ca18d06e38c0f32e5e9b810c871d324720436
7
+ data.tar.gz: eb5a9c9a4346d810b638000608ef37f02d382a5dc9683576bc67f757af49a0b0e66fbcb1bcd691c171bd1ea6d861941fd80510175857ebf83d69bc95d56feb47
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ .ruby-version
19
+ .ruby-gemset
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
data/Gemfile CHANGED
@@ -1,11 +1,3 @@
1
1
  source 'https://rubygems.org'
2
- gemspec
3
-
4
- # test
5
- gem 'pry'
6
- gem 'pry-nav'
7
- gem 'rspec'
8
- gem 'simplecov', require: false, group: :test
9
2
 
10
- # backwards compatibility tests
11
- gem 'quickbooks_api', path: '../old/quickbooks_api'
3
+ gemspec
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Qbxml
2
2
 
3
+ [![Build Status](https://travis-ci.org/qbwc/qbxml.svg?branch=master)](https://travis-ci.org/qbwc/qbxml)
4
+
3
5
  Qbxml is a QBXML parser and validation tool.
4
6
 
5
7
  ## Installation
@@ -20,11 +22,11 @@ Or install it yourself as:
20
22
 
21
23
  ### Initialization
22
24
 
23
- The parser can be initialized to either Quickbooks (:qb) or Quickbooks Point of
24
- Sale (:qbpos)
25
+ The QBXML supported depends on whether you use QuickBooks (`:qb`) or
26
+ QuickBooks Point of Sale (`:qbpos`) and on the version of QuickBooks used.
25
27
 
26
28
  ```ruby
27
- q = Qbxml.new(:qb)
29
+ q = Qbxml.new(:qb, '7.0')
28
30
  ```
29
31
 
30
32
  ### API Introspection
@@ -73,18 +75,7 @@ q.to_qbxml(hsh, validate: true)
73
75
 
74
76
  ## Caveats
75
77
 
76
- Correct case conversion depends on the following ActiveSupport inflection
77
- settings. Correct behaviour cannot be guaranteed if any of the following
78
- inflections are modified.
79
-
80
- ```ruby
81
- ACRONYMS = ['AP', 'AR', 'COGS', 'COM', 'UOM', 'QBXML', 'UI', 'AVS', 'ID',
82
- 'PIN', 'SSN', 'COM', 'CLSID', 'FOB', 'EIN', 'UOM', 'PO', 'PIN', 'QB']
83
-
84
- ActiveSupport::Inflector.inflections do |inflect|
85
- ACRONYMS.each { |a| inflect.acronym a }
86
- end
87
- ```
78
+ QuickBooks only supports [ISO-8859-1](http://en.wikipedia.org/wiki/ISO/IEC_8859-1) characters. Any characters outside of ISO-8859-1 will become question marks in QuickBooks.
88
79
 
89
80
  ## Contributing
90
81
 
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ end
7
+ task :default => :test
@@ -15,7 +15,11 @@ class Qbxml::Hash < ::Hash
15
15
  def self.from_hash(hash, opts = {}, &block)
16
16
  key_proc = \
17
17
  if opts[:camelize]
18
- lambda { |k| k.camelize }
18
+ lambda { |k|
19
+ # QB wants things like ListID, not ListId. Adding inflections then using camelize can accomplish
20
+ # the same thing, but then the inflections will apply to everything the user does everywhere.
21
+ k.camelize.gsub(Qbxml::Types::ACRONYM_REGEXP) { "#{$1}#{$2.upcase}#{$3}" }
22
+ }
19
23
  elsif opts[:underscore]
20
24
  lambda { |k| k.underscore }
21
25
  end
@@ -51,6 +55,7 @@ private
51
55
  builder = opts[:builder]
52
56
 
53
57
  unless opts.delete(:skip_instruct)
58
+ builder.instruct!(:xml, :encoding => "ISO-8859-1")
54
59
  builder.instruct!(opts[:schema], version: opts[:version])
55
60
  end
56
61
 
@@ -60,7 +65,12 @@ private
60
65
  when Hash
61
66
  self.hash_to_xml(val, opts.merge({root: key, skip_instruct: true}))
62
67
  when Array
63
- val.map { |i| self.hash_to_xml(i, opts.merge({root: key, skip_instruct: true})) }
68
+ val.map { |i|
69
+ if i.is_a?(String)
70
+ next builder.tag!(key, i, {})
71
+ end
72
+ next self.hash_to_xml(i, opts.merge({root: key, skip_instruct: true}))
73
+ }
64
74
  else
65
75
  builder.tag!(key, val, {})
66
76
  end
@@ -78,9 +88,12 @@ private
78
88
 
79
89
  # Insert node hash into parent hash correctly.
80
90
  case hash[name]
81
- when Array then hash[name] << node_hash
82
- when Hash then hash[name] = [hash[name], node_hash]
83
- else hash[name] = node_hash
91
+ when Array
92
+ hash[name] << node_hash
93
+ when Hash, String
94
+ hash[name] = [hash[name], node_hash]
95
+ else
96
+ hash[name] = node_hash
84
97
  end
85
98
 
86
99
  # Handle child elements
@@ -103,12 +116,14 @@ private
103
116
  node_hash.delete(CONTENT_ROOT)
104
117
  elsif node_hash[CONTENT_ROOT].present?
105
118
  node_hash.delete(ATTR_ROOT)
106
- hash[name] = \
107
- if schema
108
- typecast(schema, node.path, node_hash[CONTENT_ROOT], opts[:typecast_cache])
109
- else
110
- node_hash[CONTENT_ROOT]
111
- end
119
+ v = schema ? typecast(schema, node.path, node_hash[CONTENT_ROOT], opts[:typecast_cache]) : node_hash[CONTENT_ROOT]
120
+ # We only updated the last element
121
+ if hash[name].is_a?(Array)
122
+ hash[name].pop
123
+ hash[name] << v
124
+ else
125
+ hash[name] = v
126
+ end
112
127
  else
113
128
  hash[name] = node_hash[CONTENT_ROOT]
114
129
  end
@@ -86,6 +86,8 @@ class Qbxml
86
86
  end
87
87
 
88
88
  def select_schema(schema_key, version)
89
+ # Try to handle it if a user gave us a numeric version. Assume 1 decimal.
90
+ version = '%.1f' % version if version.is_a?(Numeric)
89
91
  raise "invalid schema '#{schema_key}', must be one of #{SCHEMAS.keys.inspect}" if !SCHEMAS.has_key?(schema_key)
90
92
  raise "invalid version '#{version}' for schema #{schema_key}, must be one of #{SCHEMAS[schema_key].keys.inspect}" if !SCHEMAS[schema_key].has_key?(version)
91
93
  return SCHEMAS[schema_key][version]
@@ -30,11 +30,14 @@ module Qbxml::Types
30
30
  "TIMEINTERVALTYPE" => STR_CAST
31
31
  }
32
32
 
33
+ # Strings in tag names that should be capitalized in QB's XML
33
34
  ACRONYMS = ['AP', 'AR', 'COGS', 'COM', 'UOM', 'QBXML', 'UI', 'AVS', 'ID',
34
35
  'PIN', 'SSN', 'COM', 'CLSID', 'FOB', 'EIN', 'UOM', 'PO', 'PIN', 'QB']
35
36
 
36
- ActiveSupport::Inflector.inflections do |inflect|
37
- ACRONYMS.each { |a| inflect.acronym a }
38
- end
37
+ # Based on the regexp in ActiveSupport::Inflector.camelize
38
+ # Substring 1: Start of string, lower case letter, or slash
39
+ # Substring 2: One of the acronyms above, In Capitalized Casing
40
+ # Substring 3: End of string or capital letter
41
+ ACRONYM_REGEXP = Regexp.new("(?:(^|[a-z]|\\/))(#{ACRONYMS.map{|a| a.capitalize}.join("|")})([A-Z]|$)")
39
42
 
40
43
  end
@@ -1,3 +1,3 @@
1
1
  class Qbxml
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -6,8 +6,8 @@ require 'qbxml/version'
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "qbxml"
8
8
  gem.version = Qbxml::VERSION
9
- gem.authors = ["Alex Skryl"]
10
- gem.email = ["rut216@gmail.com"]
9
+ gem.authors = ["Alex Skryl", "Jason Barnabe"]
10
+ gem.email = ["rut216@gmail.com", "jason.barnabe@gmail.com"]
11
11
  gem.description = %q{Quickbooks XML Parser}
12
12
  gem.summary = %q{Quickbooks XML Parser and Validation Tool}
13
13
  gem.homepage = ""
@@ -20,4 +20,10 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency('activesupport', '>= 3.2.9')
21
21
  gem.add_dependency('nokogiri', '~> 1.5')
22
22
  gem.add_dependency('builder', '~> 3.0')
23
+
24
+ gem.add_development_dependency('pry')
25
+ gem.add_development_dependency('pry-nav')
26
+ gem.add_development_dependency('rspec')
27
+ gem.add_development_dependency('simplecov')
28
+ gem.add_development_dependency('rake')
23
29
  end
@@ -0,0 +1,78 @@
1
+ require 'minitest/autorun'
2
+ require 'qbxml'
3
+
4
+ class HashToXmlTest < Minitest::Test
5
+
6
+ def test_hash_to_xml_customer_query
7
+ qbxml = Qbxml.new
8
+ assert_equal "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<?qbxml version=\"7.0\"?>\n<QBXML>\n <QBXMLMsgsRq>\n <CustomerQueryRq>\n <ListID>GUID-GOES-HERE</ListID>\n </CustomerQueryRq>\n </QBXMLMsgsRq>\n</QBXML>\n", qbxml.to_qbxml({:qbxml => {:qbxml_msgs_rq => {:customer_query_rq => {:list_id => 'GUID-GOES-HERE'}}}})
9
+ end
10
+
11
+ def test_hash_to_xml_invoice_mod
12
+ qbxml = Qbxml.new
13
+ xml = <<-EOF
14
+ <?xml version="1.0" encoding="ISO-8859-1"?>
15
+ <?qbxml version="7.0"?>
16
+ <QBXML>
17
+ <QBXMLMsgsRq>
18
+ <InvoiceModRq>
19
+ <InvoiceMod>
20
+ <TxnID>1929B9-1423150873</TxnID>
21
+ <EditSequence/>
22
+ <CustomerRef>
23
+ <ListID>4A50001-1013529664</ListID>
24
+ </CustomerRef>
25
+ <TxnDate>2015-01-28</TxnDate>
26
+ <RefNumber>12345678</RefNumber>
27
+ <Memo></Memo>
28
+ <InvoiceLineMod>
29
+ <TxnLineID>-1</TxnLineID>
30
+ <ItemRef>
31
+ <FullName>Sales</FullName>
32
+ </ItemRef>
33
+ <Desc>Contract 123</Desc>
34
+ <Quantity>23.44165</Quantity>
35
+ <Rate>515.0</Rate>
36
+ <SalesTaxCodeRef>
37
+ <FullName>E</FullName>
38
+ </SalesTaxCodeRef>
39
+ </InvoiceLineMod>
40
+ </InvoiceMod>
41
+ </InvoiceModRq>
42
+ </QBXMLMsgsRq>
43
+ </QBXML>
44
+ EOF
45
+ assert_equal xml, qbxml.to_qbxml({:invoice_mod_rq=>{:invoice_mod=>{:txn_id=>"1929B9-1423150873", :edit_sequence=>nil, :customer_ref=>{:list_id=>"4A50001-1013529664"}, :txn_date=>"2015-01-28", :ref_number=>"12345678", :memo=>"", :invoice_line_mod=>[{:txn_line_id=>-1, :item_ref=>{:full_name=>"Sales"}, :desc=>"Contract 123", :quantity=>"23.44165", :rate=> 515.0, :sales_tax_code_ref=>{:full_name=>"E"}}]}}})
46
+ end
47
+
48
+ def test_hash_to_xml_customer_add
49
+ qbxml = Qbxml.new
50
+ xml = <<-EOF
51
+ <?xml version="1.0" encoding="ISO-8859-1"?>
52
+ <?qbxml version="7.0"?>
53
+ <QBXML>
54
+ <QBXMLMsgsRq>
55
+ <CustomerAddRq>
56
+ <CustomerAdd>
57
+ <Name>Joe Blow</Name>
58
+ <CompanyName>Joe Blow Inc.</CompanyName>
59
+ <BillAddress>
60
+ <Addr1>123 Fake Street</Addr1>
61
+ <City>Springfield</City>
62
+ <State>Texachussets</State>
63
+ <PostalCode>99999</PostalCode>
64
+ <Country>USA</Country>
65
+ </BillAddress>
66
+ </CustomerAdd>
67
+ </CustomerAddRq>
68
+ </QBXMLMsgsRq>
69
+ </QBXML>
70
+ EOF
71
+ assert_equal xml, qbxml.to_qbxml({:customer_add_rq=>{:customer_add=>{:name=>"Joe Blow", :company_name=>"Joe Blow Inc.", :bill_address=>{:addr1=>"123 Fake Street", :city=>"Springfield", :state=>"Texachussets", :postal_code=>"99999", :country=>"USA"}}}})
72
+ end
73
+
74
+ def test_array_of_strings
75
+ assert_equal "<foo>\n <bar>baz</bar>\n <bar>guh</bar>\n</foo>\n", Qbxml::Hash.to_xml({:foo => {:bar => ['baz', 'guh']}}, {skip_instruct: true})
76
+ end
77
+
78
+ end
@@ -0,0 +1,24 @@
1
+ require 'minitest/autorun'
2
+ require 'qbxml'
3
+
4
+ class VersionTest < Minitest::Test
5
+
6
+ def test_string_version
7
+ Qbxml.new(:qb, '7.0')
8
+ end
9
+
10
+ def test_bad_version
11
+ assert_raises RuntimeError do
12
+ Qbxml.new(:qb, '3.14')
13
+ end
14
+ end
15
+
16
+ def test_float_version
17
+ Qbxml.new(:qb, 7.0)
18
+ end
19
+
20
+ def test_int_version
21
+ Qbxml.new(:qb, 7)
22
+ end
23
+
24
+ end
@@ -0,0 +1,30 @@
1
+ require 'minitest/autorun'
2
+ require 'qbxml'
3
+
4
+ class XmlToHashTest < Minitest::Test
5
+
6
+ def test_xml_to_hash
7
+ qbxml = Qbxml.new
8
+ h = {"qbxml"=>{"xml_attributes"=>{}, "qbxml_msgs_rq"=>{"xml_attributes"=>{}, "customer_query_rq"=>{"xml_attributes"=>{}, "list_id"=>"GUID-GOES-HERE"}}}}
9
+ assert_equal h, qbxml.from_qbxml("<?qbxml version=\"7.0\"?>\n<QBXML>\n <QBXMLMsgsRq>\n <CustomerQueryRq>\n <ListID>GUID-GOES-HERE</ListID>\n </CustomerQueryRq>\n </QBXMLMsgsRq>\n</QBXML>\n")
10
+ end
11
+
12
+ def test_array_of_strings
13
+ qbxml = Qbxml.new
14
+ h = {
15
+ "qbxml" => {
16
+ "xml_attributes" => {},
17
+ "qbxml_msgs_rq" => {
18
+ "xml_attributes" => {},
19
+ 'invoice_query_rq' => {
20
+ "xml_attributes" => {},
21
+ 'include_ret_element' => ['TxnID', 'RefNumber']
22
+ }
23
+ }
24
+ }
25
+ }
26
+ assert_equal h, qbxml.from_qbxml("<?qbxml version=\"7.0\"?>\n<QBXML>\n <QBXMLMsgsRq>\n <InvoiceQueryRq>\n <IncludeRetElement>TxnID</IncludeRetElement>\n <IncludeRetElement>RefNumber</IncludeRetElement>\n </InvoiceQueryRq>\n </QBXMLMsgsRq>\n</QBXML>\n")
27
+ end
28
+
29
+ end
30
+
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qbxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Skryl
8
+ - Jason Barnabe
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-01-16 00:00:00.000000000 Z
12
+ date: 2015-04-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: activesupport
@@ -52,19 +53,90 @@ dependencies:
52
53
  - - "~>"
53
54
  - !ruby/object:Gem::Version
54
55
  version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: pry-nav
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: simplecov
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rake
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
55
126
  description: Quickbooks XML Parser
56
127
  email:
57
128
  - rut216@gmail.com
129
+ - jason.barnabe@gmail.com
58
130
  executables: []
59
131
  extensions: []
60
132
  extra_rdoc_files: []
61
133
  files:
62
134
  - ".gitignore"
135
+ - ".travis.yml"
63
136
  - Gemfile
64
137
  - LICENSE.txt
65
138
  - README.md
66
139
  - Rakefile
67
- - TODO.md
68
140
  - lib/qbxml.rb
69
141
  - lib/qbxml/hash.rb
70
142
  - lib/qbxml/qbxml.rb
@@ -106,6 +178,9 @@ files:
106
178
  - spec/support/responses/purchase_order_query_rs.xml
107
179
  - spec/support/responses/receive_payment_query_rs.xml
108
180
  - spec/support/responses/sales_receipt_add_rs.xml
181
+ - test/unit/hash_to_xml_test.rb
182
+ - test/unit/version_test.rb
183
+ - test/unit/xml_to_hash_test.rb
109
184
  homepage: ''
110
185
  licenses: []
111
186
  metadata: {}
@@ -125,7 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
200
  version: '0'
126
201
  requirements: []
127
202
  rubyforge_project:
128
- rubygems_version: 2.4.2
203
+ rubygems_version: 2.4.6
129
204
  signing_key:
130
205
  specification_version: 4
131
206
  summary: Quickbooks XML Parser and Validation Tool
@@ -151,3 +226,6 @@ test_files:
151
226
  - spec/support/responses/purchase_order_query_rs.xml
152
227
  - spec/support/responses/receive_payment_query_rs.xml
153
228
  - spec/support/responses/sales_receipt_add_rs.xml
229
+ - test/unit/hash_to_xml_test.rb
230
+ - test/unit/version_test.rb
231
+ - test/unit/xml_to_hash_test.rb
data/TODO.md DELETED
@@ -1,3 +0,0 @@
1
- stupid backwards compatibility
2
- - rename :xml_attributes to __attributes__
3
- - remove empty xml_attributes hashes