capsulecrm-b 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +10 -0
- data/README.rdoc +33 -0
- data/Rakefile +9 -0
- data/capsulecrm.gemspec +23 -0
- data/examples.rb +82 -0
- data/lib/capsulecrm/address.rb +23 -0
- data/lib/capsulecrm/base.rb +176 -0
- data/lib/capsulecrm/child.rb +24 -0
- data/lib/capsulecrm/child_collection.rb +14 -0
- data/lib/capsulecrm/collection.rb +14 -0
- data/lib/capsulecrm/contact.rb +27 -0
- data/lib/capsulecrm/custom_field.rb +41 -0
- data/lib/capsulecrm/email.rb +64 -0
- data/lib/capsulecrm/history.rb +32 -0
- data/lib/capsulecrm/history_item.rb +20 -0
- data/lib/capsulecrm/opportunity.rb +60 -0
- data/lib/capsulecrm/organisation.rb +41 -0
- data/lib/capsulecrm/party.rb +114 -0
- data/lib/capsulecrm/person.rb +126 -0
- data/lib/capsulecrm/phone.rb +15 -0
- data/lib/capsulecrm/record_not_found.rb +1 -0
- data/lib/capsulecrm/recorn_not_recognised.rb +1 -0
- data/lib/capsulecrm/tag.rb +12 -0
- data/lib/capsulecrm/version.rb +3 -0
- data/lib/capsulecrm/website.rb +19 -0
- data/lib/capsulecrm.rb +46 -0
- data/test/create_person_test.rb +36 -0
- data/test/fixtures/responses/create_person.yml +59 -0
- data/test/fixtures/responses/party_history.yml +45 -0
- data/test/fixtures/responses/party_tags.yml +26 -0
- data/test/fixtures/responses/person_find_all.yml +28 -0
- data/test/fixtures/responses/person_find_all_with_limit.yml +26 -0
- data/test/fixtures/responses/person_find_all_with_offset.yml +28 -0
- data/test/fixtures/responses/person_find_by_id.yml +102 -0
- data/test/fixtures/responses/update_person.yml +85 -0
- data/test/fixtures/responses/update_person_without_changes.yml +28 -0
- data/test/party_dot_find_test.rb +40 -0
- data/test/party_dot_history_test.rb +29 -0
- data/test/party_dot_tags_test.rb +30 -0
- data/test/person_dot_find_all_test.rb +48 -0
- data/test/person_dot_find_by_id_test.rb +61 -0
- data/test/test_helper.rb +52 -0
- data/test/update_person_test.rb +46 -0
- metadata +158 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
= CapsuleCRM
|
2
|
+
|
3
|
+
This is a ruby wrapper for the CapsuleCRM API. It is alpha software and should be considered a work in progress. It currently supports People & Organisations. There is support for CustomFields & Contacts but they are read-only.
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
[sudo] gem install capsulecrm
|
8
|
+
|
9
|
+
gem 'capsulecrm' # rails
|
10
|
+
require 'capsulecrm' # non-rails
|
11
|
+
|
12
|
+
== Getting started
|
13
|
+
|
14
|
+
You will need to supply the gem with your CapsuleCRM API token and account name. The account name is the first part of your CapsuleCRM url:
|
15
|
+
|
16
|
+
CapsuleCRM.account_name = "test-account" # http://test-account.capsulecrm.com
|
17
|
+
CapsuleCRM.api_token = 'MY_API_TOKEN'
|
18
|
+
CapsuleCRM.initialize!
|
19
|
+
|
20
|
+
If you're using rails, you can put that in config/initializers/capsulecrm.rb
|
21
|
+
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
Please see examples.rb for usage examples
|
26
|
+
|
27
|
+
== Feedback
|
28
|
+
|
29
|
+
Bug reports, feature requests and patches are welcome. Please create an issue on the github issue tracker.
|
30
|
+
|
31
|
+
== License
|
32
|
+
|
33
|
+
MIT License. Copyright 2011 Ahmed Adam
|
data/Rakefile
ADDED
data/capsulecrm.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "capsulecrm/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "capsulecrm-b"
|
7
|
+
s.version = CapsuleCRM::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ahmed Adam", "dsimard", "FWMatt"]
|
10
|
+
s.email = ["ahmed.msgs@gmail.com", "dsimard@azanka.ca", "matt@futureworkshops.com"]
|
11
|
+
s.homepage = "https://github.com/dsimard/capsulecrm"
|
12
|
+
s.summary = %q{CapsuleCRM API Gem}
|
13
|
+
s.description = %q{CapsuleCRM API Gem}
|
14
|
+
|
15
|
+
s.add_dependency 'httparty', '~> 0.7'
|
16
|
+
s.add_dependency 'activemodel', '~> 3.0'
|
17
|
+
s.add_dependency 'activesupport', '~> 3.0'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/examples.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# NOTE: CapsuleCRM::Person and CapsuleCRM::Organisation have virtually identically methods.
|
2
|
+
require 'capsulecrm'
|
3
|
+
|
4
|
+
# find by id
|
5
|
+
person = CapsuleCRM::Person.find 123
|
6
|
+
|
7
|
+
# or if you don't know what you are searching for Party
|
8
|
+
something = CapsuleCRM::Party.find 123
|
9
|
+
something.is?(:person)
|
10
|
+
something.is?(:organisation)
|
11
|
+
|
12
|
+
# find by email
|
13
|
+
person = CapsuleCRM::Person.find_by_email 'foo@example.com'
|
14
|
+
|
15
|
+
# find all
|
16
|
+
person = CapsuleCRM::Person.find :all, :offset => 10, :limit => 5
|
17
|
+
|
18
|
+
# find by search term (searches name, telephone number and searchable custom fields)
|
19
|
+
person = CapsuleCRM::Person.search 'fred', :offset => 10, :limit => 5
|
20
|
+
|
21
|
+
# CapsuleCRM::Person attributes
|
22
|
+
person.id
|
23
|
+
person.title
|
24
|
+
person.first_name
|
25
|
+
person.last_name
|
26
|
+
person.job_title
|
27
|
+
person.about
|
28
|
+
|
29
|
+
# Add a note to the history
|
30
|
+
person.add_history "This is a note"
|
31
|
+
|
32
|
+
# update a person object
|
33
|
+
person.first_name = "Homer"
|
34
|
+
person.last_name = "Simpson"
|
35
|
+
person.save # returns true/false
|
36
|
+
|
37
|
+
# create a person object
|
38
|
+
person = CapsuleCRM::Person.new
|
39
|
+
person.first_name = "Marge"
|
40
|
+
person.last_name = "Simpson"
|
41
|
+
person.save # returns true/false
|
42
|
+
|
43
|
+
|
44
|
+
# get the person's organisation
|
45
|
+
person.organisation # CapsuleCRM::Organisation
|
46
|
+
|
47
|
+
# CapsuleCRM::Organisation attributes:
|
48
|
+
organisation.about
|
49
|
+
organisation.name
|
50
|
+
|
51
|
+
|
52
|
+
# Contacts: CapsuleCRM::Phone (read-only)
|
53
|
+
person.phone_numbers # CapsuleCRM::Collection
|
54
|
+
person.phone_numbers.first.number # 01234 56789
|
55
|
+
person.phone_numbers.first.type # work
|
56
|
+
|
57
|
+
# Contacts: CapsuleCRM::Website (read-only)
|
58
|
+
party.websites # CapsuleCRM::Collection
|
59
|
+
party.websites.first.url # http://google.com
|
60
|
+
party.websites.first.web_address # http://google.com
|
61
|
+
|
62
|
+
# Contacts: CapsuleCRM::Email (read-only)
|
63
|
+
person.emails # CapsuleCRM::Collection
|
64
|
+
person.emails.first.address # 'foo@example.com'
|
65
|
+
person.emails.first.type # 'home'
|
66
|
+
|
67
|
+
# Contacts: CapsuleCRM::Address (read-only)
|
68
|
+
person.addresses # CapsuleCRM::Collection
|
69
|
+
person.addresses.first.street # 10 Somestreet
|
70
|
+
person.addresses.first.city # Manchester
|
71
|
+
person.addresses.first.state # Greater Manchester
|
72
|
+
person.addresses.first.zip # ME10 7TR
|
73
|
+
person.addresses.first.country # United Kingdom
|
74
|
+
|
75
|
+
# CapsuleCRM::CustomFields (read-only)
|
76
|
+
person.custom_fields # CapsuleCRM::Collection
|
77
|
+
person.custom_fields.first.label # 'Favourite colour'
|
78
|
+
person.custom_fields.first.value # 'Blue'
|
79
|
+
|
80
|
+
# CapsuleCRM::Tag (read-only)
|
81
|
+
person.tags # CapsuleCRM::Collection
|
82
|
+
person.tag_names # ["array", "of all", "tags", "as strings"]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CapsuleCRM::Address < CapsuleCRM::Contact
|
2
|
+
|
3
|
+
attr_accessor :street
|
4
|
+
attr_accessor :city
|
5
|
+
attr_accessor :state
|
6
|
+
attr_accessor :zip
|
7
|
+
attr_accessor :country
|
8
|
+
|
9
|
+
|
10
|
+
# nodoc
|
11
|
+
def self.xml_map
|
12
|
+
map = {
|
13
|
+
'street' => 'street',
|
14
|
+
'city' => 'city',
|
15
|
+
'state' => 'state',
|
16
|
+
'zip' => 'zip',
|
17
|
+
'country' => 'country'
|
18
|
+
}
|
19
|
+
super.merge map
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module CapsuleCRM
|
2
|
+
class Base
|
3
|
+
include HTTParty
|
4
|
+
include ActiveModel::Dirty
|
5
|
+
|
6
|
+
|
7
|
+
# This is needed for PUT and POST requests because the capsule API returns html
|
8
|
+
# if the request is unsuccessful. HTTParty's default xlm parser crashes if fed
|
9
|
+
# html so this parser is used for PUT/POST
|
10
|
+
class Parser::Simple < HTTParty::Parser
|
11
|
+
def parse; body; end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# -- Attributes --
|
16
|
+
attr_accessor :id
|
17
|
+
attr_accessor :raw_data
|
18
|
+
@@last_response = nil
|
19
|
+
|
20
|
+
|
21
|
+
# -- HttpParty --
|
22
|
+
format :xml
|
23
|
+
headers 'User-Agent' => 'CapsuleCRM ruby gem'
|
24
|
+
|
25
|
+
|
26
|
+
# nodoc
|
27
|
+
def initialize(attributes={})
|
28
|
+
attributes.each do |name, value|
|
29
|
+
send("#{name}=", value)
|
30
|
+
end
|
31
|
+
changed_attributes.clear
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# nodoc
|
36
|
+
def ==(other)
|
37
|
+
return false if other.nil?
|
38
|
+
id == other.id
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# nodoc
|
43
|
+
def id
|
44
|
+
return nil if @id.nil?
|
45
|
+
@id.to_i
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
# nodoc
|
50
|
+
def errors
|
51
|
+
@errors ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# nodoc
|
56
|
+
def new_record?
|
57
|
+
id.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# -- Class Methods --
|
62
|
+
|
63
|
+
|
64
|
+
# nodoc
|
65
|
+
def self.find(what, options={})
|
66
|
+
return find_all(options) if what == :all
|
67
|
+
find_one(what)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# for debugging
|
72
|
+
def self.last_response
|
73
|
+
@@last_response
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
|
80
|
+
# uses xml_map() to convert the xml hash from response.data into an attributes hash
|
81
|
+
def self.attributes_from_xml_hash(hash)
|
82
|
+
attributes = {:raw_data => hash}
|
83
|
+
xml_map.each { |k,v| attributes[v] = hash[k] }
|
84
|
+
attributes
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# uses xml_map() to convert :attributes into an xml string
|
89
|
+
def self.attributes_to_xml(attributes, root=nil)
|
90
|
+
xml = {}
|
91
|
+
map = xml_map.invert
|
92
|
+
attributes.each do |k,v|
|
93
|
+
key = map[k.to_s]
|
94
|
+
xml[key] = v
|
95
|
+
end
|
96
|
+
xml.to_xml :root => root
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
# nodoc
|
101
|
+
def self.find_one(id)
|
102
|
+
path = get_path + '/' + id.to_s
|
103
|
+
@@last_response = get(path)
|
104
|
+
raise_404(id) if last_response.code == 404
|
105
|
+
init_one(last_response)
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# nodoc
|
110
|
+
def self.find_all(options={}, path=nil)
|
111
|
+
path ||= get_path
|
112
|
+
params = query_params(options)
|
113
|
+
@@last_response = get(path, :query => params)
|
114
|
+
init_many(last_response)
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
# over-ride in sub-classes
|
119
|
+
def self.get_path
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# capsule API uses :start. :offset is prob more familiar to ruby/active_record users
|
124
|
+
def self.query_params(options)
|
125
|
+
params = options.dup
|
126
|
+
params[:start] = params.delete(:offset) if params.has_key?(:offset)
|
127
|
+
params
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# nodoc
|
132
|
+
def self.raise_404(id)
|
133
|
+
err = "Could not find #{name} with id #{id}"
|
134
|
+
raise CapsuleCRM::RecordNotFound, err
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# creates a new object, and returns the ID
|
139
|
+
# returns false if something went wrong (use last_response() to debug)
|
140
|
+
def self.create(attributes, options={})
|
141
|
+
return false if attributes.empty?
|
142
|
+
xml = attributes_to_xml(attributes, options.delete(:root))
|
143
|
+
@@last_response = post options[:path], xml_request_options(xml)
|
144
|
+
return false unless last_response.code == 201
|
145
|
+
last_response.headers['location'].split('/').last
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
# updates an object with the given id, returns true on success, false
|
150
|
+
# on failure.
|
151
|
+
def self.update(id, attributes, options={})
|
152
|
+
return true if attributes.empty?
|
153
|
+
xml = attributes_to_xml(attributes, options.delete(:root))
|
154
|
+
@@last_response = put options[:path], xml_request_options(xml)
|
155
|
+
last_response.code == 200
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
# nodoc
|
160
|
+
def self.xml_map
|
161
|
+
{'id' => 'id'}
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# needed for PUT and POST operations
|
166
|
+
def self.xml_request_options(xml)
|
167
|
+
options = {
|
168
|
+
:body => xml,
|
169
|
+
:headers => {'Content-Type' => 'text/xml'},
|
170
|
+
:parser => Parser::Simple
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class CapsuleCRM::Child < CapsuleCRM::Base
|
2
|
+
attr_accessor :parent
|
3
|
+
|
4
|
+
|
5
|
+
# nodoc
|
6
|
+
def initialize(parent, attributes={})
|
7
|
+
@parent = parent
|
8
|
+
super(attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# nodoc
|
13
|
+
def self.init_many(parent, data)
|
14
|
+
CapsuleCRM::ChildCollection.new(parent, self, data)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# nodoc
|
19
|
+
def self.init_one(parent, data)
|
20
|
+
new(parent, attributes_from_xml_map(data))
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CapsuleCRM::ChildCollection < CapsuleCRM::Collection
|
2
|
+
|
3
|
+
|
4
|
+
# nodoc
|
5
|
+
def initialize(parent, klass, data)
|
6
|
+
return if data.nil?
|
7
|
+
[data].flatten.each do |attributes|
|
8
|
+
attributes = klass.attributes_from_xml_hash(attributes)
|
9
|
+
self.push klass.new(parent, attributes)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CapsuleCRM::Collection < Array
|
2
|
+
|
3
|
+
|
4
|
+
# nodoc
|
5
|
+
def initialize(klass, data)
|
6
|
+
return if data.nil?
|
7
|
+
[data].flatten.each do |attributes|
|
8
|
+
attributes = klass.attributes_from_xml_hash(attributes)
|
9
|
+
self.push klass.new(attributes)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CapsuleCRM::Contact < CapsuleCRM::Child
|
2
|
+
|
3
|
+
attr_accessor :type
|
4
|
+
define_attribute_methods [:type]
|
5
|
+
|
6
|
+
# nodoc
|
7
|
+
def attributes
|
8
|
+
attrs = {}
|
9
|
+
arr = [:type]
|
10
|
+
arr.each do |key|
|
11
|
+
attrs[key] = self.send(key)
|
12
|
+
end
|
13
|
+
attrs
|
14
|
+
end
|
15
|
+
|
16
|
+
# nodoc
|
17
|
+
def type=(value)
|
18
|
+
type_will_change! unless value == type
|
19
|
+
@type = value
|
20
|
+
end
|
21
|
+
|
22
|
+
# nodoc
|
23
|
+
def self.xml_map
|
24
|
+
map = {'type' => 'type'}
|
25
|
+
super.merge map
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class CapsuleCRM::CustomField < CapsuleCRM::Child
|
2
|
+
|
3
|
+
attr_accessor :boolean
|
4
|
+
attr_accessor :date
|
5
|
+
attr_accessor :label
|
6
|
+
attr_accessor :text
|
7
|
+
|
8
|
+
|
9
|
+
# nodoc
|
10
|
+
def boolean=(value)
|
11
|
+
return @boolean = true if value.to_s == 'true'
|
12
|
+
@boolean = false
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# nodoc
|
17
|
+
def date=(value)
|
18
|
+
value = Time.parse(value) if value.is_a?(String)
|
19
|
+
@date = value
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# nodoc
|
24
|
+
def value
|
25
|
+
date || text || boolean
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# nodoc
|
30
|
+
def self.xml_map
|
31
|
+
map = {
|
32
|
+
'label' => 'label',
|
33
|
+
'text' => 'text',
|
34
|
+
'date' => 'date',
|
35
|
+
'boolean' => 'boolean'
|
36
|
+
}
|
37
|
+
super.merge map
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class CapsuleCRM::Email < CapsuleCRM::Contact
|
2
|
+
|
3
|
+
attr_accessor :address
|
4
|
+
define_attribute_methods [:address]
|
5
|
+
|
6
|
+
|
7
|
+
# nodoc
|
8
|
+
def attributes
|
9
|
+
attrs = {}
|
10
|
+
arr = [:address, :type]
|
11
|
+
arr.each do |key|
|
12
|
+
attrs[key] = self.send(key)
|
13
|
+
end
|
14
|
+
attrs
|
15
|
+
end
|
16
|
+
|
17
|
+
# nodoc
|
18
|
+
def address=(value)
|
19
|
+
address_will_change! unless value == address
|
20
|
+
@address = value
|
21
|
+
end
|
22
|
+
|
23
|
+
# nodoc
|
24
|
+
def dirty_attributes
|
25
|
+
Hash[attributes.select { |k,v| changed.include? k.to_s }]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parent_type
|
29
|
+
return "person" if self.parent.class == CapsuleCRM::Person
|
30
|
+
return "organisation" if self.parent.class == CapsuleCRM::Organisation
|
31
|
+
raise "Unknown Parent Type"
|
32
|
+
end
|
33
|
+
|
34
|
+
# nodoc
|
35
|
+
def save
|
36
|
+
path = ["", "api", parent_type, self.parent.id].join("/")
|
37
|
+
options = {:root => 'person', :path => path}
|
38
|
+
attrs = new_record?? attributes : {:id => id}.merge(dirty_attributes)
|
39
|
+
success = self.class.update id, attrs, options
|
40
|
+
changed_attributes.clear if success
|
41
|
+
success
|
42
|
+
end
|
43
|
+
|
44
|
+
# uses xml_map() to convert :attributes into an xml string
|
45
|
+
def self.attributes_to_xml(attributes, root=nil)
|
46
|
+
xml = {"contacts" => {"email" => {}}}
|
47
|
+
map = xml_map.invert
|
48
|
+
attributes.each do |k,v|
|
49
|
+
key = map[k.to_s]
|
50
|
+
xml["contacts"]["email"][key] = v
|
51
|
+
end
|
52
|
+
xml.to_xml :root => root
|
53
|
+
end
|
54
|
+
|
55
|
+
# nodoc
|
56
|
+
def self.xml_map
|
57
|
+
map = {
|
58
|
+
'emailAddress' => 'address'
|
59
|
+
}
|
60
|
+
super.merge map
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CapsuleCRM::History
|
2
|
+
# Reload history
|
3
|
+
def history!
|
4
|
+
@history = nil
|
5
|
+
history
|
6
|
+
end
|
7
|
+
|
8
|
+
# Load history if not loaded
|
9
|
+
def history
|
10
|
+
return @history if @history
|
11
|
+
|
12
|
+
path = self.class.get_path
|
13
|
+
path = [path, id, 'history'].join '/'
|
14
|
+
last_response = self.class.get(path)
|
15
|
+
data = last_response['history'].try(:[], 'historyItem')
|
16
|
+
@history = CapsuleCRM::HistoryItem.init_many(self, data)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def add_history(note)
|
21
|
+
if note
|
22
|
+
path = [self.class.get_path, self.id, 'history'].join '/'
|
23
|
+
self.class.create(Hash[{:note => note}], {:root => 'historyItem', :path => path})
|
24
|
+
|
25
|
+
# TODO : Should be optimized so it doesn't reload history each time
|
26
|
+
@history = nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :add_note :add_history
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class CapsuleCRM::HistoryItem < CapsuleCRM::Child
|
2
|
+
attr_reader :id
|
3
|
+
attr_accessor :type
|
4
|
+
attr_accessor :entry_date
|
5
|
+
attr_accessor :creator
|
6
|
+
attr_accessor :subject
|
7
|
+
attr_accessor :note
|
8
|
+
|
9
|
+
def self.xml_map
|
10
|
+
map = {
|
11
|
+
"id" => 'id',
|
12
|
+
"type" => 'type',
|
13
|
+
"entryDate" => 'entry_date',
|
14
|
+
"creator" => 'creator',
|
15
|
+
"subject" => 'subject',
|
16
|
+
"note" => 'note'
|
17
|
+
}
|
18
|
+
super.merge map
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class CapsuleCRM::Opportunity < CapsuleCRM::Base
|
2
|
+
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :party_id
|
5
|
+
attr_accessor :currency
|
6
|
+
attr_accessor :value
|
7
|
+
attr_accessor :duration_basis
|
8
|
+
attr_accessor :duration
|
9
|
+
attr_accessor :expected_close_date
|
10
|
+
attr_accessor :actual_close_date
|
11
|
+
attr_accessor :milestone
|
12
|
+
attr_accessor :updated_on
|
13
|
+
attr_accessor :created_on
|
14
|
+
|
15
|
+
# nodoc
|
16
|
+
def self.xml_map
|
17
|
+
map = {
|
18
|
+
'name' => 'name',
|
19
|
+
'partyId' => 'party_id',
|
20
|
+
'currency' => 'currency',
|
21
|
+
'value' => 'value',
|
22
|
+
'durationBasis' => 'duration_basis',
|
23
|
+
'expectedCloseDate' => 'expected_close_date',
|
24
|
+
'actualCloseDate' => 'actual_close_date',
|
25
|
+
'milestone' => 'milestone',
|
26
|
+
'updatedOn' => 'updated_on',
|
27
|
+
'createdOn' => 'created_on',
|
28
|
+
}
|
29
|
+
super.merge map
|
30
|
+
end
|
31
|
+
|
32
|
+
def all
|
33
|
+
return @opportunities if @opportunities
|
34
|
+
path = self.class.get_path
|
35
|
+
last_response = self.class.get(path)
|
36
|
+
@opportunities = CapsuleCRM::Opportunity.init_many(last_response)
|
37
|
+
end
|
38
|
+
|
39
|
+
# nodoc
|
40
|
+
def self.get_path
|
41
|
+
'/api/opportunity'
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# nodoc
|
46
|
+
def self.init_many(response)
|
47
|
+
data = response['opportunities']['opportunity']
|
48
|
+
CapsuleCRM::Collection.new(self, data)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# nodoc
|
53
|
+
def self.init_one(response)
|
54
|
+
data = response['opportunity']
|
55
|
+
new(attributes_from_xml_hash(data))
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
end
|