xeroizer 2.15.9 → 2.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/README.md +59 -1
  2. data/lib/xeroizer/exceptions.rb +14 -11
  3. data/lib/xeroizer/generic_application.rb +8 -1
  4. data/lib/xeroizer/http.rb +19 -10
  5. data/lib/xeroizer/models/attachment.rb +15 -13
  6. data/lib/xeroizer/models/bank_transaction.rb +3 -1
  7. data/lib/xeroizer/models/branding_theme.rb +1 -1
  8. data/lib/xeroizer/models/contact.rb +3 -1
  9. data/lib/xeroizer/models/credit_note.rb +26 -2
  10. data/lib/xeroizer/models/employee.rb +49 -4
  11. data/lib/xeroizer/models/invoice.rb +1 -1
  12. data/lib/xeroizer/models/journal.rb +3 -1
  13. data/lib/xeroizer/models/journal_line.rb +1 -0
  14. data/lib/xeroizer/models/journal_line_tracking_category.rb +2 -1
  15. data/lib/xeroizer/models/line_item.rb +1 -0
  16. data/lib/xeroizer/models/manual_journal.rb +1 -1
  17. data/lib/xeroizer/models/organisation.rb +41 -1
  18. data/lib/xeroizer/models/phone.rb +2 -1
  19. data/lib/xeroizer/models/prepayment.rb +39 -0
  20. data/lib/xeroizer/models/repeating_invoice.rb +80 -0
  21. data/lib/xeroizer/models/schedule.rb +33 -0
  22. data/lib/xeroizer/record/base.rb +6 -0
  23. data/lib/xeroizer/record/base_model.rb +41 -26
  24. data/lib/xeroizer/record/record_association_helper.rb +1 -1
  25. data/lib/xeroizer/record/validation_helper.rb +6 -2
  26. data/lib/xeroizer/record/validators/length_of_validator.rb +23 -0
  27. data/lib/xeroizer/record/xml_helper.rb +10 -2
  28. data/lib/xeroizer/version.rb +1 -1
  29. data/lib/xeroizer.rb +3 -0
  30. data/test/stub_responses/prepayments.xml +27 -0
  31. data/test/stub_responses/records/prepayment-7d3619b1-82cc-405b-8f44-9d4f9a787a8a.xml +92 -0
  32. data/test/stub_responses/records/repeating_invoice-ad3550bc-1ae0-45c0-a782-48c6d2061127.xml +43 -0
  33. data/test/stub_responses/repeating_invoices.xml +43 -0
  34. data/test/unit/models/contact_test.rb +1 -1
  35. data/test/unit/models/employee_test.rb +43 -0
  36. data/test/unit/models/invoice_test.rb +21 -1
  37. data/test/unit/models/journal_test.rb +44 -0
  38. data/test/unit/models/manual_journal_test.rb +25 -0
  39. data/test/unit/models/organisation_test.rb +38 -0
  40. data/test/unit/models/phone_test.rb +31 -0
  41. data/test/unit/models/prepayment_test.rb +21 -0
  42. data/test/unit/models/repeating_invoice_test.rb +36 -0
  43. data/test/unit/record/base_test.rb +4 -2
  44. metadata +27 -1
data/README.md CHANGED
@@ -183,7 +183,7 @@ Partner applications are only in beta testing via the Xero API and you will need
183
183
  get permission to create a partner application and for them to send you information on obtaining your client-side SSL
184
184
  certificate.
185
185
 
186
- Ruby's OpenSSL library rqeuires the certificate and private key to be extracted from the `entrust-client.p12` file
186
+ Ruby's OpenSSL library requires the certificate and private key to be extracted from the `entrust-client.p12` file
187
187
  downloaded via Xero's instructions. To extract:
188
188
 
189
189
  openssl pkcs12 -in entrust-client.p12 -clcerts -nokeys -out entrust-cert.pem
@@ -391,6 +391,36 @@ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
391
391
  puts "Invoice Contact Name: #{invoice.contact.name}"
392
392
  ```
393
393
 
394
+ Attachments
395
+ ------------
396
+ Files or raw data can be attached to record types
397
+ **attach\_data examples:**
398
+ ```ruby
399
+ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
400
+ invoice.attach_data("example.txt", "This is raw data", "txt")
401
+ ```
402
+
403
+ ```ruby
404
+ attach_data('cd09aa49-134d-40fb-a52b-b63c6a91d712', "example.txt", "This is raw data", "txt")
405
+ ```
406
+
407
+ **attach\_file examples:**
408
+ ```ruby
409
+ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
410
+ invoice.attach_file("example.png", "/path/to/image.png", "image/png")
411
+ ```
412
+
413
+ ```ruby
414
+ attach_file('cd09aa49-134d-40fb-a52b-b63c6a91d712', "example.png", "/path/to/image.png", "image/png")
415
+ ```
416
+
417
+ **include with online invoice**
418
+ To include an attachment with an invoice set include_online parameter to true within the options hash
419
+ ```ruby
420
+ invoice = xero.Invoice.find('cd09aa49-134d-40fb-a52b-b63c6a91d712')
421
+ invoice.attach_file("example.png", "/path/to/image.png", "image/png", { include_online: true })
422
+ ```
423
+
394
424
  Creating/Updating Data
395
425
  ----------------------
396
426
 
@@ -456,6 +486,15 @@ unsaved records aren't valid, it'll return `false` before sending anything acros
456
486
  otherwise, it returns `true`. `batch_save` takes one optional argument: the number of records to
457
487
  create/update per request. (Defaults to 2,000.)
458
488
 
489
+ If you'd rather build and send the records manually, there's a `save_records` method:
490
+ ```ruby
491
+ contact1 = xero.Contact.build(some_attributes)
492
+ contact2 = xero.Contact.build(some_other_attributes)
493
+ contact3 = xero.Contact.build(some_more_attributes)
494
+ xero.Contact.save_records([contact1, contact2, contact3])
495
+ ```
496
+ It has the same return values as `batch_save`.
497
+
459
498
  ### Errors
460
499
 
461
500
  If a record doesn't match its internal validation requirements, the `#save` method will return
@@ -561,6 +600,25 @@ client = Xeroizer::PublicApplication.new(YOUR_OAUTH_CONSUMER_KEY,
561
600
  :logger => XeroLogger)
562
601
  ```
563
602
 
603
+ HTTP Callbacks
604
+ --------------------
605
+
606
+ You can provide "before" and "after" callbacks which will be invoked every
607
+ time Xeroizer makes an HTTP request, which is potentially useful for both
608
+ throttling and logging:
609
+
610
+ ```ruby
611
+ Xeroizer::PublicApplication.new(
612
+ credentials[:key], credentials[:secret],
613
+ before_request: ->(request) { puts "Hitting this URL: #{request.url}" },
614
+ after_request: ->(request, response) { puts "Got this response: #{response.code}" }
615
+ )
616
+ ```
617
+
618
+ The `request` parameter is a custom Struct with `url`, `headers`, `body`,
619
+ and `params` methods. The `response` parameter is a Net::HTTPResponse object.
620
+
621
+
564
622
  Unit Price Precision
565
623
  --------------------
566
624
 
@@ -13,7 +13,10 @@
13
13
  # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
14
 
15
15
  module Xeroizer
16
- class ApiException < StandardError
16
+
17
+ class XeroizerError < StandardError; end
18
+
19
+ class ApiException < XeroizerError
17
20
 
18
21
  attr_reader :type, :message, :xml, :parsed_xml, :request_body
19
22
 
@@ -41,7 +44,7 @@ module Xeroizer
41
44
 
42
45
  end
43
46
 
44
- class UnparseableResponse < StandardError
47
+ class UnparseableResponse < XeroizerError
45
48
 
46
49
  def initialize(root_element_name)
47
50
  @root_element_name = root_element_name
@@ -53,7 +56,7 @@ module Xeroizer
53
56
 
54
57
  end
55
58
 
56
- class ObjectNotFound < StandardError
59
+ class ObjectNotFound < XeroizerError
57
60
 
58
61
  def initialize(api_endpoint)
59
62
  @api_endpoint = api_endpoint
@@ -65,11 +68,11 @@ module Xeroizer
65
68
 
66
69
  end
67
70
 
68
- class InvoiceNotFoundError < StandardError; end
71
+ class InvoiceNotFoundError < XeroizerError; end
69
72
 
70
- class CreditNoteNotFoundError < StandardError; end
73
+ class CreditNoteNotFoundError < XeroizerError; end
71
74
 
72
- class MethodNotAllowed < StandardError
75
+ class MethodNotAllowed < XeroizerError
73
76
 
74
77
  def initialize(klass, method)
75
78
  @klass = klass
@@ -82,7 +85,7 @@ module Xeroizer
82
85
 
83
86
  end
84
87
 
85
- class RecordKeyMustBeDefined < StandardError
88
+ class RecordKeyMustBeDefined < XeroizerError
86
89
 
87
90
  def initialize(possible_keys)
88
91
  @possible_keys = possible_keys
@@ -94,7 +97,7 @@ module Xeroizer
94
97
 
95
98
  end
96
99
 
97
- class SettingTotalDirectlyNotSupported < StandardError
100
+ class SettingTotalDirectlyNotSupported < XeroizerError
98
101
 
99
102
  def initialize(attribute_name)
100
103
  @attribute_name = attribute_name
@@ -106,7 +109,7 @@ module Xeroizer
106
109
 
107
110
  end
108
111
 
109
- class InvalidAttributeInWhere < StandardError
112
+ class InvalidAttributeInWhere < XeroizerError
110
113
 
111
114
  def initialize(model_name, attribute_name)
112
115
  @model_name = model_name
@@ -119,7 +122,7 @@ module Xeroizer
119
122
 
120
123
  end
121
124
 
122
- class AssociationTypeMismatch < StandardError
125
+ class AssociationTypeMismatch < XeroizerError
123
126
 
124
127
  def initialize(model_class, actual_class)
125
128
  @model_class = model_class
@@ -132,7 +135,7 @@ module Xeroizer
132
135
 
133
136
  end
134
137
 
135
- class CannotChangeInvoiceStatus < StandardError
138
+ class CannotChangeInvoiceStatus < XeroizerError
136
139
 
137
140
  def initialize(invoice, new_status)
138
141
  @invoice = invoice
@@ -6,12 +6,14 @@ module Xeroizer
6
6
  include Http
7
7
  extend Record::ApplicationHelper
8
8
 
9
- attr_reader :client, :xero_url, :logger, :rate_limit_sleep, :rate_limit_max_attempts, :default_headers, :unitdp
9
+ attr_reader :client, :xero_url, :logger, :rate_limit_sleep, :rate_limit_max_attempts,
10
+ :default_headers, :unitdp, :before_request, :after_request
10
11
 
11
12
  extend Forwardable
12
13
  def_delegators :client, :access_token
13
14
 
14
15
  record :Account
16
+ record :Allocation
15
17
  record :Attachment
16
18
  record :BrandingTheme
17
19
  record :Contact
@@ -26,7 +28,10 @@ module Xeroizer
26
28
  record :ManualJournal
27
29
  record :Organisation
28
30
  record :Payment
31
+ record :Prepayment
29
32
  record :Receipt
33
+ record :RepeatingInvoice
34
+ record :Schedule
30
35
  record :TaxRate
31
36
  record :TrackingCategory
32
37
  record :TrackingCategoryChild
@@ -54,6 +59,8 @@ module Xeroizer
54
59
  @rate_limit_sleep = options[:rate_limit_sleep] || false
55
60
  @rate_limit_max_attempts = options[:rate_limit_max_attempts] || 5
56
61
  @default_headers = options[:default_headers] || {}
62
+ @before_request = options.delete(:before_request)
63
+ @after_request = options.delete(:after_request)
57
64
  @client = OAuth.new(consumer_key, consumer_secret, options.merge({default_headers: default_headers}))
58
65
  @logger = options[:logger] || false
59
66
  @unitdp = options[:unitdp] || 2
data/lib/xeroizer/http.rb CHANGED
@@ -15,6 +15,7 @@
15
15
  module Xeroizer
16
16
  module Http
17
17
  class BadResponse < StandardError; end
18
+ RequestInfo = Struct.new(:url, :headers, :params, :body)
18
19
 
19
20
  ACCEPT_MIME_MAP = {
20
21
  :pdf => 'application/pdf',
@@ -57,9 +58,9 @@ module Xeroizer
57
58
 
58
59
  headers = self.default_headers.merge({ 'charset' => 'utf-8' })
59
60
 
60
- # include the unitdp query string parameter
61
+ # include the unitdp query string parameter
61
62
  params.merge!(unitdp_param(url))
62
-
63
+
63
64
  if method != :get
64
65
  headers['Content-Type'] ||= "application/x-www-form-urlencoded"
65
66
  end
@@ -84,10 +85,13 @@ module Xeroizer
84
85
  url += "?" + params.map {|key,value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}"}.join("&")
85
86
  end
86
87
 
87
- uri = URI.parse(url)
88
+ uri = URI.parse(url)
88
89
 
89
90
  attempts = 0
90
91
 
92
+ request_info = RequestInfo.new(url, headers, params, body)
93
+ before_request.call(request_info) if before_request
94
+
91
95
  begin
92
96
  attempts += 1
93
97
  logger.info("XeroGateway Request: #{method.to_s.upcase} #{uri.request_uri}") if self.logger
@@ -100,12 +104,8 @@ module Xeroizer
100
104
  when :put then client.put(uri.request_uri, raw_body, headers)
101
105
  end
102
106
 
103
- if self.logger
104
- logger.info("XeroGateway Response (#{response.code})")
105
- unless response.code.to_i == 200
106
- logger.info("#{uri.request_uri}\n== Response Body\n\n#{response.plain_body}\n== End Response Body")
107
- end
108
- end
107
+ log_response(response, uri)
108
+ after_request.call(request_info, response) if after_request
109
109
 
110
110
  case response.code.to_i
111
111
  when 200
@@ -133,7 +133,16 @@ module Xeroizer
133
133
  end
134
134
  end
135
135
 
136
- def handle_oauth_error!(response)
136
+ def log_response(response, uri)
137
+ if self.logger
138
+ logger.info("XeroGateway Response (#{response.code})")
139
+ logger.add(response.code.to_i == 200 ? Logger::DEBUG : Logger::INFO) {
140
+ "#{uri.request_uri}\n== Response Body\n\n#{response.plain_body}\n== End Response Body"
141
+ }
142
+ end
143
+ end
144
+
145
+ def handle_oauth_error!(response)
137
146
  error_details = CGI.parse(response.plain_body)
138
147
  description = error_details["oauth_problem_advice"].first
139
148
  problem = error_details["oauth_problem"].first
@@ -4,12 +4,12 @@ module Xeroizer
4
4
  class AttachmentModel < BaseModel
5
5
 
6
6
  module Extensions
7
- def attach_data(id, filename, data, content_type = "application/octet-stream")
8
- application.Attachment.attach_data(url, id, filename, data, content_type)
7
+ def attach_data(id, filename, data, content_type = "application/octet-stream", options = {})
8
+ application.Attachment.attach_data(url, id, filename, data, content_type, options)
9
9
  end
10
10
 
11
- def attach_file(id, filename, path, content_type = "application/octet-stream")
12
- application.Attachment.attach_file(url, id, filename, path, content_type)
11
+ def attach_file(id, filename, path, content_type = "application/octet-stream", options = {})
12
+ application.Attachment.attach_file(url, id, filename, path, content_type, options)
13
13
  end
14
14
 
15
15
  def attachments(id)
@@ -19,11 +19,13 @@ module Xeroizer
19
19
 
20
20
  set_permissions :read
21
21
 
22
- def attach_data(url, id, filename, data, content_type)
22
+ def attach_data(url, id, filename, data, content_type, options = {})
23
+ options = { include_online: false }.merge(options)
24
+
23
25
  response_xml = @application.http_put(@application.client,
24
26
  "#{url}/#{CGI.escape(id)}/Attachments/#{CGI.escape(filename)}",
25
27
  data,
26
- :raw_body => true, :content_type => content_type
28
+ :raw_body => true, :content_type => content_type, "IncludeOnline" => options[:include_online]
27
29
  )
28
30
  response = parse_response(response_xml)
29
31
  if (response_items = response.response_items) && response_items.size > 0
@@ -33,8 +35,8 @@ module Xeroizer
33
35
  end
34
36
  end
35
37
 
36
- def attach_file(url, id, filename, path, content_type)
37
- attach_data(url, id, filename, File.read(path), content_type)
38
+ def attach_file(url, id, filename, path, content_type, options = {})
39
+ attach_data(url, id, filename, File.read(path), content_type, options)
38
40
  end
39
41
 
40
42
  def attachments_for(url, id)
@@ -54,12 +56,12 @@ module Xeroizer
54
56
  class Attachment < Base
55
57
 
56
58
  module Extensions
57
- def attach_file(filename, path, content_type = "application/octet-stream")
58
- parent.attach_file(id, filename, path, content_type)
59
+ def attach_file(filename, path, content_type = "application/octet-stream", options = {})
60
+ parent.attach_file(id, filename, path, content_type, options)
59
61
  end
60
62
 
61
- def attach_data(filename, data, content_type = "application/octet-stream")
62
- parent.attach_data(id, filename, data, content_type)
63
+ def attach_data(filename, data, content_type = "application/octet-stream", options = {})
64
+ parent.attach_data(id, filename, data, content_type, options)
63
65
  end
64
66
 
65
67
  def attachments
@@ -90,4 +92,4 @@ module Xeroizer
90
92
  end
91
93
 
92
94
  end
93
- end
95
+ end
@@ -38,12 +38,14 @@ module Xeroizer
38
38
  string :bank_transaction_id, :api_name => "BankTransactionID"
39
39
  boolean :is_reconciled
40
40
  string :status
41
+ string :currency_code
42
+ decimal :currency_rate
41
43
 
42
44
  alias_method :reconciled?, :is_reconciled
43
45
 
44
46
  belongs_to :contact, :model_name => 'Contact'
45
47
  string :line_amount_types
46
- has_many :line_items, :model_name => 'LineItem'
48
+ has_many :line_items, :model_name => 'LineItem', :complete_on_page => true
47
49
  belongs_to :bank_account, :model_name => 'BankAccount'
48
50
 
49
51
  validates_inclusion_of :line_amount_types,
@@ -14,7 +14,7 @@ module Xeroizer
14
14
  guid :branding_theme_id
15
15
  string :name
16
16
  integer :sort_order
17
- datetime :created_date_utc, :api_name => 'CreatedDateUTC'
17
+ datetime_utc :created_date_utc, :api_name => 'CreatedDateUTC'
18
18
 
19
19
  end
20
20
 
@@ -36,10 +36,12 @@ module Xeroizer
36
36
  string :skype_user_name
37
37
  string :contact_groups
38
38
  string :default_currency
39
+ string :purchases_default_account_code
40
+ string :sales_default_account_code
39
41
  datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
40
42
  boolean :is_supplier
41
43
  boolean :is_customer
42
-
44
+
43
45
  has_many :addresses, :list_complete => true
44
46
  has_many :phones, :list_complete => true
45
47
  has_many :contact_groups
@@ -126,8 +126,32 @@ module Xeroizer
126
126
  def pdf(filename = nil)
127
127
  parent.pdf(id, filename)
128
128
  end
129
-
129
+
130
+ def save
131
+ # Calling parse_save_response() on the credit note will wipe out
132
+ # the allocations, so we have to manually preserve them.
133
+ allocations_backup = self.allocations
134
+ if super
135
+ self.allocations = allocations_backup
136
+ allocate unless self.allocations.empty?
137
+ true
138
+ end
139
+ end
140
+
141
+ def allocate
142
+ if self.class.possible_primary_keys && self.class.possible_primary_keys.all? { | possible_key | self[possible_key].nil? }
143
+ raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys)
144
+ end
145
+
146
+ request = association_to_xml(:allocations)
147
+ allocations_url = "#{parent.url}/#{CGI.escape(id)}/Allocations"
148
+
149
+ log "[ALLOCATION SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}"
150
+ response = parent.application.http_put(parent.application.client, allocations_url, request)
151
+ log "[ALLOCATION RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}"
152
+ parse_save_response(response)
153
+ end
130
154
  end
131
-
155
+
132
156
  end
133
157
  end
@@ -17,12 +17,57 @@ module Xeroizer
17
17
  string :status
18
18
  string :first_name
19
19
  string :last_name
20
+ date :date_of_birth
20
21
 
21
- belongs_to :external_link
22
+ # Optional attributes
23
+ string :gender # M or F
24
+ string :email
25
+ string :phone # (max length = 50, but AU only?)
26
+ string :mobile # (max length = 50)
27
+ date :start_date
28
+ date :termination_date
29
+ boolean :is_authorised_to_approve_timesheets
30
+ string :employee_group_name
22
31
 
23
- validates_presence_of :first_name, :last_name
32
+ datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
24
33
 
34
+ belongs_to :external_link
35
+
36
+ validates_presence_of :first_name, :last_name, :date_of_birth
37
+
25
38
  end
26
-
39
+
40
+ # # AU Only API
41
+ # class EmployeeAU < Employee
42
+ # string :occupation
43
+ # string :classification # length: 100
44
+ # string :ordinary_earnings_rate_id
45
+ # boolean :is_authorised_to_approve_leave
46
+
47
+ # string :title # (max length = 10)
48
+ # string :twitter_user_name # (max length = 50)
49
+ # # PayrollCalendarID Xero unique identifier for payroll calendar for the employee
50
+ # # BankAccounts See BankAccount
51
+ # # SuperMemberships See SuperMemberships
52
+ # end
53
+
54
+ # # US Only API
55
+ # class EmployeeUS < Employee
56
+ # string :middle_names #max length = 35
57
+ # string :job_title
58
+ # string :employee_number
59
+ # string :social_security_number # (xxx-xx-xxxx)
60
+ # string :holiday_group_id
61
+ # string :pay_schedule_id
62
+ # string :employment_basis # One of http://developer.xero.com/documentation/payroll-api-us/Types-Codes/#EmploymentBasis
63
+ # boolean :is_authorised_to_approve_time_off
64
+
65
+ # # MailingAddress - US only, has_one relationship
66
+ # # SalaryAndWages See SalaryAndWages
67
+ # # WorkLocations See WorkLocations
68
+ # # PaymentMethod See PaymentMethods
69
+ # # PayTemplate See PayTemplate
70
+ # # OpeningBalances See OpeningBalances
71
+ # end
27
72
  end
28
- end
73
+ end
@@ -78,7 +78,7 @@ module Xeroizer
78
78
  boolean :has_attachments
79
79
 
80
80
  belongs_to :contact
81
- has_many :line_items
81
+ has_many :line_items, :complete_on_page => true
82
82
  has_many :payments
83
83
  has_many :credit_notes
84
84
 
@@ -14,8 +14,10 @@ module Xeroizer
14
14
  guid :journal_id
15
15
  date :journal_date, :internal_name => :date
16
16
  string :journal_number
17
- datetime :created_date_utc, :api_name => 'CreatedDateUTC'
17
+ datetime_utc :created_date_utc, :api_name => 'CreatedDateUTC'
18
18
  string :reference
19
+ guid :source_id
20
+ string :source_type
19
21
 
20
22
  has_many :journal_lines
21
23
 
@@ -19,6 +19,7 @@ module Xeroizer
19
19
  decimal :tax_amount
20
20
  string :tax_type
21
21
  string :tax_name
22
+ string :description
22
23
 
23
24
  has_many :tracking_categories, :model_name => 'JournalLineTrackingCategory'
24
25
 
@@ -13,8 +13,9 @@ module Xeroizer
13
13
  string :name
14
14
  string :option
15
15
  guid :tracking_category_id
16
+ guid :tracking_option_id
16
17
 
17
18
  end
18
19
 
19
20
  end
20
- end
21
+ end
@@ -19,6 +19,7 @@ module Xeroizer
19
19
  decimal :tax_amount
20
20
  decimal :line_amount, :calculated => true
21
21
  decimal :discount_rate
22
+ string :line_item_id
22
23
 
23
24
  has_many :tracking, :model_name => 'TrackingCategoryChild'
24
25
 
@@ -29,7 +29,7 @@ module Xeroizer
29
29
  boolean :show_on_cash_basis_reports
30
30
  datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
31
31
 
32
- has_many :journal_lines, :model_name => 'ManualJournalLine'
32
+ has_many :journal_lines, :model_name => 'ManualJournalLine', :complete_on_page => true
33
33
 
34
34
  validates_presence_of :narration
35
35
  validates_associated :journal_lines
@@ -10,6 +10,28 @@ module Xeroizer
10
10
 
11
11
  class Organisation < Base
12
12
 
13
+ SALES_TAX_BASIS = {
14
+ 'NZ' =>
15
+ {'PAYMENTS' => 'Payments Basis',
16
+ 'INVOICE' => 'Invoice Basis',
17
+ 'NONE' => 'None'},
18
+ 'GB' =>
19
+ {
20
+ 'CASH' => 'Cash Scheme',
21
+ 'ACCRUAL' => 'Accrual Scheme',
22
+ 'FLATRATECASH' => 'Flate Rate Cash Scheme',
23
+ 'NONE' => 'None'
24
+ },
25
+ 'GLOBAL' =>
26
+ {
27
+ 'CASH' => 'Cash Basis',
28
+ 'ACCRUALS' => 'Accruals Basis',
29
+ 'NONE' => 'None'
30
+ }
31
+ } unless defined?(SALES_TAX_BASES)
32
+
33
+ SALES_TAX_BASES = SALES_TAX_BASIS.values.map(&:keys).flatten.sort
34
+
13
35
  string :api_key, :api_name => 'APIKey'
14
36
  string :name
15
37
  string :legal_name
@@ -23,16 +45,34 @@ module Xeroizer
23
45
  string :organisation_status
24
46
  integer :financial_year_end_day
25
47
  integer :financial_year_end_month
48
+ string :sales_tax_basis
49
+ string :sales_tax_period
26
50
  date :period_lock_date
27
51
  date :end_of_year_lock_date
28
52
  string :tax_number
29
53
  string :registration_number
30
54
  string :timezone
31
- datetime :created_date_utc, :api_name => 'CreatedDateUTC'
55
+ datetime_utc :created_date_utc, :api_name => 'CreatedDateUTC'
56
+ string :sales_tax_basis
57
+ string :sales_tax_period
32
58
 
33
59
  has_many :addresses
34
60
  has_many :phones
35
61
 
62
+ validates :sales_tax_basis, :message => "is not a valid option" do
63
+ valid = true
64
+ if sales_tax_basis
65
+
66
+ valid_bases = (SALES_TAX_BASIS[country_code] || SALES_TAX_BASIS['GLOBAL']).keys
67
+
68
+ unless valid_bases.include?(sales_tax_basis)
69
+ valid = false
70
+ end
71
+ end
72
+
73
+ valid
74
+ end
75
+
36
76
  end
37
77
 
38
78
  end
@@ -18,7 +18,8 @@ module Xeroizer
18
18
  string :phone_number, :internal_name => :number
19
19
  string :phone_area_code, :internal_name => :area_code
20
20
  string :phone_country_code, :internal_name => :country_code
21
-
21
+
22
+ validates_length_of :phone_number, :max => 50
22
23
  end
23
24
 
24
25
  end
@@ -0,0 +1,39 @@
1
+ module Xeroizer
2
+ module Record
3
+
4
+ class PrepaymentModel < BaseModel
5
+
6
+ set_xml_root_name 'Prepayments'
7
+ set_permissions :read
8
+
9
+ end
10
+
11
+ class Prepayment < Base
12
+ set_primary_key :prepayment_id
13
+
14
+ guid :prepayment_id
15
+ date :date
16
+ string :status
17
+ string :line_amount_types
18
+ decimal :sub_total
19
+ decimal :total_tax
20
+ decimal :total
21
+ datetime_utc :updated_date_utc, :api_name => 'UpdatedDateUTC'
22
+ string :currency_code
23
+ datetime_utc :fully_paid_on_date
24
+ string :type
25
+ string :reference
26
+ decimal :currency_rate
27
+ decimal :remaining_credit
28
+ boolean :has_attachments
29
+
30
+ belongs_to :contact
31
+ has_many :line_items
32
+
33
+ def contact_id
34
+ contact.id if contact
35
+ end
36
+
37
+ end
38
+ end
39
+ end