xero 0.0.1.1

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 (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