xeroizer 2.15.5 → 2.15.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.md +14 -3
  2. data/lib/xeroizer.rb +5 -0
  3. data/lib/xeroizer/generic_application.rb +15 -9
  4. data/lib/xeroizer/http.rb +44 -34
  5. data/lib/xeroizer/models/account.rb +15 -11
  6. data/lib/xeroizer/models/allocation.rb +13 -0
  7. data/lib/xeroizer/models/attachment.rb +93 -0
  8. data/lib/xeroizer/models/bank_transaction.rb +3 -2
  9. data/lib/xeroizer/models/contact.rb +20 -12
  10. data/lib/xeroizer/models/contact_person.rb +20 -0
  11. data/lib/xeroizer/models/credit_note.rb +2 -0
  12. data/lib/xeroizer/models/expense_claim.rb +29 -0
  13. data/lib/xeroizer/models/invoice.rb +42 -29
  14. data/lib/xeroizer/models/line_item.rb +1 -0
  15. data/lib/xeroizer/models/organisation.rb +6 -1
  16. data/lib/xeroizer/models/payment.rb +16 -11
  17. data/lib/xeroizer/models/receipt.rb +39 -0
  18. data/lib/xeroizer/models/tax_component.rb +13 -0
  19. data/lib/xeroizer/models/tax_rate.rb +14 -3
  20. data/lib/xeroizer/models/user.rb +26 -0
  21. data/lib/xeroizer/oauth.rb +3 -3
  22. data/lib/xeroizer/record/base.rb +40 -31
  23. data/lib/xeroizer/record/base_model.rb +31 -25
  24. data/lib/xeroizer/record/base_model_http_proxy.rb +4 -1
  25. data/lib/xeroizer/record/record_association_helper.rb +35 -35
  26. data/lib/xeroizer/record/xml_helper.rb +1 -1
  27. data/lib/xeroizer/report/factory.rb +2 -1
  28. data/lib/xeroizer/version.rb +3 -0
  29. data/test/stub_responses/organisation.xml +30 -0
  30. data/test/stub_responses/tax_rates.xml +81 -1
  31. data/test/stub_responses/users.xml +17 -0
  32. data/test/unit/generic_application_test.rb +21 -0
  33. data/test/unit/http_test.rb +18 -0
  34. data/test/unit/models/bank_transaction_test.rb +1 -4
  35. data/test/unit/models/line_item_sum_test.rb +3 -2
  36. data/test/unit/models/tax_rate_test.rb +81 -0
  37. data/test/unit/oauth_test.rb +4 -2
  38. data/test/unit/record/base_test.rb +1 -1
  39. data/test/unit/record/model_definition_test.rb +9 -3
  40. data/test/unit/record/record_association_test.rb +1 -1
  41. data/test/unit/record_definition_test.rb +1 -1
  42. metadata +540 -205
  43. data/.bundle/config +0 -2
  44. data/.gitattributes +0 -22
  45. data/Gemfile +0 -20
  46. data/Gemfile.lock +0 -59
  47. data/Rakefile +0 -55
  48. data/VERSION +0 -1
  49. data/xeroizer.gemspec +0 -409
@@ -0,0 +1,13 @@
1
+ module Xeroizer
2
+ module Record
3
+ class TaxComponentModel < BaseModel
4
+ set_permissions :read
5
+ end
6
+
7
+ class TaxComponent < Base
8
+ string :name
9
+ decimal :rate
10
+ boolean :is_compound
11
+ end
12
+ end
13
+ end
@@ -5,9 +5,19 @@ module Xeroizer
5
5
 
6
6
  set_permissions :read
7
7
 
8
+ # TaxRates can be created using either POST or PUT.
9
+ # POST will also silently update the tax, which can
10
+ # be unexpected. PUT is only for create.
11
+ def create_method
12
+ :http_put
13
+ end
8
14
  end
9
15
 
10
16
  class TaxRate < Base
17
+ set_primary_key :tax_type
18
+ set_possible_primary_keys :tax_type, :name
19
+
20
+ set_primary_key :name
11
21
 
12
22
  string :name
13
23
  string :tax_type
@@ -19,8 +29,9 @@ module Xeroizer
19
29
  boolean :can_apply_to_revenue
20
30
  decimal :display_tax_rate
21
31
  decimal :effective_rate
22
-
32
+
33
+ has_many :tax_components
23
34
  end
24
-
35
+
25
36
  end
26
- end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Xeroizer
2
+ module Record
3
+
4
+ class UserModel < BaseModel
5
+
6
+ set_api_controller_name 'User'
7
+ set_permissions :read
8
+
9
+ end
10
+
11
+ class User < Base
12
+
13
+ set_primary_key :user_id
14
+
15
+ guid :user_id
16
+ string :email_address
17
+ string :first_name
18
+ string :last_name
19
+ datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
20
+ boolean :is_subscriber
21
+ string :organisation_role
22
+
23
+ end
24
+
25
+ end
26
+ end
@@ -84,13 +84,13 @@ module Xeroizer
84
84
  #
85
85
  # @option params [String] :oauth_callback URL to redirect user to when they have authenticated your application with Xero. If not specified, the user will be shown an authorisation code on the screen that they need to get into your application.
86
86
  def request_token(params = {})
87
- consumer.get_request_token(params)
87
+ consumer.get_request_token(params, {}, @consumer_options[:default_headers])
88
88
  end
89
89
 
90
90
  # Create an AccessToken from a PUBLIC/PARTNER authorisation.
91
91
  def authorize_from_request(rtoken, rsecret, params = {})
92
92
  request_token = ::OAuth::RequestToken.new(consumer, rtoken, rsecret)
93
- access_token = request_token.get_access_token(params)
93
+ access_token = request_token.get_access_token(params, {}, @consumer_options[:default_headers])
94
94
  update_attributes_from_token(access_token)
95
95
  end
96
96
 
@@ -116,7 +116,7 @@ module Xeroizer
116
116
  access_token = old_token.get_access_token({
117
117
  :oauth_session_handle => (session_handle || @session_handle),
118
118
  :token => old_token
119
- })
119
+ }, {}, @consumer_options[:default_headers])
120
120
  update_attributes_from_token(access_token)
121
121
  end
122
122
 
@@ -6,60 +6,68 @@ require 'xeroizer/logging'
6
6
 
7
7
  module Xeroizer
8
8
  module Record
9
-
9
+
10
10
  class Base
11
-
11
+
12
12
  include ClassLevelInheritableAttributes
13
13
  class_inheritable_attributes :fields, :possible_primary_keys, :primary_key_name, :summary_only, :validators
14
-
14
+
15
15
  attr_reader :attributes
16
16
  attr_reader :parent
17
+ attr_reader :model
17
18
  attr_accessor :errors
18
19
  attr_accessor :complete_record_downloaded
19
-
20
+
20
21
  include ModelDefinitionHelper
21
22
  include RecordAssociationHelper
22
23
  include ValidationHelper
23
24
  include XmlHelper
24
-
25
+
25
26
  class << self
26
27
 
27
28
  # Build a record with attributes set to the value of attributes.
28
29
  def build(attributes, parent)
29
30
  record = new(parent)
30
31
  attributes.each do | key, value |
31
- record.send("#{key}=".to_sym, value)
32
+ attr = record.respond_to?("#{key}=") ? key : record.class.fields[key][:internal_name]
33
+ record.send("#{attr}=", value)
32
34
  end
33
35
  record
34
36
  end
35
-
37
+
36
38
  end
37
-
39
+
38
40
  public
39
-
41
+
40
42
  def initialize(parent)
41
43
  @parent = parent
44
+ @model = new_model_class(self.class.name.demodulize)
42
45
  @attributes = {}
43
46
  end
44
-
47
+
45
48
  def new_model_class(model_name)
46
- Xeroizer::Record.const_get("#{model_name}Model".to_sym).new(parent.application, model_name.to_s)
49
+ Xeroizer::Record.const_get("#{model_name}Model".to_sym).new(parent.try(:application), model_name.to_s)
47
50
  end
48
-
51
+
49
52
  def [](attribute)
50
53
  self.send(attribute)
51
54
  end
52
-
55
+
53
56
  def []=(attribute, value)
54
57
  parent.mark_dirty(self) if parent
55
58
  self.send("#{attribute}=".to_sym, value)
56
59
  end
57
60
 
61
+ def non_calculated_attributes
62
+ attributes.reject {|name| self.class.fields[name][:calculated] }
63
+ end
64
+
58
65
  def attributes=(new_attributes)
59
66
  return unless new_attributes.is_a?(Hash)
60
67
  parent.mark_dirty(self) if parent
61
68
  new_attributes.each do | key, value |
62
- self.send("#{key}=".to_sym, value)
69
+ attr = respond_to?("#{key}=") ? key : self.class.fields[key][:internal_name]
70
+ self.send("#{attr}=", value)
63
71
  end
64
72
  end
65
73
 
@@ -67,11 +75,11 @@ module Xeroizer
67
75
  self.attributes = attributes
68
76
  save
69
77
  end
70
-
78
+
71
79
  def new_record?
72
80
  id.nil?
73
81
  end
74
-
82
+
75
83
  # Check to see if the complete record is downloaded.
76
84
  def complete_record_downloaded?
77
85
  if !!self.class.list_contains_summary_only?
@@ -80,7 +88,7 @@ module Xeroizer
80
88
  true
81
89
  end
82
90
  end
83
-
91
+
84
92
  # Downloads the complete record if we only have a summary of the record.
85
93
  def download_complete_record!
86
94
  record = self.parent.find(self.id)
@@ -89,7 +97,7 @@ module Xeroizer
89
97
  parent.mark_clean(self)
90
98
  self
91
99
  end
92
-
100
+
93
101
  def save
94
102
  return false unless valid?
95
103
  if new_record?
@@ -105,7 +113,7 @@ module Xeroizer
105
113
  parent.mark_clean(self)
106
114
  true
107
115
  end
108
-
116
+
109
117
  def to_json(*args)
110
118
  to_h.to_json(*args)
111
119
  end
@@ -128,37 +136,38 @@ module Xeroizer
128
136
  end.join(", ")
129
137
  "#<#{self.class} #{attribute_string}>"
130
138
  end
131
-
139
+
132
140
  protected
133
-
141
+
134
142
  # Attempt to create a new record.
135
143
  def create
136
144
  request = to_xml
137
145
  log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}"
138
-
139
- response = parent.http_post(request)
146
+
147
+ response = parent.send(parent.create_method, request)
148
+
140
149
  log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}"
141
-
150
+
142
151
  parse_save_response(response)
143
152
  end
144
-
153
+
145
154
  # Attempt to update an existing record.
146
155
  def update
147
156
  if self.class.possible_primary_keys && self.class.possible_primary_keys.all? { | possible_key | self[possible_key].nil? }
148
157
  raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys)
149
158
  end
150
-
159
+
151
160
  request = to_xml
152
-
161
+
153
162
  log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}"
154
-
163
+
155
164
  response = parent.http_post(request)
156
165
 
157
166
  log "[UPDATE RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}"
158
167
 
159
168
  parse_save_response(response)
160
169
  end
161
-
170
+
162
171
  # Parse the response from a create/update request.
163
172
  def parse_save_response(response_xml)
164
173
  response = parent.parse_response(response_xml)
@@ -172,8 +181,8 @@ module Xeroizer
172
181
  def log(what)
173
182
  Xeroizer::Logging::Log.info what
174
183
  end
175
-
184
+
176
185
  end
177
-
186
+
178
187
  end
179
188
  end
@@ -2,19 +2,19 @@ require 'xeroizer/record/base_model_http_proxy'
2
2
 
3
3
  module Xeroizer
4
4
  module Record
5
-
5
+
6
6
  class BaseModel
7
7
 
8
8
  include ClassLevelInheritableAttributes
9
9
  class_inheritable_attributes :api_controller_name
10
-
10
+
11
11
  module InvaidPermissionError; end
12
12
  class InvalidPermissionError < StandardError
13
13
  include InvaidPermissionError
14
14
  end
15
15
  ALLOWED_PERMISSIONS = [:read, :write, :update]
16
16
  class_inheritable_attributes :permissions
17
-
17
+
18
18
  class_inheritable_attributes :xml_root_name
19
19
  class_inheritable_attributes :optional_xml_root_name
20
20
  class_inheritable_attributes :xml_node_name
@@ -27,18 +27,18 @@ module Xeroizer
27
27
  attr_reader :model_name
28
28
  attr :model_class
29
29
  attr_reader :response
30
-
30
+
31
31
  class << self
32
-
33
- # Method to allow override of the default controller name used
34
- # in the API URLs.
32
+
33
+ # Method to allow override of the default controller name used
34
+ # in the API URLs.
35
35
  #
36
36
  # Default: pluaralized model name (e.g. if the controller name is
37
37
  # Invoice then the default is Invoices.
38
38
  def set_api_controller_name(controller_name)
39
39
  self.api_controller_name = controller_name
40
40
  end
41
-
41
+
42
42
  # Set the permissions allowed for this class type.
43
43
  # There are no permissions set by default.
44
44
  # Valid permissions are :read, :write, :update.
@@ -49,34 +49,34 @@ module Xeroizer
49
49
  self.permissions[permission] = true
50
50
  end
51
51
  end
52
-
52
+
53
53
  # Method to allow override of the default XML node name.
54
54
  #
55
55
  # Default: singularized model name in camel-case.
56
56
  def set_xml_node_name(node_name)
57
57
  self.xml_node_name = node_name
58
58
  end
59
-
59
+
60
60
  # Method to allow override of the default XML root name to use
61
61
  # in has_many associations.
62
62
  def set_xml_root_name(root_name)
63
63
  self.xml_root_name = root_name
64
64
  end
65
-
65
+
66
66
  # Method to add an extra top-level node to use in has_many associations.
67
67
  def set_optional_xml_root_name(optional_root_name)
68
- self.optional_root_name = optional_root_name
68
+ self.optional_xml_root_name = optional_root_name
69
69
  end
70
-
70
+
71
71
  end
72
-
72
+
73
73
  public
74
-
74
+
75
75
  def initialize(application, model_name)
76
76
  @application = application
77
77
  @model_name = model_name
78
78
  end
79
-
79
+
80
80
  # Retrieve the controller name.
81
81
  #
82
82
  # Default: pluaralized model name (e.g. if the controller name is
@@ -84,11 +84,11 @@ module Xeroizer
84
84
  def api_controller_name
85
85
  self.class.api_controller_name || model_name.pluralize
86
86
  end
87
-
87
+
88
88
  def model_class
89
89
  @model_class ||= Xeroizer::Record.const_get(model_name.to_sym)
90
90
  end
91
-
91
+
92
92
  # Build a record with attributes set to the value of attributes.
93
93
  def build(attributes = {})
94
94
  model_class.build(attributes, self).tap do |resource|
@@ -113,15 +113,15 @@ module Xeroizer
113
113
  def create(attributes = {})
114
114
  build(attributes).tap { |resource| resource.save }
115
115
  end
116
-
117
- # Retreive full record list for this model.
116
+
117
+ # Retreive full record list for this model.
118
118
  def all(options = {})
119
119
  raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
120
120
  response_xml = http_get(parse_params(options))
121
121
  response = parse_response(response_xml, options)
122
122
  response.response_items || []
123
123
  end
124
-
124
+
125
125
  # Helper method to retrieve just the first element from
126
126
  # the full record list.
127
127
  def first(options = {})
@@ -129,7 +129,7 @@ module Xeroizer
129
129
  result = all(options)
130
130
  result.first if result.is_a?(Array)
131
131
  end
132
-
132
+
133
133
  # Retrieve record matching the passed in ID.
134
134
  def find(id, options = {})
135
135
  raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
@@ -141,6 +141,7 @@ module Xeroizer
141
141
  end
142
142
 
143
143
  def batch_save(chunk_size = DEFAULT_RECORDS_PER_BATCH_SAVE)
144
+ no_errors = true
144
145
  @objects = {}
145
146
  @allow_batch_operations = true
146
147
 
@@ -149,15 +150,16 @@ module Xeroizer
149
150
  if @objects[model_class]
150
151
  objects = @objects[model_class].values.compact
151
152
  return false unless objects.all?(&:valid?)
152
- actions = objects.group_by {|o| o.new_record? ? :http_put : :http_post }
153
+ actions = objects.group_by {|o| o.new_record? ? create_method : :http_post }
153
154
  actions.each_pair do |http_method, records|
154
155
  records.each_slice(chunk_size) do |some_records|
155
156
  request = to_bulk_xml(some_records)
156
157
  response = parse_response(self.send(http_method, request, {:summarizeErrors => false}))
157
158
  response.response_items.each_with_index do |record, i|
158
159
  if record and record.is_a?(model_class)
159
- some_records[i].attributes = record.attributes
160
+ some_records[i].attributes = record.non_calculated_attributes
160
161
  some_records[i].errors = record.errors
162
+ no_errors = record.errors.nil? || record.errors.empty? if no_errors
161
163
  some_records[i].saved!
162
164
  end
163
165
  end
@@ -167,7 +169,7 @@ module Xeroizer
167
169
 
168
170
  @objects = {}
169
171
  @allow_batch_operations = false
170
- true
172
+ no_errors
171
173
  end
172
174
 
173
175
  def parse_response(response_xml, options = {})
@@ -179,6 +181,10 @@ module Xeroizer
179
181
  end
180
182
  end
181
183
 
184
+ def create_method
185
+ :http_put
186
+ end
187
+
182
188
  protected
183
189
 
184
190
  # Parse the records part of the XML response and builds model instances as necessary.