qbxml 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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