xero 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +8 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/active_support/inflection_config.rb +4 -0
  9. data/lib/active_support/inflections.rb +3 -0
  10. data/lib/oauth/consumer.rb +13 -0
  11. data/lib/xero/associations/belongs_to.rb +24 -0
  12. data/lib/xero/associations/has_many.rb +40 -0
  13. data/lib/xero/associations/has_many_proxy.rb +20 -0
  14. data/lib/xero/associations/has_one.rb +25 -0
  15. data/lib/xero/associations.rb +11 -0
  16. data/lib/xero/client.rb +83 -0
  17. data/lib/xero/clients/partner_application.rb +6 -0
  18. data/lib/xero/clients/private_application.rb +12 -0
  19. data/lib/xero/configuration.rb +14 -0
  20. data/lib/xero/connection.rb +62 -0
  21. data/lib/xero/models/account.rb +66 -0
  22. data/lib/xero/models/address.rb +28 -0
  23. data/lib/xero/models/base_model.rb +52 -0
  24. data/lib/xero/models/contact.rb +27 -0
  25. data/lib/xero/models/invoice.rb +37 -0
  26. data/lib/xero/models/item.rb +26 -0
  27. data/lib/xero/models/item_detail.rb +14 -0
  28. data/lib/xero/models/line_item.rb +16 -0
  29. data/lib/xero/models/payment.rb +21 -0
  30. data/lib/xero/models/phone.rb +18 -0
  31. data/lib/xero/models/tracking_category.rb +12 -0
  32. data/lib/xero/models/tracking_category_option.rb +12 -0
  33. data/lib/xero/version.rb +3 -0
  34. data/lib/xero.rb +39 -0
  35. data/spec/lib/xero/client_spec.rb +4 -0
  36. data/spec/lib/xero/clients/private_application_spec.rb +216 -0
  37. data/spec/lib/xero/models/account_spec.rb +38 -0
  38. data/spec/lib/xero/models/address_spec.rb +21 -0
  39. data/spec/lib/xero/models/base_model_spec.rb +37 -0
  40. data/spec/lib/xero/models/contact_spec.rb +4 -0
  41. data/spec/lib/xero/models/invoice_spec.rb +6 -0
  42. data/spec/lib/xero/models/item_detail_spec.rb +10 -0
  43. data/spec/lib/xero/models/item_spec.rb +119 -0
  44. data/spec/lib/xero/models/line_item_spec.rb +12 -0
  45. data/spec/lib/xero/models/payment_spec.rb +4 -0
  46. data/spec/lib/xero/models/phone_spec.rb +15 -0
  47. data/spec/lib/xero/models/tracking_category_option_spec.rb +4 -0
  48. data/spec/lib/xero/models/tracking_category_spec.rb +4 -0
  49. data/spec/spec_helper.rb +28 -0
  50. data/spec/support/find_account.xml +13 -0
  51. data/spec/support/helpers.rb +9 -0
  52. data/spec/support/vcr.rb +6 -0
  53. data/spec/vcr_cassettes/contact.yml +66 -0
  54. data/spec/vcr_cassettes/contacts.yml +66 -0
  55. data/spec/vcr_cassettes/create_item.yml +67 -0
  56. data/spec/vcr_cassettes/get_invoice.yml +79 -0
  57. data/spec/vcr_cassettes/get_item.yml +61 -0
  58. data/spec/vcr_cassettes/get_items.yml +67 -0
  59. data/spec/vcr_cassettes/invoices.yml +62 -0
  60. data/xero.gemspec +30 -0
  61. metadata +274 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ spec/support/privatekey.pem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xero.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Matt Beedle
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Xero
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'xero'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install xero
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ inflect.irregular 'status', 'status'
3
+ inflect.irregular 'address', 'address'
4
+ end
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ inflect.irregular 'status', 'status'
3
+ end
@@ -0,0 +1,13 @@
1
+ module OAuth
2
+ class Consumer
3
+
4
+ def http_with_ssl_client_certificates(*args)
5
+ @http ||= http_without_ssl_client_certificates(*args).tap do |http|
6
+ http.cert = options[:ssl_client_cert]
7
+ http.key = options[:ssl_client_key]
8
+ end
9
+ end
10
+
11
+ alias_method_chain :http, :ssl_client_certificates
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Xero
2
+ module Associations
3
+ module BelongsTo
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ def belongs_to(association_name, options = {})
9
+
10
+ attr_accessor :"#{association_name}_id"
11
+
12
+ define_method association_name do
13
+ instance_variable_get(:"#{association_name}")
14
+ end
15
+
16
+ define_method "#{association_name}=" do |value|
17
+ instance_variable_set(:"@#{association_name}", value)
18
+ send "#{association_name}_id", value.id
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ module Xero
2
+ module Associations
3
+ module HasMany
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def has_many(association_name, options = {})
8
+
9
+ define_method association_name do
10
+ var = instance_variable_get(:"@#{association_name}")
11
+ if var.blank?
12
+ var = HasManyProxy.new
13
+ instance_variable_set(:"@#{association_name}", var)
14
+ end
15
+ var
16
+ end
17
+
18
+ define_method "#{association_name}=" do |value|
19
+ if value.is_a?(Hash) && value[value.keys.first].is_a?(Array)
20
+ klass = options[:class_name] || association_name.to_s.singularize
21
+ klass = "Xero::Models::#{klass.to_s.classify}".constantize
22
+ value[value.keys.first].each do |item|
23
+ self.send(association_name).send :<<, klass.new(item)
24
+ end
25
+ end
26
+
27
+ # if value.is_a?(Hash)
28
+ # klass = options[:class_name] || association_name.to_s.singularize
29
+ # value = "Xero::Models::#{klass.to_s.classify}".constantize.
30
+ # new(value)
31
+ # end
32
+ # instance_variable_set(
33
+ # :"@#{association_name}", HasManyProxy.new(value)
34
+ # )
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ module Xero
2
+ module Associations
3
+ class HasManyProxy < BasicObject
4
+
5
+ def initialize(*args)
6
+ @target = ::Array.new(*args)
7
+ end
8
+
9
+ protected
10
+
11
+ def target
12
+ @target
13
+ end
14
+
15
+ def method_missing(method, *args, &block)
16
+ target.send method, *args, &block
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ module Xero
2
+ module Associations
3
+ module HasOne
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def has_one(association_name, options = {})
8
+
9
+ define_method association_name do
10
+ instance_variable_get(:"@#{association_name}")
11
+ end
12
+
13
+ define_method "#{association_name}=" do |value|
14
+ if value.is_a?(Hash)
15
+ klass = options[:class_name] || association_name
16
+ value = "Xero::Models::#{klass.to_s.classify}".constantize.
17
+ new(value)
18
+ end
19
+ instance_variable_set(:"@#{association_name}", value)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Xero
2
+ module Associations
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Xero::Associations::BelongsTo
7
+ include Xero::Associations::HasMany
8
+ include Xero::Associations::HasOne
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,83 @@
1
+ module Xero
2
+ class Client
3
+ attr_accessor :client
4
+
5
+ def initialize(options = {})
6
+ self.client = OAuth::Consumer.new(
7
+ Xero.configuration.consumer_key,
8
+ Xero.configuration.consumer_secret, options
9
+ )
10
+ end
11
+
12
+ def contacts(options = {})
13
+ response = self.connection.get(Xero::Models::Contact.path, options)
14
+ contacts_attributes = hash_from_response(response, 'Contacts')
15
+ unless contacts_attributes.is_a?(Array)
16
+ contacts_attributes = [contacts_attributes]
17
+ end
18
+ contacts_attributes.map { |attrs| Xero::Models::Contact.new(attrs) }
19
+ end
20
+
21
+ def get_contact(contact_id)
22
+ response = self.connection.
23
+ get_by_id(Xero::Models::Contact.path, contact_id)
24
+ Xero::Models::Contact.new hash_from_response(response, 'Contacts')
25
+ end
26
+
27
+ def get_item(item_id)
28
+ response = self.connection.get_by_id(Xero::Models::Item.path, item_id)
29
+ Xero::Models::Item.new(hash_from_response(response, 'Items'))
30
+ end
31
+
32
+ def items(options = {})
33
+ response = self.connection.get(Xero::Models::Item.path, options)
34
+ items = hash_from_response(response, 'Items')
35
+ items.map { |item| Xero::Models::Item.new(item) }
36
+ end
37
+
38
+ def get_invoice(invoice_id)
39
+ response = self.connection.
40
+ get_by_id(Xero::Models::Invoice.path, invoice_id)
41
+ Xero::Models::Invoice.new hash_from_response(response, 'Invoices')
42
+ end
43
+
44
+ def invoices(options = {})
45
+ response = self.connection.get(Xero::Models::Invoice.path, options)
46
+ invoices_attributes = hash_from_response(response, 'Invoices')
47
+ unless invoices_attributes.is_a?(Array)
48
+ invoices_attributes = [invoices_attributes]
49
+ end
50
+ invoices_attributes.map { |attrs| Xero::Models::Invoice.new(attrs) }
51
+ end
52
+
53
+ def save(model)
54
+ model.tap do |item|
55
+ response = self.connection.post(model.class.path, model.to_xero_xml)
56
+ attrs = Hash.from_xml(response.body)['Response']['Items']['Item']
57
+ model.id = attrs["#{model.class.to_s.demodulize}ID"]
58
+ model.new_record = false
59
+ end
60
+ end
61
+
62
+ def build_item(attributes = {})
63
+ build_model Xero::Models::Item, attributes
64
+ end
65
+
66
+ def build_model(klass, item_attributes = {})
67
+ klass.new(item_attributes).tap do |item|
68
+ item.client = self
69
+ end
70
+ end
71
+
72
+ protected
73
+
74
+ def connection
75
+ @connection ||= Xero::Connection.new
76
+ end
77
+
78
+ def hash_from_response(response, klass_name)
79
+ attributes = Hash.from_xml(response.body)['Response']
80
+ attributes[klass_name][klass_name.singularize]
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,6 @@
1
+ module Xero
2
+ module Clients
3
+ class PartnerApplication
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module Xero
2
+ module Clients
3
+ class PrivateApplication < Client
4
+ def connection
5
+ @connection ||= Xero::Connection.new(
6
+ signature_method: 'RSA-SHA1',
7
+ private_key_file: Xero.configuration.private_key_path
8
+ )
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module Xero
2
+ class Configuration
3
+
4
+ attr_accessor :logger, :consumer_key, :consumer_secret, :private_key_path
5
+
6
+ def logger
7
+ @logger || Logger.new(STDOUT)
8
+ end
9
+
10
+ def xero_url
11
+ 'https://api.xero.com/api.xro/2.0'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ module Xero
2
+ class Connection
3
+
4
+ attr_accessor :client, :consumer_options
5
+
6
+ def initialize(options = {})
7
+ self.consumer_options = {
8
+ site: 'https://api.xero.com',
9
+ request_token_path: '/oauth/RequestToken',
10
+ access_token_path: '/oauth/AccessToken',
11
+ authorize_path: '/oauth/Authorize'
12
+ }.reverse_merge(options)
13
+ end
14
+
15
+ def consumer
16
+ @consumer ||= ::OAuth::Consumer.new(
17
+ Xero.configuration.consumer_key,
18
+ Xero.configuration.consumer_secret,
19
+ self.consumer_options
20
+ )
21
+ end
22
+
23
+ def access_token
24
+ @access_token ||= ::OAuth::AccessToken.new(
25
+ consumer, Xero.configuration.consumer_key,
26
+ Xero.configuration.consumer_secret
27
+ )
28
+ end
29
+
30
+ def get_by_id(path, id)
31
+ make_request(:get, "#{path}/#{id}")
32
+ end
33
+
34
+ def get(path, params = {})
35
+ path = "#{path}?#{params.to_query}" unless params.blank?
36
+ make_request(:get, path)
37
+ end
38
+
39
+ def post(path, payload, params = {})
40
+ make_request(:post, path, { xml: payload })
41
+ end
42
+
43
+ private
44
+
45
+ def make_request(method, path, params = {})
46
+ uri = "#{Xero.configuration.xero_url}/#{path}"
47
+
48
+ headers = { 'charset' => 'utf-8' }
49
+
50
+ if method == :get
51
+ response = access_token.get(uri, params)
52
+ else
53
+ response = access_token.post(uri, params, headers)
54
+ end
55
+ handle_response(response)
56
+ end
57
+
58
+ def handle_response(response)
59
+ response
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ module Xero
2
+ module Models
3
+
4
+ class Account < BaseModel
5
+
6
+ self.path = 'Accounts'
7
+
8
+ TYPE = {
9
+ 'CURRENT' => '',
10
+ 'FIXED' => '',
11
+ 'PREPAYMENT' => '',
12
+ 'EQUITY' => '',
13
+ 'DEPRECIATN' => '',
14
+ 'DIRECTCOSTS' => '',
15
+ 'EXPENSE' => '',
16
+ 'OVERHEADS' => '',
17
+ 'CURRLIAB' => '',
18
+ 'LIABILITY' => '',
19
+ 'TERMLIAB' => '',
20
+ 'OTHERINCOME' => '',
21
+ 'REVENUE' => '',
22
+ 'SALES' => ''
23
+ } unless defined?(TYPE)
24
+
25
+ TAX_TYPE = {
26
+ 'NONE' => 'No GST',
27
+ 'EXEMPTINPUT' => 'VAT on expenses exempt from VAT (UK only)',
28
+ 'INPUT' => 'GST on expenses',
29
+ 'SRINPUT' => 'VAT on expenses',
30
+ 'ZERORATEDINPUT' => 'Expense purchased from overseas (UK only)',
31
+ 'RRINPUT' => 'Reduced rate VAT on expenses (UK Only)',
32
+ 'EXEMPTOUTPUT' => 'VAT on sales exempt from VAT (UK only)',
33
+ 'ECZROUTPUT' => 'EC Zero-rated output',
34
+ 'OUTPUT' => 'OUTPUT (old rate)',
35
+ 'OUTPUT2' => 'OUTPUT2',
36
+ 'SROUTPUT' => 'SROUTPUT',
37
+ 'ZERORATEDOUTPUT' => 'Sales made from overseas (UK only)',
38
+ 'RROUTPUT' => 'Reduced rate VAT on sales (UK Only)',
39
+ 'ZERORATED' => 'Zero-rated supplies/sales from overseas (NZ Only)'
40
+ } unless defined?(TAX_TYPE)
41
+
42
+ attribute :id
43
+ attribute :code
44
+ attribute :name
45
+ attribute :account_type
46
+ attribute :tax_type
47
+ attribute :description
48
+ attribute :system_account
49
+ attribute :enable_payments_to_account, type: Boolean
50
+ attribute :bank_account_number
51
+ attribute :currency_code
52
+
53
+ # def self.from_xml(xml)
54
+ # attrs = Hash.from_xml(xml)['Accounts']['Account']
55
+
56
+ # attrs['id'] = attrs.delete('AccountID')
57
+ # attrs['account_type'] = attrs.delete('Type')
58
+ # attrs.keys.each do |key|
59
+ # attrs[key.tableize.singularize] = attrs.delete(key)
60
+ # end
61
+
62
+ # self.new(attrs)
63
+ # end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ module Xero
2
+ module Models
3
+ class Address < BaseModel
4
+
5
+ attribute :address_type
6
+ attribute :attention_to
7
+ attribute :address_line1
8
+ attribute :address_line2
9
+ attribute :address_line3
10
+ attribute :address_line4
11
+ attribute :city
12
+ attribute :region
13
+ attribute :postal_code
14
+ attribute :country
15
+
16
+ validates :address_type, presence: true
17
+ validates :attention_to, presence: true
18
+ validates :address_line1, presence: true
19
+ validates :city, presence: true
20
+ validates :region, presence: true
21
+ validates :postal_code, presence: true
22
+ validates :country, presence: true
23
+
24
+ belongs_to :contact
25
+ belongs_to :phone
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ module Xero
2
+ module Models
3
+ class BaseModel
4
+ include ActiveAttr::Model
5
+ include Xero::Associations
6
+
7
+ class << self
8
+ attr_accessor :path
9
+ end
10
+
11
+ attribute :id
12
+
13
+ attr_accessor :new_record, :client
14
+
15
+ def initialize(attributes = {})
16
+ @new_record = true
17
+ cleanup_hash(attributes).each { |key, value| send("#{key}=", value) }
18
+ end
19
+
20
+ def save
21
+ raise StandardError if self.client.blank?
22
+ self.client.save(self)
23
+ end
24
+
25
+ def xero_attributes(attrs = nil)
26
+ attrs ||= attributes.clone
27
+ attrs.delete('id')
28
+ attrs.keys.each do |key|
29
+ value = attrs.delete(key)
30
+ value = xero_attributes(value) if value.is_a?(Hash)
31
+ attrs[key.to_s.camelize] = value.to_s
32
+ end
33
+ attrs
34
+ end
35
+
36
+ def cleanup_hash(hash)
37
+ hash.keys.each do |key|
38
+ value = hash.delete(key)
39
+ value = cleanup_hash(value) if value.is_a?(Hash)
40
+ key = key.to_s.underscore
41
+ key = 'id' if key.match(/_id/)
42
+ hash[key] = value
43
+ end
44
+ hash
45
+ end
46
+
47
+ def persisted?
48
+ id && !@new_record
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,27 @@
1
+ module Xero
2
+ module Models
3
+ class Contact < Xero::Models::BaseModel
4
+
5
+ self.path = 'Contacts'
6
+
7
+ attribute :contact_number
8
+ attribute :name
9
+ attribute :first_name
10
+ attribute :last_name
11
+ attribute :contact_status
12
+ attribute :skype_user_name
13
+ attribute :bank_account_details
14
+ attribute :tax_number
15
+ attribute :accounts_receivable_tax_type
16
+ attribute :accounts_payable_tax_type
17
+ attribute :updated_date_utc, type: DateTime
18
+ attribute :is_supplier, type: Boolean
19
+ attribute :is_customer, type: Boolean
20
+ attribute :default_currency
21
+ attribute :email_address
22
+
23
+ has_many :addresses
24
+ has_many :phones
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ module Xero
2
+ module Models
3
+ class Invoice < Xero::Models::BaseModel
4
+
5
+ self.path = 'Invoices'
6
+
7
+ attribute :type
8
+ attribute :date, type: Date
9
+ attribute :due_date, type: Date
10
+ attribute :line_amount_type
11
+ attribute :invoice_number
12
+ attribute :reference
13
+ attribute :url
14
+ attribute :currency_code
15
+ attribute :status
16
+ attribute :line_amount_types
17
+ attribute :subtotal, type: Float
18
+ attribute :total_tax, type: Float
19
+ attribute :total, type: Float
20
+ attribute :amount_due, type: Float
21
+ attribute :amount_paid, type: Float
22
+ attribute :amount_credited, type: Float
23
+ attribute :total_discount, type: Float
24
+ attribute :sub_total
25
+ attribute :updated_date_utc, type: DateTime
26
+ attribute :has_attachments, type: Boolean
27
+ attribute :sent_to_contact, type: Boolean
28
+ attribute :currency_rate
29
+
30
+ has_one :contact
31
+ has_many :line_items
32
+ has_many :payments
33
+
34
+ validates :type, presence: true
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Xero
2
+ module Models
3
+ class Item < BaseModel
4
+
5
+ self.path = 'Items'
6
+
7
+ attribute :code
8
+ attribute :description
9
+ attribute :updated_date_utc, type: DateTime
10
+
11
+ validates :code, presence: true
12
+ validates :purchase_details, presence: true
13
+ validates :sales_details, presence: true
14
+
15
+ has_one :purchase_details, class_name: :item_detail
16
+ has_one :sales_details, class_name: :item_detail
17
+
18
+ def to_xero_xml
19
+ xero_attributes(attributes.clone).
20
+ merge('PurchaseDetails' => self.purchase_details.xero_attributes).
21
+ merge('SalesDetails' => self.sales_details.xero_attributes).
22
+ to_xml(root: 'Item')
23
+ end
24
+ end
25
+ end
26
+ end