xero-ruby 18.2.0 → 18.4.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/xero-ruby/api/account_data_api.rb +419 -0
  3. data/lib/xero-ruby/api/payroll_nz_api.rb +2 -0
  4. data/lib/xero-ruby/api_client.rb +10 -0
  5. data/lib/xero-ruby/configuration.rb +2 -0
  6. data/lib/xero-ruby/models/account_data/account.rb +351 -0
  7. data/lib/xero-ruby/models/account_data/account_balance.rb +287 -0
  8. data/lib/xero-ruby/models/account_data/account_balances_response.rb +229 -0
  9. data/lib/xero-ruby/models/account_data/account_movement.rb +347 -0
  10. data/lib/xero-ruby/models/account_data/account_movements_response.rb +229 -0
  11. data/lib/xero-ruby/models/account_data/account_transaction_line.rb +359 -0
  12. data/lib/xero-ruby/models/account_data/account_transactions_response.rb +269 -0
  13. data/lib/xero-ruby/models/account_data/account_transactions_response_pagination.rb +243 -0
  14. data/lib/xero-ruby/models/account_data/accounting_date_range.rb +242 -0
  15. data/lib/xero-ruby/models/account_data/field_problem.rb +273 -0
  16. data/lib/xero-ruby/models/account_data/gateway_problem.rb +285 -0
  17. data/lib/xero-ruby/models/account_data/ledger_decimal.rb +278 -0
  18. data/lib/xero-ruby/models/account_data/line_source.rb +273 -0
  19. data/lib/xero-ruby/models/account_data/line_tax_rate.rb +243 -0
  20. data/lib/xero-ruby/models/account_data/problem_details.rb +273 -0
  21. data/lib/xero-ruby/models/account_data/tracking_item_details.rb +257 -0
  22. data/lib/xero-ruby/models/account_data/transaction_account.rb +366 -0
  23. data/lib/xero-ruby/models/account_data/validation_problem.rb +275 -0
  24. data/lib/xero-ruby/models/accounting/account.rb +11 -1
  25. data/lib/xero-ruby/models/payroll_nz/employee_tax.rb +44 -6
  26. data/lib/xero-ruby/version.rb +2 -2
  27. data/lib/xero-ruby.rb +19 -0
  28. metadata +385 -366
@@ -0,0 +1,351 @@
1
+ =begin
2
+ #Xero Account Data API (Beta)
3
+
4
+ #**Beta:** this API is in beta. Endpoints, request parameters and response shapes may change before general availability. The Account Data API provides read access to general ledger account data for a Xero organisation: account balances as at a date, account movements over a date range, and the individual account transactions behind them.
5
+
6
+ Contact: api@xero.com
7
+ Generated by: https://openapi-generator.tech
8
+ OpenAPI Generator version: 4.3.1
9
+
10
+ =end
11
+
12
+ require 'time'
13
+ require 'date'
14
+
15
+ module XeroRuby::AccountData
16
+ require 'bigdecimal'
17
+
18
+ class Account
19
+
20
+ attr_accessor :code
21
+
22
+
23
+ attr_accessor :name
24
+
25
+
26
+ attr_accessor :_class
27
+ ASSET ||= "ASSET".freeze
28
+ LIABILITY ||= "LIABILITY".freeze
29
+ EQUITY ||= "EQUITY".freeze
30
+ REVENUE ||= "REVENUE".freeze
31
+ EXPENSE ||= "EXPENSE".freeze
32
+
33
+ # Account type (e.g. `BANK`, `CURRENT`). Reported as `INVENTORY` for inventory asset accounts, regardless of the account's underlying type.
34
+ attr_accessor :type
35
+
36
+
37
+ attr_accessor :status
38
+ ACTIVE ||= "ACTIVE".freeze
39
+ ARCHIVED ||= "ARCHIVED".freeze
40
+ DELETED ||= "DELETED".freeze
41
+
42
+ # System account code, or `NONE` if the account has no system account code.
43
+ attr_accessor :system_account_code
44
+
45
+ class EnumAttributeValidator
46
+ attr_reader :datatype
47
+ attr_reader :allowable_values
48
+
49
+ def initialize(datatype, allowable_values)
50
+ @allowable_values = allowable_values.map do |value|
51
+ case datatype.to_s
52
+ when /Integer/i
53
+ value.to_i
54
+ when /Float/i
55
+ value.to_f
56
+ else
57
+ value
58
+ end
59
+ end
60
+ end
61
+
62
+ def valid?(value)
63
+ !value || allowable_values.include?(value)
64
+ end
65
+ end
66
+
67
+ # Attribute mapping from ruby-style variable name to JSON key.
68
+ def self.attribute_map
69
+ {
70
+ :'code' => :'code',
71
+ :'name' => :'name',
72
+ :'_class' => :'class',
73
+ :'type' => :'type',
74
+ :'status' => :'status',
75
+ :'system_account_code' => :'systemAccountCode'
76
+ }
77
+ end
78
+
79
+ # Attribute type mapping.
80
+ def self.openapi_types
81
+ {
82
+ :'code' => :'String',
83
+ :'name' => :'String',
84
+ :'_class' => :'String',
85
+ :'type' => :'String',
86
+ :'status' => :'String',
87
+ :'system_account_code' => :'String'
88
+ }
89
+ end
90
+
91
+ # Initializes the object
92
+ # @param [Hash] attributes Model attributes in the form of hash
93
+ def initialize(attributes = {})
94
+ if (!attributes.is_a?(Hash))
95
+ fail ArgumentError, "The input argument (attributes) must be a hash in `XeroRuby::AccountData::Account` initialize method"
96
+ end
97
+
98
+ # check to see if the attribute exists and convert string to symbol for hash key
99
+ attributes = attributes.each_with_object({}) { |(k, v), h|
100
+ if (!self.class.attribute_map.key?(k.to_sym))
101
+ fail ArgumentError, "`#{k}` is not a valid attribute in `XeroRuby::AccountData::Account`. Please check the name to make sure it's valid. List of attributes: " + self.class.attribute_map.keys.inspect
102
+ end
103
+ h[k.to_sym] = v
104
+ }
105
+
106
+ if attributes.key?(:'code')
107
+ self.code = attributes[:'code']
108
+ end
109
+
110
+ if attributes.key?(:'name')
111
+ self.name = attributes[:'name']
112
+ end
113
+
114
+ if attributes.key?(:'_class')
115
+ self._class = attributes[:'_class']
116
+ end
117
+
118
+ if attributes.key?(:'type')
119
+ self.type = attributes[:'type']
120
+ end
121
+
122
+ if attributes.key?(:'status')
123
+ self.status = attributes[:'status']
124
+ end
125
+
126
+ if attributes.key?(:'system_account_code')
127
+ self.system_account_code = attributes[:'system_account_code']
128
+ end
129
+ end
130
+
131
+ # Show invalid properties with the reasons. Usually used together with valid?
132
+ # @return Array for valid properties with the reasons
133
+ def list_invalid_properties
134
+ invalid_properties = Array.new
135
+ if @name.nil?
136
+ invalid_properties.push('invalid value for "name", name cannot be nil.')
137
+ end
138
+
139
+ if @_class.nil?
140
+ invalid_properties.push('invalid value for "_class", _class cannot be nil.')
141
+ end
142
+
143
+ if @type.nil?
144
+ invalid_properties.push('invalid value for "type", type cannot be nil.')
145
+ end
146
+
147
+ if @status.nil?
148
+ invalid_properties.push('invalid value for "status", status cannot be nil.')
149
+ end
150
+
151
+ if @system_account_code.nil?
152
+ invalid_properties.push('invalid value for "system_account_code", system_account_code cannot be nil.')
153
+ end
154
+
155
+ invalid_properties
156
+ end
157
+
158
+ # Check to see if the all the properties in the model are valid
159
+ # @return true if the model is valid
160
+ def valid?
161
+ return false if @name.nil?
162
+ return false if @_class.nil?
163
+ _class_validator = EnumAttributeValidator.new('String', ["ASSET", "LIABILITY", "EQUITY", "REVENUE", "EXPENSE"])
164
+ return false unless _class_validator.valid?(@_class)
165
+ return false if @type.nil?
166
+ return false if @status.nil?
167
+ status_validator = EnumAttributeValidator.new('String', ["ACTIVE", "ARCHIVED", "DELETED"])
168
+ return false unless status_validator.valid?(@status)
169
+ return false if @system_account_code.nil?
170
+ true
171
+ end
172
+
173
+ # Custom attribute writer method checking allowed values (enum).
174
+ # @param [Object] _class Object to be assigned
175
+ def _class=(_class)
176
+ validator = EnumAttributeValidator.new('String', ["ASSET", "LIABILITY", "EQUITY", "REVENUE", "EXPENSE"])
177
+ unless validator.valid?(_class)
178
+ fail ArgumentError, "invalid value for \"_class\", must be one of #{validator.allowable_values}."
179
+ end
180
+ @_class = _class
181
+ end
182
+
183
+ # Custom attribute writer method checking allowed values (enum).
184
+ # @param [Object] status Object to be assigned
185
+ def status=(status)
186
+ validator = EnumAttributeValidator.new('String', ["ACTIVE", "ARCHIVED", "DELETED"])
187
+ unless validator.valid?(status)
188
+ fail ArgumentError, "invalid value for \"status\", must be one of #{validator.allowable_values}."
189
+ end
190
+ @status = status
191
+ end
192
+
193
+ # Checks equality by comparing each attribute.
194
+ # @param [Object] Object to be compared
195
+ def ==(o)
196
+ return true if self.equal?(o)
197
+ self.class == o.class &&
198
+ code == o.code &&
199
+ name == o.name &&
200
+ _class == o._class &&
201
+ type == o.type &&
202
+ status == o.status &&
203
+ system_account_code == o.system_account_code
204
+ end
205
+
206
+ # @see the `==` method
207
+ # @param [Object] Object to be compared
208
+ def eql?(o)
209
+ self == o
210
+ end
211
+
212
+ # Calculates hash code according to all attributes.
213
+ # @return [Integer] Hash code
214
+ def hash
215
+ [code, name, _class, type, status, system_account_code].hash
216
+ end
217
+
218
+ # Builds the object from hash
219
+ # @param [Hash] attributes Model attributes in the form of hash
220
+ # @return [Object] Returns the model itself
221
+ def self.build_from_hash(attributes)
222
+ new.build_from_hash(attributes)
223
+ end
224
+
225
+ # Builds the object from hash
226
+ # @param [Hash] attributes Model attributes in the form of hash
227
+ # @return [Object] Returns the model itself
228
+ def build_from_hash(attributes)
229
+ return nil unless attributes.is_a?(Hash)
230
+ self.class.openapi_types.each_pair do |key, type|
231
+ if type =~ /\AArray<(.*)>/i
232
+ # check to ensure the input is an array given that the attribute
233
+ # is documented as an array but the input is not
234
+ if attributes[self.class.attribute_map[key]].is_a?(Array)
235
+ self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) })
236
+ end
237
+ elsif !attributes[self.class.attribute_map[key]].nil?
238
+ self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
239
+ end # or else data not found in attributes(hash), not an issue as the data can be optional
240
+ end
241
+
242
+ self
243
+ end
244
+
245
+ # Deserializes the data based on type
246
+ # @param string type Data type
247
+ # @param string value Value to be deserialized
248
+ # @return [Object] Deserialized data
249
+ def _deserialize(type, value)
250
+ case type.to_sym
251
+ when :DateTime
252
+ DateTime.parse(parse_date(value))
253
+ when :Date
254
+ Date.parse(parse_date(value))
255
+ when :String
256
+ value.to_s
257
+ when :Integer
258
+ value.to_i
259
+ when :Float
260
+ value.to_f
261
+ when :BigDecimal
262
+ BigDecimal(value.to_s)
263
+ when :Boolean
264
+ if value.to_s =~ /\A(true|t|yes|y|1)\z/i
265
+ true
266
+ else
267
+ false
268
+ end
269
+ when :Object
270
+ # generic object (usually a Hash), return directly
271
+ value
272
+ when /\AArray<(?<inner_type>.+)>\z/
273
+ inner_type = Regexp.last_match[:inner_type]
274
+ value.map { |v| _deserialize(inner_type, v) }
275
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
276
+ k_type = Regexp.last_match[:k_type]
277
+ v_type = Regexp.last_match[:v_type]
278
+ {}.tap do |hash|
279
+ value.each do |k, v|
280
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
281
+ end
282
+ end
283
+ else # model
284
+ XeroRuby::AccountData.const_get(type).build_from_hash(value)
285
+ end
286
+ end
287
+
288
+ # Returns the string representation of the object
289
+ # @return [String] String presentation of the object
290
+ def to_s
291
+ to_hash.to_s
292
+ end
293
+
294
+ # to_body is an alias to to_hash (backward compatibility)
295
+ # @return [Hash] Returns the object in the form of hash
296
+ def to_body
297
+ to_hash
298
+ end
299
+
300
+ # Returns the object in the form of hash
301
+ # @return [Hash] Returns the object in the form of hash
302
+ def to_hash(downcase: false)
303
+ hash = {}
304
+ self.class.attribute_map.each_pair do |attr, param|
305
+ value = self.send(attr)
306
+ next if value.nil?
307
+ key = downcase ? attr : param
308
+ hash[key] = _to_hash(value, downcase: downcase)
309
+ end
310
+ hash
311
+ end
312
+
313
+ # Returns the object in the form of hash with snake_case
314
+ def to_attributes
315
+ to_hash(downcase: true)
316
+ end
317
+
318
+ # Outputs non-array value in the form of hash
319
+ # For object, use to_hash. Otherwise, just return the value
320
+ # @param [Object] value Any valid value
321
+ # @return [Hash] Returns the value in the form of hash
322
+ def _to_hash(value, downcase: false)
323
+ if value.is_a?(Array)
324
+ value.map do |v|
325
+ v.to_hash(downcase: downcase)
326
+ end
327
+ elsif value.is_a?(Hash)
328
+ {}.tap do |hash|
329
+ value.map { |k, v| hash[k] = _to_hash(v, downcase: downcase) }
330
+ end
331
+ elsif value.respond_to? :to_hash
332
+ value.to_hash(downcase: downcase)
333
+ else
334
+ value
335
+ end
336
+ end
337
+
338
+ def parse_date(datestring)
339
+ if datestring.include?('Date')
340
+ date_pattern = /\/Date\((-?\d+)(\+\d+)?\)\//
341
+ original, date, timezone = *date_pattern.match(datestring)
342
+ date = (date.to_i / 1000)
343
+ Time.at(date).utc.strftime('%Y-%m-%dT%H:%M:%S%z').to_s
344
+ elsif /(\d\d\d\d)-(\d\d)/.match(datestring) # handles dates w/out Days: YYYY-MM*-DD
345
+ Time.parse(datestring + '-01').strftime('%Y-%m-%dT%H:%M:%S').to_s
346
+ else # handle date 'types' for small subset of payroll API's
347
+ Time.parse(datestring).strftime('%Y-%m-%dT%H:%M:%S').to_s
348
+ end
349
+ end
350
+ end
351
+ end
@@ -0,0 +1,287 @@
1
+ =begin
2
+ #Xero Account Data API (Beta)
3
+
4
+ #**Beta:** this API is in beta. Endpoints, request parameters and response shapes may change before general availability. The Account Data API provides read access to general ledger account data for a Xero organisation: account balances as at a date, account movements over a date range, and the individual account transactions behind them.
5
+
6
+ Contact: api@xero.com
7
+ Generated by: https://openapi-generator.tech
8
+ OpenAPI Generator version: 4.3.1
9
+
10
+ =end
11
+
12
+ require 'time'
13
+ require 'date'
14
+
15
+ module XeroRuby::AccountData
16
+ require 'bigdecimal'
17
+
18
+ class AccountBalance
19
+ # The account this balance is for.
20
+ attr_accessor :account_id
21
+
22
+ # The date this balance applies to (YYYY-MM-DD).
23
+ attr_accessor :date
24
+
25
+
26
+ attr_accessor :balance
27
+
28
+ # Balance as a signed number (positive for debit, negative for credit).
29
+ attr_accessor :signed_balance
30
+
31
+
32
+ attr_accessor :account
33
+
34
+ # Attribute mapping from ruby-style variable name to JSON key.
35
+ def self.attribute_map
36
+ {
37
+ :'account_id' => :'accountId',
38
+ :'date' => :'date',
39
+ :'balance' => :'balance',
40
+ :'signed_balance' => :'signedBalance',
41
+ :'account' => :'account'
42
+ }
43
+ end
44
+
45
+ # Attribute type mapping.
46
+ def self.openapi_types
47
+ {
48
+ :'account_id' => :'String',
49
+ :'date' => :'Date',
50
+ :'balance' => :'LedgerDecimal',
51
+ :'signed_balance' => :'Float',
52
+ :'account' => :'Account'
53
+ }
54
+ end
55
+
56
+ # Initializes the object
57
+ # @param [Hash] attributes Model attributes in the form of hash
58
+ def initialize(attributes = {})
59
+ if (!attributes.is_a?(Hash))
60
+ fail ArgumentError, "The input argument (attributes) must be a hash in `XeroRuby::AccountData::AccountBalance` initialize method"
61
+ end
62
+
63
+ # check to see if the attribute exists and convert string to symbol for hash key
64
+ attributes = attributes.each_with_object({}) { |(k, v), h|
65
+ if (!self.class.attribute_map.key?(k.to_sym))
66
+ fail ArgumentError, "`#{k}` is not a valid attribute in `XeroRuby::AccountData::AccountBalance`. Please check the name to make sure it's valid. List of attributes: " + self.class.attribute_map.keys.inspect
67
+ end
68
+ h[k.to_sym] = v
69
+ }
70
+
71
+ if attributes.key?(:'account_id')
72
+ self.account_id = attributes[:'account_id']
73
+ end
74
+
75
+ if attributes.key?(:'date')
76
+ self.date = attributes[:'date']
77
+ end
78
+
79
+ if attributes.key?(:'balance')
80
+ self.balance = attributes[:'balance']
81
+ end
82
+
83
+ if attributes.key?(:'signed_balance')
84
+ self.signed_balance = attributes[:'signed_balance']
85
+ end
86
+
87
+ if attributes.key?(:'account')
88
+ self.account = attributes[:'account']
89
+ end
90
+ end
91
+
92
+ # Show invalid properties with the reasons. Usually used together with valid?
93
+ # @return Array for valid properties with the reasons
94
+ def list_invalid_properties
95
+ invalid_properties = Array.new
96
+ if @account_id.nil?
97
+ invalid_properties.push('invalid value for "account_id", account_id cannot be nil.')
98
+ end
99
+
100
+ if @date.nil?
101
+ invalid_properties.push('invalid value for "date", date cannot be nil.')
102
+ end
103
+
104
+ if @balance.nil?
105
+ invalid_properties.push('invalid value for "balance", balance cannot be nil.')
106
+ end
107
+
108
+ if @signed_balance.nil?
109
+ invalid_properties.push('invalid value for "signed_balance", signed_balance cannot be nil.')
110
+ end
111
+
112
+ if @account.nil?
113
+ invalid_properties.push('invalid value for "account", account cannot be nil.')
114
+ end
115
+
116
+ invalid_properties
117
+ end
118
+
119
+ # Check to see if the all the properties in the model are valid
120
+ # @return true if the model is valid
121
+ def valid?
122
+ return false if @account_id.nil?
123
+ return false if @date.nil?
124
+ return false if @balance.nil?
125
+ return false if @signed_balance.nil?
126
+ return false if @account.nil?
127
+ true
128
+ end
129
+
130
+ # Checks equality by comparing each attribute.
131
+ # @param [Object] Object to be compared
132
+ def ==(o)
133
+ return true if self.equal?(o)
134
+ self.class == o.class &&
135
+ account_id == o.account_id &&
136
+ date == o.date &&
137
+ balance == o.balance &&
138
+ signed_balance == o.signed_balance &&
139
+ account == o.account
140
+ end
141
+
142
+ # @see the `==` method
143
+ # @param [Object] Object to be compared
144
+ def eql?(o)
145
+ self == o
146
+ end
147
+
148
+ # Calculates hash code according to all attributes.
149
+ # @return [Integer] Hash code
150
+ def hash
151
+ [account_id, date, balance, signed_balance, account].hash
152
+ end
153
+
154
+ # Builds the object from hash
155
+ # @param [Hash] attributes Model attributes in the form of hash
156
+ # @return [Object] Returns the model itself
157
+ def self.build_from_hash(attributes)
158
+ new.build_from_hash(attributes)
159
+ end
160
+
161
+ # Builds the object from hash
162
+ # @param [Hash] attributes Model attributes in the form of hash
163
+ # @return [Object] Returns the model itself
164
+ def build_from_hash(attributes)
165
+ return nil unless attributes.is_a?(Hash)
166
+ self.class.openapi_types.each_pair do |key, type|
167
+ if type =~ /\AArray<(.*)>/i
168
+ # check to ensure the input is an array given that the attribute
169
+ # is documented as an array but the input is not
170
+ if attributes[self.class.attribute_map[key]].is_a?(Array)
171
+ self.send("#{key}=", attributes[self.class.attribute_map[key]].map { |v| _deserialize($1, v) })
172
+ end
173
+ elsif !attributes[self.class.attribute_map[key]].nil?
174
+ self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
175
+ end # or else data not found in attributes(hash), not an issue as the data can be optional
176
+ end
177
+
178
+ self
179
+ end
180
+
181
+ # Deserializes the data based on type
182
+ # @param string type Data type
183
+ # @param string value Value to be deserialized
184
+ # @return [Object] Deserialized data
185
+ def _deserialize(type, value)
186
+ case type.to_sym
187
+ when :DateTime
188
+ DateTime.parse(parse_date(value))
189
+ when :Date
190
+ Date.parse(parse_date(value))
191
+ when :String
192
+ value.to_s
193
+ when :Integer
194
+ value.to_i
195
+ when :Float
196
+ value.to_f
197
+ when :BigDecimal
198
+ BigDecimal(value.to_s)
199
+ when :Boolean
200
+ if value.to_s =~ /\A(true|t|yes|y|1)\z/i
201
+ true
202
+ else
203
+ false
204
+ end
205
+ when :Object
206
+ # generic object (usually a Hash), return directly
207
+ value
208
+ when /\AArray<(?<inner_type>.+)>\z/
209
+ inner_type = Regexp.last_match[:inner_type]
210
+ value.map { |v| _deserialize(inner_type, v) }
211
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
212
+ k_type = Regexp.last_match[:k_type]
213
+ v_type = Regexp.last_match[:v_type]
214
+ {}.tap do |hash|
215
+ value.each do |k, v|
216
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
217
+ end
218
+ end
219
+ else # model
220
+ XeroRuby::AccountData.const_get(type).build_from_hash(value)
221
+ end
222
+ end
223
+
224
+ # Returns the string representation of the object
225
+ # @return [String] String presentation of the object
226
+ def to_s
227
+ to_hash.to_s
228
+ end
229
+
230
+ # to_body is an alias to to_hash (backward compatibility)
231
+ # @return [Hash] Returns the object in the form of hash
232
+ def to_body
233
+ to_hash
234
+ end
235
+
236
+ # Returns the object in the form of hash
237
+ # @return [Hash] Returns the object in the form of hash
238
+ def to_hash(downcase: false)
239
+ hash = {}
240
+ self.class.attribute_map.each_pair do |attr, param|
241
+ value = self.send(attr)
242
+ next if value.nil?
243
+ key = downcase ? attr : param
244
+ hash[key] = _to_hash(value, downcase: downcase)
245
+ end
246
+ hash
247
+ end
248
+
249
+ # Returns the object in the form of hash with snake_case
250
+ def to_attributes
251
+ to_hash(downcase: true)
252
+ end
253
+
254
+ # Outputs non-array value in the form of hash
255
+ # For object, use to_hash. Otherwise, just return the value
256
+ # @param [Object] value Any valid value
257
+ # @return [Hash] Returns the value in the form of hash
258
+ def _to_hash(value, downcase: false)
259
+ if value.is_a?(Array)
260
+ value.map do |v|
261
+ v.to_hash(downcase: downcase)
262
+ end
263
+ elsif value.is_a?(Hash)
264
+ {}.tap do |hash|
265
+ value.map { |k, v| hash[k] = _to_hash(v, downcase: downcase) }
266
+ end
267
+ elsif value.respond_to? :to_hash
268
+ value.to_hash(downcase: downcase)
269
+ else
270
+ value
271
+ end
272
+ end
273
+
274
+ def parse_date(datestring)
275
+ if datestring.include?('Date')
276
+ date_pattern = /\/Date\((-?\d+)(\+\d+)?\)\//
277
+ original, date, timezone = *date_pattern.match(datestring)
278
+ date = (date.to_i / 1000)
279
+ Time.at(date).utc.strftime('%Y-%m-%dT%H:%M:%S%z').to_s
280
+ elsif /(\d\d\d\d)-(\d\d)/.match(datestring) # handles dates w/out Days: YYYY-MM*-DD
281
+ Time.parse(datestring + '-01').strftime('%Y-%m-%dT%H:%M:%S').to_s
282
+ else # handle date 'types' for small subset of payroll API's
283
+ Time.parse(datestring).strftime('%Y-%m-%dT%H:%M:%S').to_s
284
+ end
285
+ end
286
+ end
287
+ end