intacctrb 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +113 -0
- data/LICENSE.txt +23 -0
- data/README.md +37 -0
- data/Rakefile +7 -0
- data/intacctrb.gemspec +31 -0
- data/lib/.DS_Store +0 -0
- data/lib/intacctrb.rb +35 -0
- data/lib/intacctrb/ap_payment.rb +168 -0
- data/lib/intacctrb/base.rb +119 -0
- data/lib/intacctrb/bill.rb +159 -0
- data/lib/intacctrb/customer.rb +116 -0
- data/lib/intacctrb/invoice.rb +133 -0
- data/lib/intacctrb/journal_entry.rb +166 -0
- data/lib/intacctrb/vendor.rb +130 -0
- data/lib/intacctrb/version.rb +3 -0
- data/spec/features/intacct_bill.feature +17 -0
- data/spec/features/intacct_customer.feature +18 -0
- data/spec/features/intacct_invoice.feature +17 -0
- data/spec/features/intacct_vendor.feature +17 -0
- data/spec/helpers.rb +115 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/steps/intacct_bill_steps.rb +178 -0
- data/spec/steps/intacct_customer_steps.rb +27 -0
- data/spec/steps/intacct_invoice_steps.rb +170 -0
- data/spec/steps/intacct_vendor_steps.rb +23 -0
- data/spec/turnip_helper.rb +7 -0
- metadata +224 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
module IntacctRB
|
2
|
+
class Base < Struct.new(:object, :current_user)
|
3
|
+
include Hooks
|
4
|
+
include Hooks::InstanceHooks
|
5
|
+
|
6
|
+
define_hook :after_create, :after_update, :after_delete,
|
7
|
+
:after_get, :after_send_xml, :on_error, :before_create
|
8
|
+
|
9
|
+
after_create :set_intacct_system_id
|
10
|
+
after_delete :delete_intacct_system_id
|
11
|
+
after_delete :delete_intacct_key
|
12
|
+
after_send_xml :set_date_time
|
13
|
+
|
14
|
+
attr_accessor :response, :data, :sent_xml, :intacct_action
|
15
|
+
|
16
|
+
def initialize *params
|
17
|
+
params[0] = OpenStruct.new(params[0]) if params[0].is_a? Hash
|
18
|
+
super(*params)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def send_xml action
|
24
|
+
@intacct_action = action.to_s
|
25
|
+
run_hook :"before_#{intacct_action}" if action=="create"
|
26
|
+
|
27
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
28
|
+
xml.request {
|
29
|
+
xml.control {
|
30
|
+
xml.senderid IntacctRB.xml_sender_id
|
31
|
+
xml.password IntacctRB.xml_password
|
32
|
+
xml.controlid "INVOICE XML"
|
33
|
+
xml.uniqueid "false"
|
34
|
+
xml.dtdversion "2.1"
|
35
|
+
}
|
36
|
+
xml.operation(transaction: "false") {
|
37
|
+
xml.authentication {
|
38
|
+
xml.login {
|
39
|
+
xml.userid IntacctRB.app_user_id
|
40
|
+
xml.companyid IntacctRB.app_company_id
|
41
|
+
xml.password IntacctRB.app_password
|
42
|
+
}
|
43
|
+
}
|
44
|
+
xml.content {
|
45
|
+
yield xml
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
xml = builder.doc.root.to_xml
|
52
|
+
puts xml
|
53
|
+
@sent_xml = xml
|
54
|
+
|
55
|
+
url = "https://www.intacct.com/ia/xml/xmlgw.phtml"
|
56
|
+
uri = URI(url)
|
57
|
+
|
58
|
+
res = Net::HTTP.post_form(uri, 'xmlrequest' => xml)
|
59
|
+
@response = Nokogiri::XML(res.body)
|
60
|
+
puts res.body
|
61
|
+
if successful?
|
62
|
+
if key = response.at('//result//key')
|
63
|
+
set_intacct_key key.content
|
64
|
+
end
|
65
|
+
|
66
|
+
if intacct_action
|
67
|
+
run_hook :after_send_xml, intacct_action
|
68
|
+
#run_hook :"after_#{intacct_action}"
|
69
|
+
end
|
70
|
+
else
|
71
|
+
run_hook :on_error
|
72
|
+
end
|
73
|
+
|
74
|
+
@response
|
75
|
+
end
|
76
|
+
|
77
|
+
def successful?
|
78
|
+
if status = response.at('//result//status') and status.content == "success"
|
79
|
+
true
|
80
|
+
else
|
81
|
+
false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
%w(invoice bill vendor customer).each do |type|
|
86
|
+
define_method "intacct_#{type}_prefix" do
|
87
|
+
Intacct.send("#{type}_prefix")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def intacct_system_id
|
92
|
+
intacct_object_id
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_intacct_system_id
|
96
|
+
object.intacct_system_id = intacct_object_id
|
97
|
+
end
|
98
|
+
|
99
|
+
def delete_intacct_system_id
|
100
|
+
object.intacct_system_id = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_intacct_key key
|
104
|
+
object.intacct_key = key if object.respond_to? :intacct_key
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete_intacct_key
|
108
|
+
object.intacct_key = nil if object.respond_to? :intacct_key
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_date_time type
|
112
|
+
if %w(create update delete).include? type
|
113
|
+
if object.respond_to? :"intacct_#{type}d_at"
|
114
|
+
object.send("intacct_#{type}d_at=", DateTime.now)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module IntacctRB
|
2
|
+
class Bill < IntacctRB::Base
|
3
|
+
attr_accessor :customer_data
|
4
|
+
define_hook :custom_bill_fields, :bill_item_fields
|
5
|
+
|
6
|
+
def create
|
7
|
+
return false if object.payment.intacct_system_id.present?
|
8
|
+
|
9
|
+
# Need to create the customer if one doesn't exist
|
10
|
+
unless object.customer.intacct_system_id
|
11
|
+
intacct_customer = Intacct::Customer.new object.customer
|
12
|
+
unless intacct_customer.create
|
13
|
+
raise 'Could not grab Intacct customer data'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Create vendor if we have one and not in Intacct
|
18
|
+
if object.vendor and object.vendor.intacct_system_id.blank?
|
19
|
+
intacct_vendor = Intacct::Vendor.new object.vendor
|
20
|
+
if intacct_vendor.create
|
21
|
+
object.vendor = intacct_vendor.object
|
22
|
+
else
|
23
|
+
raise 'Could not create vendor'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
send_xml('create') do |xml|
|
28
|
+
xml.function(controlid: "f1") {
|
29
|
+
xml.send("create_bill") {
|
30
|
+
bill_xml xml
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
successful?
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete
|
39
|
+
# return false unless object.payment.intacct_system_id.present?
|
40
|
+
|
41
|
+
send_xml('delete') do |xml|
|
42
|
+
xml.function(controlid: "1") {
|
43
|
+
xml.delete_bill(externalkey: "false", key: object.intacct_key)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
successful?
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_list(options = {})
|
51
|
+
send_xml('get_list') do |xml|
|
52
|
+
xml.function(controlid: "f4") {
|
53
|
+
xml.get_list(object: "bill", maxitems: (options[:max_items] || 0),
|
54
|
+
start: (options[:start] || 0), showprivate:"true") {
|
55
|
+
if options[:filters]
|
56
|
+
xml.filter {
|
57
|
+
options[:filters].each do |filter|
|
58
|
+
xml.expression do
|
59
|
+
filter.each_pair do |k,v|
|
60
|
+
xml.send(k,v)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
if options[:fields]
|
67
|
+
xml.fields {
|
68
|
+
fields.each do |field|
|
69
|
+
xml.field field.to_s
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
if successful?
|
78
|
+
@data = []
|
79
|
+
@response.xpath('//bill').each do |invoice|
|
80
|
+
@data << OpenStruct.new({
|
81
|
+
id: invoice.at("key").content,
|
82
|
+
vendor_id: invoice.at("vendorid").content,
|
83
|
+
bill_number: invoice.at("billno").content,
|
84
|
+
po_number: invoice.at("ponumber").content,
|
85
|
+
state: invoice.at("state").content,
|
86
|
+
date_posted: get_date_at('dateposted', invoice),
|
87
|
+
date_due: get_date_at('datedue', invoice),
|
88
|
+
date_paid: get_date_at('datepaid', invoice),
|
89
|
+
total: invoice.at("totalamount").content,
|
90
|
+
total_paid: invoice.at("totalpaid").content,
|
91
|
+
total_due: invoice.at("totaldue").content,
|
92
|
+
termname: invoice.at("termname").content,
|
93
|
+
description: invoice.at("description").content,
|
94
|
+
modified_at: invoice.at("whenmodified").content
|
95
|
+
})
|
96
|
+
end
|
97
|
+
@data
|
98
|
+
else
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def get_date_at(xpath, object)
|
104
|
+
year = object.at("#{xpath}/year").content
|
105
|
+
month = object.at("#{xpath}/month").content
|
106
|
+
day = object.at("#{xpath}/day").content
|
107
|
+
if [year,month,day].any?(&:empty?)
|
108
|
+
nil
|
109
|
+
else
|
110
|
+
Date.new(year.to_i,month.to_i,day.to_i)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def intacct_object_id
|
115
|
+
"#{intacct_bill_prefix}#{object.payment.id}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def bill_xml xml
|
119
|
+
xml.vendorid object.vendor.intacct_system_id
|
120
|
+
xml.datecreated {
|
121
|
+
xml.year object.payment.created_at.strftime("%Y")
|
122
|
+
xml.month object.payment.created_at.strftime("%m")
|
123
|
+
xml.day object.payment.created_at.strftime("%d")
|
124
|
+
}
|
125
|
+
xml.dateposted {
|
126
|
+
xml.year object.payment.created_at.strftime("%Y")
|
127
|
+
xml.month object.payment.created_at.strftime("%m")
|
128
|
+
xml.day object.payment.created_at.strftime("%d")
|
129
|
+
}
|
130
|
+
xml.datedue {
|
131
|
+
xml.year object.payment.paid_at.strftime("%Y")
|
132
|
+
xml.month object.payment.paid_at.strftime("%m")
|
133
|
+
xml.day object.payment.paid_at.strftime("%d")
|
134
|
+
}
|
135
|
+
run_hook :custom_bill_fields, xml
|
136
|
+
run_hook :bill_item_fields, xml
|
137
|
+
end
|
138
|
+
|
139
|
+
def set_intacct_system_id
|
140
|
+
object.payment.intacct_system_id = intacct_object_id
|
141
|
+
end
|
142
|
+
|
143
|
+
def delete_intacct_system_id
|
144
|
+
object.payment.intacct_system_id = nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_intacct_key
|
148
|
+
object.payment.intacct_key = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
def set_date_time type
|
152
|
+
if %w(create update delete).include? type
|
153
|
+
if object.payment.respond_to? :"intacct_#{type}d_at"
|
154
|
+
object.payment.send("intacct_#{type}d_at=", DateTime.now)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module IntacctRB
|
2
|
+
class Customer < IntacctRB::Base
|
3
|
+
def create
|
4
|
+
send_xml('create') do |xml|
|
5
|
+
xml.function(controlid: "1") {
|
6
|
+
xml.send("create_customer") {
|
7
|
+
xml.customerid intacct_object_id
|
8
|
+
xml.name object.name
|
9
|
+
xml.comments
|
10
|
+
xml.status "active"
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
successful?
|
16
|
+
end
|
17
|
+
|
18
|
+
def get *fields
|
19
|
+
#return false unless object.intacct_system_id.present?
|
20
|
+
|
21
|
+
fields = [
|
22
|
+
:customerid,
|
23
|
+
:name,
|
24
|
+
:termname
|
25
|
+
] if fields.empty?
|
26
|
+
|
27
|
+
send_xml('get') do |xml|
|
28
|
+
xml.function(controlid: "f4") {
|
29
|
+
xml.get(object: "customer", key: "intacct_system_id") {
|
30
|
+
xml.fields {
|
31
|
+
fields.each do |field|
|
32
|
+
xml.field field.to_s
|
33
|
+
end
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
if successful?
|
40
|
+
@data = OpenStruct.new({
|
41
|
+
id: response.at("//customer//customerid").content,
|
42
|
+
name: response.at("//customer//name").content,
|
43
|
+
termname: response.at("//customer//termname").content
|
44
|
+
})
|
45
|
+
end
|
46
|
+
|
47
|
+
successful?
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_list *fields
|
51
|
+
#return false unless object.intacct_system_id.present?
|
52
|
+
|
53
|
+
fields = [
|
54
|
+
:customerid,
|
55
|
+
:name,
|
56
|
+
:termname
|
57
|
+
] if fields.empty?
|
58
|
+
|
59
|
+
send_xml('get_list') do |xml|
|
60
|
+
xml.function(controlid: "f4") {
|
61
|
+
xml.get_list(object: "customer", maxitems: "10", showprivate:"false") {
|
62
|
+
# xml.fields {
|
63
|
+
# fields.each do |field|
|
64
|
+
# xml.field field.to_s
|
65
|
+
# end
|
66
|
+
# }
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# if successful?
|
72
|
+
# @data = OpenStruct.new({
|
73
|
+
# id: response.at("//customer//customerid").content,
|
74
|
+
# name: response.at("//customer//name").content,
|
75
|
+
# termname: response.at("//customer//termname").content
|
76
|
+
# })
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# successful?
|
80
|
+
puts response
|
81
|
+
end
|
82
|
+
|
83
|
+
def update updated_customer = false
|
84
|
+
@object = updated_customer if updated_customer
|
85
|
+
return false unless object.intacct_system_id.present?
|
86
|
+
|
87
|
+
send_xml('update') do |xml|
|
88
|
+
xml.function(controlid: "1") {
|
89
|
+
xml.update_customer(customerid: intacct_system_id) {
|
90
|
+
xml.name object.name
|
91
|
+
xml.comments
|
92
|
+
xml.status "active"
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
successful?
|
98
|
+
end
|
99
|
+
|
100
|
+
def delete
|
101
|
+
return false unless object.intacct_system_id.present?
|
102
|
+
|
103
|
+
@response = send_xml('delete') do |xml|
|
104
|
+
xml.function(controlid: "1") {
|
105
|
+
xml.delete_customer(customerid: intacct_system_id)
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
successful?
|
110
|
+
end
|
111
|
+
|
112
|
+
def intacct_object_id
|
113
|
+
"#{intacct_customer_prefix}#{object.id}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module IntacctRB
|
2
|
+
class Invoice < IntacctRB::Base
|
3
|
+
attr_accessor :customer_data
|
4
|
+
define_hook :custom_invoice_fields
|
5
|
+
|
6
|
+
def get_list *fields
|
7
|
+
# return false unless object.intacct_system_id.present?
|
8
|
+
|
9
|
+
fields = [
|
10
|
+
:customerid,
|
11
|
+
:name,
|
12
|
+
:termname
|
13
|
+
] if fields.empty?
|
14
|
+
|
15
|
+
send_xml('get_list') do |xml|
|
16
|
+
xml.function(controlid: "f4") {
|
17
|
+
xml.get_list(object: "invoice", maxitems: "10", showprivate:"false") {
|
18
|
+
# xml.fields {
|
19
|
+
# fields.each do |field|
|
20
|
+
# xml.field field.to_s
|
21
|
+
# end
|
22
|
+
# }
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
if successful?
|
28
|
+
@data = []
|
29
|
+
@response.xpath('//invoice').each do |invoice|
|
30
|
+
@data << Invoice.new({
|
31
|
+
id: invoice.at("//customerid").content,
|
32
|
+
total: invoice.at("//totalamount").content,
|
33
|
+
total_paid: invoice.at("//totalpaid").content,
|
34
|
+
termname: invoice.at("//termname").content
|
35
|
+
})
|
36
|
+
end
|
37
|
+
@data
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create
|
44
|
+
return false if object.invoice.intacct_system_id.present?
|
45
|
+
|
46
|
+
# Need to create the customer if one doesn't exist
|
47
|
+
intacct_customer = Intacct::Customer.new object.customer
|
48
|
+
unless object.customer.intacct_system_id.present?
|
49
|
+
unless intacct_customer.create
|
50
|
+
raise 'Could not create customer'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if intacct_customer.get
|
55
|
+
object.customer = intacct_customer.object
|
56
|
+
@customer_data = intacct_customer.data
|
57
|
+
else
|
58
|
+
raise 'Could not grab Intacct customer data'
|
59
|
+
end
|
60
|
+
|
61
|
+
# Create vendor if we have one and not in Intacct
|
62
|
+
if object.vendor and object.vendor.intacct_system_id.blank?
|
63
|
+
intacct_vendor = Intacct::Vendor.new object.vendor
|
64
|
+
if intacct_vendor.create
|
65
|
+
object.vendor = intacct_vendor.object
|
66
|
+
else
|
67
|
+
raise 'Could not create vendor'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
send_xml('create') do |xml|
|
72
|
+
xml.function(controlid: "f1") {
|
73
|
+
xml.create_invoice {
|
74
|
+
invoice_xml xml
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
successful?
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete
|
83
|
+
return false unless object.invoice.intacct_system_id.present?
|
84
|
+
|
85
|
+
send_xml('delete') do |xml|
|
86
|
+
xml.function(controlid: "1") {
|
87
|
+
xml.delete_invoice(externalkey: "false", key: object.invoice.intacct_key)
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
successful?
|
92
|
+
end
|
93
|
+
|
94
|
+
def intacct_object_id
|
95
|
+
"#{intacct_invoice_prefix}#{object.invoice.id}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def invoice_xml xml
|
99
|
+
xml.customerid "#{object.customer.intacct_system_id}"
|
100
|
+
xml.datecreated {
|
101
|
+
xml.year object.invoice.created_at.strftime("%Y")
|
102
|
+
xml.month object.invoice.created_at.strftime("%m")
|
103
|
+
xml.day object.invoice.created_at.strftime("%d")
|
104
|
+
}
|
105
|
+
|
106
|
+
termname = customer_data.termname
|
107
|
+
xml.termname termname.present?? termname : "Net 30"
|
108
|
+
|
109
|
+
xml.invoiceno intacct_object_id
|
110
|
+
run_hook :custom_invoice_fields, xml
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_intacct_system_id
|
114
|
+
object.invoice.intacct_system_id = intacct_object_id
|
115
|
+
end
|
116
|
+
|
117
|
+
def delete_intacct_system_id
|
118
|
+
object.invoice.intacct_system_id = nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def delete_intacct_key
|
122
|
+
object.invoice.intacct_key = nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def set_date_time type
|
126
|
+
if %w(create update delete).include? type
|
127
|
+
if object.invoice.respond_to? :"intacct_#{type}d_at"
|
128
|
+
object.invoice.send("intacct_#{type}d_at=", DateTime.now)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|