xeroizer 0.3.5 → 0.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.
- data/.bundle/config +2 -2
- data/Gemfile +5 -0
- data/Rakefile +17 -1
- data/VERSION +1 -1
- data/lib/xeroizer.rb +5 -1
- data/lib/xeroizer/configuration.rb +19 -0
- data/lib/xeroizer/generic_application.rb +2 -1
- data/lib/xeroizer/logging.rb +8 -0
- data/lib/xeroizer/models/account.rb +2 -1
- data/lib/xeroizer/models/bank_account.rb +12 -0
- data/lib/xeroizer/models/bank_transaction.rb +74 -0
- data/lib/xeroizer/models/invoice.rb +17 -12
- data/lib/xeroizer/models/item.rb +3 -3
- data/lib/xeroizer/models/item_purchase_details.rb +19 -0
- data/lib/xeroizer/models/{item_purchase_sale_details.rb → item_sales_details.rb} +5 -3
- data/lib/xeroizer/models/line_amount_type.rb +11 -0
- data/lib/xeroizer/models/line_item.rb +2 -12
- data/lib/xeroizer/models/line_item_sum.rb +21 -0
- data/lib/xeroizer/models/payment.rb +2 -6
- data/lib/xeroizer/oauth.rb +1 -1
- data/lib/xeroizer/record/base.rb +21 -2
- data/lib/xeroizer/record/validation_helper.rb +14 -2
- data/lib/xeroizer/record/validators/block_validator.rb +22 -0
- data/lib/xeroizer/record/validators/validator.rb +14 -5
- data/lib/xeroizer/record/xml_helper.rb +24 -7
- data/test/acceptance/about_creating_bank_transactions_test.rb +162 -0
- data/test/acceptance/about_fetching_bank_transactions_test.rb +56 -0
- data/test/acceptance/acceptance_test.rb +53 -0
- data/test/acceptance/bank_transaction_reference_data.rb +31 -0
- data/test/test_helper.rb +11 -1
- data/test/unit/models/bank_transaction_model_parsing_test.rb +131 -0
- data/test/unit/models/bank_transaction_test.rb +47 -0
- data/test/unit/models/bank_transaction_validation_test.rb +87 -0
- data/test/unit/models/contact_test.rb +2 -2
- data/test/unit/models/credit_note_test.rb +2 -2
- data/test/unit/models/invoice_test.rb +43 -17
- data/test/unit/models/line_item_sum_test.rb +24 -0
- data/test/unit/models/line_item_test.rb +54 -0
- data/test/unit/oauth_config_test.rb +20 -0
- data/test/unit/oauth_test.rb +1 -1
- data/test/unit/private_application_test.rb +2 -2
- data/test/unit/record/base_model_test.rb +2 -2
- data/test/unit/record/base_test.rb +38 -1
- data/test/unit/record/block_validator_test.rb +125 -0
- data/test/unit/record/model_definition_test.rb +2 -2
- data/test/unit/record/parse_where_hash_test.rb +2 -2
- data/test/unit/record/record_association_test.rb +1 -1
- data/test/unit/record/validators_test.rb +51 -3
- data/test/unit/record_definition_test.rb +2 -2
- data/test/unit/report_definition_test.rb +2 -2
- data/test/unit/report_test.rb +1 -1
- data/xeroizer.gemspec +60 -6
- metadata +124 -66
- data/lib/.DS_Store +0 -0
data/.bundle/config
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
---
|
2
|
-
|
1
|
+
--- {}
|
2
|
+
|
data/Gemfile
CHANGED
@@ -4,9 +4,14 @@ gem 'builder', '>= 2.1.2'
|
|
4
4
|
gem 'oauth', '>= 0.3.6'
|
5
5
|
gem 'activesupport'
|
6
6
|
gem 'nokogiri'
|
7
|
+
gem 'i18n'
|
8
|
+
gem 'yard'
|
7
9
|
|
8
10
|
group :test do
|
9
11
|
gem 'mocha'
|
10
12
|
gem 'shoulda'
|
11
13
|
gem "jeweler", "~> 1.5.2"
|
14
|
+
gem "rest-client"
|
15
|
+
gem "turn"
|
16
|
+
gem "ansi"
|
12
17
|
end
|
data/Rakefile
CHANGED
@@ -28,11 +28,27 @@ task :default => :test
|
|
28
28
|
|
29
29
|
desc 'Test the xero gateway.'
|
30
30
|
Rake::TestTask.new(:test) do |t|
|
31
|
-
t.libs << 'lib'
|
31
|
+
t.libs << ['lib', 'test']
|
32
32
|
t.pattern = 'test/**/*_test.rb'
|
33
33
|
t.verbose = true
|
34
34
|
end
|
35
35
|
|
36
|
+
namespace :test do
|
37
|
+
desc 'Run acceptance/integration tests'
|
38
|
+
Rake::TestTask.new(:acceptance) do |t|
|
39
|
+
t.libs << ['lib', 'test']
|
40
|
+
t.pattern = 'test/acceptance/**/*_test.rb'
|
41
|
+
t.verbose = true
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Run unit tests'
|
45
|
+
Rake::TestTask.new(:unit) do |t|
|
46
|
+
t.libs << ['lib', 'test']
|
47
|
+
t.pattern = 'test/unit/**/*_test.rb'
|
48
|
+
t.verbose = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
36
52
|
YARD::Rake::YardocTask.new do |t|
|
37
53
|
# t.files = ['lib/**/*.rb', OTHER_PATHS] # optional
|
38
54
|
# t.options = ['--any', '--extra', '--opts'] # optional
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/xeroizer.rb
CHANGED
@@ -23,11 +23,14 @@ require 'xeroizer/exceptions'
|
|
23
23
|
|
24
24
|
require 'xeroizer/record/base_model'
|
25
25
|
require 'xeroizer/record/base'
|
26
|
+
require 'xeroizer/configuration'
|
26
27
|
|
27
28
|
# Include models
|
28
29
|
require 'xeroizer/models/account'
|
29
30
|
require 'xeroizer/models/address'
|
30
31
|
require 'xeroizer/models/branding_theme'
|
32
|
+
require 'xeroizer/models/bank_transaction'
|
33
|
+
require 'xeroizer/models/bank_account'
|
31
34
|
require 'xeroizer/models/contact'
|
32
35
|
require 'xeroizer/models/contact_group'
|
33
36
|
require 'xeroizer/models/credit_note'
|
@@ -35,7 +38,8 @@ require 'xeroizer/models/currency'
|
|
35
38
|
require 'xeroizer/models/employee'
|
36
39
|
require 'xeroizer/models/invoice'
|
37
40
|
require 'xeroizer/models/item'
|
38
|
-
require 'xeroizer/models/
|
41
|
+
require 'xeroizer/models/item_purchase_details'
|
42
|
+
require 'xeroizer/models/item_sales_details'
|
39
43
|
require 'xeroizer/models/journal'
|
40
44
|
require 'xeroizer/models/journal_line'
|
41
45
|
require 'xeroizer/models/line_item'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Xeroizer
|
2
|
+
OAuthCredentials = Struct.new "OAuthCredentials", :consumer_key, :consumer_secret, :key_file
|
3
|
+
|
4
|
+
class OAuthConfig
|
5
|
+
class << self
|
6
|
+
def load yaml_text
|
7
|
+
require "yaml"
|
8
|
+
yaml = YAML.load yaml_text
|
9
|
+
consumer_credential = yaml["consumer"]
|
10
|
+
|
11
|
+
OAuthCredentials.new(
|
12
|
+
consumer_credential["key"],
|
13
|
+
consumer_credential["secret"],
|
14
|
+
consumer_credential["key_file"]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'xeroizer/models/line_item'
|
2
|
+
require 'xeroizer/models/line_item_sum'
|
3
|
+
|
4
|
+
module Xeroizer
|
5
|
+
module Record
|
6
|
+
class BankTransactionModel < BaseModel
|
7
|
+
set_permissions :read
|
8
|
+
end
|
9
|
+
|
10
|
+
class BankTransaction < Base
|
11
|
+
def initialize(parent)
|
12
|
+
super parent
|
13
|
+
self.line_amount_types = "Exclusive"
|
14
|
+
end
|
15
|
+
|
16
|
+
set_primary_key :bank_transaction_id
|
17
|
+
string :type
|
18
|
+
date :date
|
19
|
+
|
20
|
+
date :updated_date_utc, :api_name => "UpdatedDateUTC"
|
21
|
+
date :fully_paid_on_date
|
22
|
+
string :bank_transaction_id, :api_name => "BankTransactionID"
|
23
|
+
boolean :is_reconciled
|
24
|
+
|
25
|
+
alias_method :reconciled?, :is_reconciled
|
26
|
+
|
27
|
+
belongs_to :contact, :model_name => 'Contact'
|
28
|
+
string :line_amount_types
|
29
|
+
has_many :line_items, :model_name => 'LineItem'
|
30
|
+
belongs_to :bank_account, :model_name => 'BankAccount'
|
31
|
+
|
32
|
+
validates_inclusion_of :line_amount_types,
|
33
|
+
:in => Xeroizer::Record::LINE_AMOUNT_TYPES, :allow_blanks => false
|
34
|
+
|
35
|
+
validates_inclusion_of :type,
|
36
|
+
:in => %w{SPEND RECEIVE}, :allow_blanks => false,
|
37
|
+
:message => "Invalid type. Expected either SPEND or RECEIVE."
|
38
|
+
|
39
|
+
validates_presence_of :contact, :bank_account, :allow_blanks => false
|
40
|
+
|
41
|
+
validates :line_items, :message => "Invalid line items. Must supply at least one." do
|
42
|
+
self.line_items.size > 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def sub_total=(value); raise SettingTotalDirectlyNotSupported.new(:sub_total); end
|
46
|
+
def total_tax=(value); raise SettingTotalDirectlyNotSupported.new(:total_tax); end
|
47
|
+
def total=(value); raise SettingTotalDirectlyNotSupported.new(:total); end
|
48
|
+
|
49
|
+
def total; sub_total + total_tax; end
|
50
|
+
|
51
|
+
def sub_total
|
52
|
+
if ought_to_recalculate_totals?
|
53
|
+
result = LineItemSum.sub_total(self.line_items)
|
54
|
+
result -= total_tax if line_amount_types == 'Inclusive'
|
55
|
+
result
|
56
|
+
else
|
57
|
+
attributes[:sub_total]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def total_tax
|
62
|
+
return ought_to_recalculate_totals? ?
|
63
|
+
LineItemSum.total_tax(self.line_items) :
|
64
|
+
attributes[:total_tax]
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def ought_to_recalculate_totals?
|
70
|
+
new_record? || line_items && line_items.size > 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -40,13 +40,6 @@ module Xeroizer
|
|
40
40
|
} unless defined?(INVOICE_STATUS)
|
41
41
|
INVOICE_STATUSES = INVOICE_STATUS.keys.sort
|
42
42
|
|
43
|
-
LINE_AMOUNT_TYPE = {
|
44
|
-
"Inclusive" => 'CreditNote lines are inclusive tax',
|
45
|
-
"Exclusive" => 'CreditNote lines are exclusive of tax (default)',
|
46
|
-
"NoTax" => 'CreditNotes lines have no tax'
|
47
|
-
} unless defined?(LINE_AMOUNT_TYPE)
|
48
|
-
LINE_AMOUNT_TYPES = LINE_AMOUNT_TYPE.keys.sort
|
49
|
-
|
50
43
|
set_primary_key :invoice_id
|
51
44
|
set_possible_primary_keys :invoice_id, :invoice_number
|
52
45
|
list_contains_summary_only true
|
@@ -77,15 +70,27 @@ module Xeroizer
|
|
77
70
|
has_many :payments
|
78
71
|
has_many :credit_notes
|
79
72
|
|
80
|
-
validates_presence_of :date, :due_date, :unless =>
|
73
|
+
validates_presence_of :date, :due_date, :unless => :new_record?
|
81
74
|
validates_inclusion_of :type, :in => INVOICE_TYPES
|
82
|
-
validates_inclusion_of :status, :in => INVOICE_STATUSES, :unless =>
|
83
|
-
validates_inclusion_of :line_amount_types, :in => LINE_AMOUNT_TYPES, :unless =>
|
75
|
+
validates_inclusion_of :status, :in => INVOICE_STATUSES, :unless => :new_record?
|
76
|
+
validates_inclusion_of :line_amount_types, :in => LINE_AMOUNT_TYPES, :unless => :new_record?
|
84
77
|
validates_associated :contact
|
85
|
-
validates_associated :line_items, :allow_blanks => true, :unless =>
|
86
|
-
validates_associated :line_items, :if =>
|
78
|
+
validates_associated :line_items, :allow_blanks => true, :unless => :approved?
|
79
|
+
validates_associated :line_items, :if => :approved?
|
87
80
|
|
88
81
|
public
|
82
|
+
|
83
|
+
# Access the contact name without forcing a download of
|
84
|
+
# an incomplete, summary invoice.
|
85
|
+
def contact_name
|
86
|
+
attributes[:contact] && attributes[:contact][:name]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Access the contact ID without forcing a download of an
|
90
|
+
# incomplete, summary invoice.
|
91
|
+
def contact_id
|
92
|
+
attributes[:contact] && attributes[:contact][:contact_id]
|
93
|
+
end
|
89
94
|
|
90
95
|
# Helper method to check if the invoice has been approved.
|
91
96
|
def approved?
|
data/lib/xeroizer/models/item.rb
CHANGED
@@ -16,12 +16,12 @@ module Xeroizer
|
|
16
16
|
string :code
|
17
17
|
string :description
|
18
18
|
|
19
|
-
belongs_to :purchase_details, :model_name => '
|
20
|
-
belongs_to :sales_details, :model_name => '
|
19
|
+
belongs_to :purchase_details, :model_name => 'ItemPurchaseDetails'
|
20
|
+
belongs_to :sales_details, :model_name => 'ItemSalesDetails'
|
21
21
|
|
22
22
|
validates_presence_of :code
|
23
23
|
|
24
24
|
end
|
25
25
|
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Xeroizer
|
2
|
+
module Record
|
3
|
+
|
4
|
+
class ItemPurchaseDetailsModel < BaseModel
|
5
|
+
|
6
|
+
set_xml_node_name 'PurchaseDetails'
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class ItemPurchaseDetails < Base
|
11
|
+
|
12
|
+
decimal :unit_price
|
13
|
+
string :account_code
|
14
|
+
string :tax_type
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Xeroizer
|
2
2
|
module Record
|
3
3
|
|
4
|
-
class
|
4
|
+
class ItemSalesDetailsModel < BaseModel
|
5
|
+
|
6
|
+
set_xml_node_name 'SalesDetails'
|
5
7
|
|
6
8
|
end
|
7
9
|
|
8
|
-
class
|
10
|
+
class ItemSalesDetails < Base
|
9
11
|
|
10
12
|
decimal :unit_price
|
11
13
|
string :account_code
|
@@ -14,4 +16,4 @@ module Xeroizer
|
|
14
16
|
end
|
15
17
|
|
16
18
|
end
|
17
|
-
end
|
19
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Xeroizer
|
2
|
+
module Record
|
3
|
+
line_amount_type = {
|
4
|
+
"Inclusive" => 'Line item amounts are inclusive of tax',
|
5
|
+
"Exclusive" => 'Line item amounts are exclusive of tax (default)',
|
6
|
+
"NoTax" => 'Line item amounts have no tax'
|
7
|
+
}
|
8
|
+
|
9
|
+
LINE_AMOUNT_TYPES = line_amount_type.keys.sort
|
10
|
+
end
|
11
|
+
end
|
@@ -1,21 +1,13 @@
|
|
1
1
|
require 'xeroizer/models/account'
|
2
|
+
require 'xeroizer/models/line_amount_type'
|
2
3
|
|
3
4
|
module Xeroizer
|
4
5
|
module Record
|
5
|
-
|
6
6
|
class LineItemModel < BaseModel
|
7
7
|
|
8
8
|
end
|
9
9
|
|
10
10
|
class LineItem < Base
|
11
|
-
|
12
|
-
LINE_AMOUNT_TYPE = {
|
13
|
-
"Inclusive" => 'CreditNote lines are inclusive tax',
|
14
|
-
"Exclusive" => 'CreditNote lines are exclusive of tax (default)',
|
15
|
-
"NoTax" => 'CreditNotes lines have no tax'
|
16
|
-
} unless defined?(LINE_AMOUNT_TYPE)
|
17
|
-
LINE_AMOUNT_TYPES = LINE_AMOUNT_TYPE.keys.sort
|
18
|
-
|
19
11
|
TAX_TYPE = Account::TAX_TYPE unless defined?(TAX_TYPE)
|
20
12
|
|
21
13
|
string :item_code
|
@@ -35,9 +27,7 @@ module Xeroizer
|
|
35
27
|
# Calculate the line_total (if there is a quantity and unit_amount).
|
36
28
|
# Description-only lines have been allowed since Xero V2.09.
|
37
29
|
def line_amount
|
38
|
-
|
39
|
-
quantity * unit_amount
|
40
|
-
end
|
30
|
+
BigDecimal((quantity * unit_amount).to_s).round(2) if quantity && unit_amount
|
41
31
|
end
|
42
32
|
|
43
33
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Xeroizer
|
2
|
+
module Record
|
3
|
+
class LineItemSum
|
4
|
+
def self.total(line_items)
|
5
|
+
sub_total(line_items) + total_tax(line_items)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.sub_total(line_items)
|
9
|
+
line_items.inject(BigDecimal("0")) do |sum, item|
|
10
|
+
sum += BigDecimal(item.line_amount.to_s).round(2)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.total_tax(line_items)
|
15
|
+
line_items.inject(BigDecimal("0")) do |sum, item|
|
16
|
+
sum += BigDecimal(item.tax_amount.to_s).round(2)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -3,6 +3,7 @@ module Xeroizer
|
|
3
3
|
|
4
4
|
class PaymentModel < BaseModel
|
5
5
|
|
6
|
+
set_xml_root_name 'Payments'
|
6
7
|
set_permissions :read, :write
|
7
8
|
|
8
9
|
end
|
@@ -16,12 +17,7 @@ module Xeroizer
|
|
16
17
|
decimal :amount
|
17
18
|
decimal :currency_rate
|
18
19
|
string :reference
|
19
|
-
|
20
|
-
guid :invoice_id
|
21
|
-
string :invoice_number
|
22
|
-
guid :account_id
|
23
|
-
string :code
|
24
|
-
|
20
|
+
|
25
21
|
belongs_to :account
|
26
22
|
belongs_to :invoice
|
27
23
|
|
data/lib/xeroizer/oauth.rb
CHANGED
@@ -97,7 +97,7 @@ module Xeroizer
|
|
97
97
|
# Renew an access token from a previously authorised token for a
|
98
98
|
# PARTNER application.
|
99
99
|
def renew_access_token(atoken = nil, asecret = nil, session_handle = nil)
|
100
|
-
old_token = ::OAuth::RequestToken.new(consumer, atoken || @atoken, asecret || @
|
100
|
+
old_token = ::OAuth::RequestToken.new(consumer, atoken || @atoken, asecret || @asecret)
|
101
101
|
access_token = old_token.get_access_token({
|
102
102
|
:oauth_session_handle => (session_handle || @session_handle),
|
103
103
|
:token => old_token
|
data/lib/xeroizer/record/base.rb
CHANGED
@@ -2,6 +2,7 @@ require 'xeroizer/record/model_definition_helper'
|
|
2
2
|
require 'xeroizer/record/record_association_helper'
|
3
3
|
require 'xeroizer/record/validation_helper'
|
4
4
|
require 'xeroizer/record/xml_helper'
|
5
|
+
require 'xeroizer/logging'
|
5
6
|
|
6
7
|
module Xeroizer
|
7
8
|
module Record
|
@@ -96,7 +97,13 @@ module Xeroizer
|
|
96
97
|
|
97
98
|
# Attempt to create a new record.
|
98
99
|
def create
|
99
|
-
|
100
|
+
request = to_xml
|
101
|
+
log "[CREATE SENT] (#{__FILE__}:#{__LINE__}) #{request}"
|
102
|
+
|
103
|
+
response = parent.http_put(request)
|
104
|
+
log "[CREATE RECEIVED] (#{__FILE__}:#{__LINE__}) #{response}"
|
105
|
+
|
106
|
+
parse_save_response(response)
|
100
107
|
end
|
101
108
|
|
102
109
|
# Attempt to update an existing record.
|
@@ -105,7 +112,15 @@ module Xeroizer
|
|
105
112
|
raise RecordKeyMustBeDefined.new(self.class.possible_primary_keys)
|
106
113
|
end
|
107
114
|
|
108
|
-
|
115
|
+
request = to_xml
|
116
|
+
|
117
|
+
log "[UPDATE SENT] (#{__FILE__}:#{__LINE__}) \r\n#{request}"
|
118
|
+
|
119
|
+
response = parent.http_post(request)
|
120
|
+
|
121
|
+
log "[UPDATE RECEIVED] (#{__FILE__}:#{__LINE__}) \r\n#{response}"
|
122
|
+
|
123
|
+
parse_save_response(response)
|
109
124
|
end
|
110
125
|
|
111
126
|
# Parse the response from a create/update request.
|
@@ -117,6 +132,10 @@ module Xeroizer
|
|
117
132
|
end
|
118
133
|
self
|
119
134
|
end
|
135
|
+
|
136
|
+
def log(what)
|
137
|
+
Xeroizer::Logging::Log.info what
|
138
|
+
end
|
120
139
|
|
121
140
|
end
|
122
141
|
|