active_zuora 1.3.0 → 2.6.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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.octopolo.yml +4 -0
- data/.soyuz.yml +13 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.markdown +41 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +191 -0
- data/Rakefile +9 -53
- data/TODO.md +2 -0
- data/active_zuora.gemspec +25 -59
- data/lib/active_zuora.rb +44 -12
- data/lib/active_zuora/amend.rb +43 -0
- data/lib/active_zuora/base.rb +84 -0
- data/lib/active_zuora/batch_subscribe.rb +53 -0
- data/lib/active_zuora/belongs_to_associations.rb +56 -0
- data/lib/active_zuora/billing_preview.rb +49 -0
- data/lib/active_zuora/collection_proxy.rb +38 -0
- data/lib/active_zuora/connection.rb +47 -0
- data/lib/active_zuora/fields.rb +129 -0
- data/lib/active_zuora/fields/array_field_decorator.rb +28 -0
- data/lib/active_zuora/fields/boolean_field.rb +12 -0
- data/lib/active_zuora/fields/date_field.rb +18 -0
- data/lib/active_zuora/fields/date_time_field.rb +19 -0
- data/lib/active_zuora/fields/decimal_field.rb +12 -0
- data/lib/active_zuora/fields/field.rb +76 -0
- data/lib/active_zuora/fields/integer_field.rb +11 -0
- data/lib/active_zuora/fields/object_field.rb +31 -0
- data/lib/active_zuora/fields/string_field.rb +11 -0
- data/lib/active_zuora/generate.rb +43 -0
- data/lib/active_zuora/generator.rb +244 -0
- data/lib/active_zuora/has_many_associations.rb +37 -0
- data/lib/active_zuora/has_many_proxy.rb +50 -0
- data/lib/active_zuora/lazy_attr.rb +52 -0
- data/lib/active_zuora/persistence.rb +172 -0
- data/lib/active_zuora/relation.rb +260 -0
- data/lib/active_zuora/scoping.rb +50 -0
- data/lib/active_zuora/subscribe.rb +42 -0
- data/lib/active_zuora/version.rb +3 -0
- data/lib/active_zuora/z_object.rb +21 -0
- data/spec/account_integration_spec.rb +41 -0
- data/spec/base_spec.rb +39 -0
- data/spec/belongs_to_associations_spec.rb +35 -0
- data/spec/collection_proxy_spec.rb +28 -0
- data/spec/connection_spec.rb +66 -0
- data/spec/fields/date_field_spec.rb +35 -0
- data/spec/has_many_integration_spec.rb +53 -0
- data/spec/lazy_attr_spec.rb +22 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/subscribe_integration_spec.rb +344 -0
- data/spec/zobject_integration_spec.rb +104 -0
- data/wsdl/zuora.wsdl +1548 -0
- metadata +141 -53
- data/LICENSE +0 -202
- data/README.rdoc +0 -15
- data/VERSION +0 -1
- data/custom_fields.yml +0 -17
- data/lib/zuora/ZUORA.rb +0 -1398
- data/lib/zuora/ZUORADriver.rb +0 -128
- data/lib/zuora/ZUORAMappingRegistry.rb +0 -1488
- data/lib/zuora/ZuoraServiceClient.rb +0 -124
- data/lib/zuora/account.rb +0 -4
- data/lib/zuora/api.rb +0 -18
- data/lib/zuora/contact.rb +0 -4
- data/lib/zuora/rate_plan.rb +0 -4
- data/lib/zuora/rate_plan_data.rb +0 -4
- data/lib/zuora/subscribe_options.rb +0 -4
- data/lib/zuora/subscribe_request.rb +0 -4
- data/lib/zuora/subscribe_with_existing_account_request.rb +0 -4
- data/lib/zuora/subscription.rb +0 -4
- data/lib/zuora/subscription_data.rb +0 -4
- data/lib/zuora/zobject.rb +0 -52
- data/lib/zuora_client.rb +0 -181
- data/lib/zuora_interface.rb +0 -199
@@ -0,0 +1,37 @@
|
|
1
|
+
module ActiveZuora
|
2
|
+
module HasManyAssociations
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
|
8
|
+
def has_many(items, options={})
|
9
|
+
class_name = options[:class_name] || nested_class_name(items.to_s.singularize.camelize)
|
10
|
+
foreign_key = options[:foreign_key] || :"#{zuora_object_name.underscore}_id"
|
11
|
+
conditions = options[:conditions]
|
12
|
+
# :order => :name
|
13
|
+
# :order => [:name, :desc]
|
14
|
+
order_attribute, order_direction = [*options[:order]]
|
15
|
+
ivar = "@#{items}"
|
16
|
+
# Define the methods on an included module, so we can override
|
17
|
+
# them using super.
|
18
|
+
generated_attribute_methods.module_eval do
|
19
|
+
define_method(items) do
|
20
|
+
if instance_variable_get(ivar)
|
21
|
+
return instance_variable_get(ivar)
|
22
|
+
else
|
23
|
+
relation = class_name.constantize.where(foreign_key => self.id)
|
24
|
+
relation = relation.merge(conditions) if conditions.present?
|
25
|
+
relation.order_attribute = order_attribute if order_attribute.present?
|
26
|
+
relation.order_direction = order_direction if order_direction.present?
|
27
|
+
proxy = HasManyProxy.new(self, relation, options)
|
28
|
+
instance_variable_set(ivar, proxy)
|
29
|
+
proxy
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActiveZuora
|
2
|
+
class HasManyProxy
|
3
|
+
|
4
|
+
# Wraps around the Relation representing a has_many association
|
5
|
+
# to add features like inverse_of loading.
|
6
|
+
|
7
|
+
attr_reader :scope, :owner
|
8
|
+
|
9
|
+
delegate :"==", :"===", :"=~", :inspect, :to_s, :to => :to_a
|
10
|
+
|
11
|
+
def initialize(owner, scope, options={})
|
12
|
+
@owner, @scope = owner, scope
|
13
|
+
# inverse_of by default. You can opt out with :inverse_of => false
|
14
|
+
@inverse_of = (options[:inverse_of] || owner.zuora_object_name.underscore) unless options[:inverse_of] == false
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
if @scope.loaded? || !@inverse_of
|
19
|
+
@scope.to_a
|
20
|
+
else
|
21
|
+
@scope.to_a.each { |record| record.send("#{@inverse_of}=", owner) }
|
22
|
+
@scope.to_a
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
alias :all :to_a
|
27
|
+
|
28
|
+
def reload
|
29
|
+
# If reload is called directly on the scope, it will reload
|
30
|
+
# without our extra functionality, like inverse_of loading.
|
31
|
+
@scope.unload
|
32
|
+
to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def method_missing(method, *args, &block)
|
38
|
+
# If we do anything that needs loading the scope, then we'll load it.
|
39
|
+
if Array.method_defined?(method)
|
40
|
+
to_a.send(method, *args, &block)
|
41
|
+
else
|
42
|
+
# Otherwise send all messages to the @scope.
|
43
|
+
@scope.send(method, *args, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveZuora
|
2
|
+
module LazyAttr
|
3
|
+
|
4
|
+
# This is meant to be included onto an Invoice class.
|
5
|
+
# Returns true/false on success.
|
6
|
+
# Result hash is stored in #result.
|
7
|
+
# If success, the id will be set in the object.
|
8
|
+
# If failure, errors will be present on object.
|
9
|
+
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
include Base
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def fetch_field(field_name)
|
18
|
+
return nil unless self.id
|
19
|
+
|
20
|
+
records = fetch_field_records("select #{self.class.get_field!(field_name).zuora_name} from #{zuora_object_name} where Id = '#{self.id}'")
|
21
|
+
type_cast_fetched_field(field_name, records.nil? ? nil : records[field_name.to_sym])
|
22
|
+
end
|
23
|
+
private :fetch_field
|
24
|
+
|
25
|
+
def fetch_field_records(query_string)
|
26
|
+
response = self.class.connection.request(:query){ |soap| soap.body = { :query_string => query_string } }
|
27
|
+
response[:query_response][:result][:records]
|
28
|
+
end
|
29
|
+
private :fetch_field_records
|
30
|
+
|
31
|
+
def type_cast_fetched_field(field_name, value)
|
32
|
+
get_field!(field_name).type_cast(value)
|
33
|
+
end
|
34
|
+
private :type_cast_fetched_field
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
def lazy_load(*field_names)
|
38
|
+
Array(field_names).map(&:to_sym).each do |field_name|
|
39
|
+
define_lazy_field field_name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_lazy_field(field)
|
44
|
+
instance_eval do
|
45
|
+
define_method field do
|
46
|
+
instance_variable_get("@#{field}") || instance_variable_set("@#{field}", fetch_field(field))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module ActiveZuora
|
2
|
+
module Persistence
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
MAX_BATCH_SIZE = 50
|
7
|
+
|
8
|
+
def new_record?
|
9
|
+
id.blank?
|
10
|
+
end
|
11
|
+
|
12
|
+
def save
|
13
|
+
new_record? ? create : update
|
14
|
+
end
|
15
|
+
|
16
|
+
def save!
|
17
|
+
raise "Could Not Save Zuora Object: #{errors.full_messages.join ', '}" unless save
|
18
|
+
end
|
19
|
+
|
20
|
+
def update_attributes(attributes)
|
21
|
+
self.attributes = attributes
|
22
|
+
save
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_attributes!(attributes)
|
26
|
+
self.attributes = attributes
|
27
|
+
save!
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete
|
31
|
+
self.class.delete(id) > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def reload
|
35
|
+
raise ArgumentError.new("You can't reload a new record") if new_record?
|
36
|
+
self.untracked_attributes = self.class.find(id).attributes
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def xml_field_names
|
41
|
+
# If we're rendering an existing record, always include the id.
|
42
|
+
new_record? ? super : ([:id] + super).uniq
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def create
|
48
|
+
return false unless new_record? && valid?
|
49
|
+
result = self.class.connection.request(:create) do |soap|
|
50
|
+
soap.body do |xml|
|
51
|
+
build_xml(xml, soap,
|
52
|
+
:namespace => soap.namespace,
|
53
|
+
:element_name => :zObjects,
|
54
|
+
:force_type => true)
|
55
|
+
end
|
56
|
+
end[:create_response][:result]
|
57
|
+
if result[:success]
|
58
|
+
self.id = result[:id]
|
59
|
+
clear_changed_attributes
|
60
|
+
true
|
61
|
+
else
|
62
|
+
errors.add(:base, result[:errors][:message]) if result[:errors]
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def update
|
68
|
+
self.class.update(self)
|
69
|
+
self.errors.blank?
|
70
|
+
end
|
71
|
+
|
72
|
+
module ClassMethods
|
73
|
+
|
74
|
+
def create(attributes={})
|
75
|
+
new(attributes).tap(&:save)
|
76
|
+
end
|
77
|
+
|
78
|
+
def create!(attributes={})
|
79
|
+
new(attributes).tap(&:save!)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Takes an array of zobjects and batch saves new and updated records separately
|
83
|
+
def save(*zobjects)
|
84
|
+
new_records = 0
|
85
|
+
updated_records = 0
|
86
|
+
|
87
|
+
# Get all new objects
|
88
|
+
new_objects = zobjects.flatten.select do |zobject|
|
89
|
+
zobject.new_record? && zobject.changed.present? && zobject.valid?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get all updated objects
|
93
|
+
updated_objects = zobjects.flatten.select do |zobject|
|
94
|
+
!zobject.new_record? && zobject.changed.present? && zobject.valid?
|
95
|
+
end
|
96
|
+
|
97
|
+
# Make calls in batches of 50
|
98
|
+
new_objects.each_slice(MAX_BATCH_SIZE) do |batch|
|
99
|
+
new_records += process_save(batch, :create)
|
100
|
+
end
|
101
|
+
|
102
|
+
updated_objects.each_slice(MAX_BATCH_SIZE) do |batch|
|
103
|
+
updated_records += process_save(batch, :update)
|
104
|
+
end
|
105
|
+
|
106
|
+
new_records + updated_records
|
107
|
+
end
|
108
|
+
|
109
|
+
# For backwards compatability
|
110
|
+
def update(*zobjects)
|
111
|
+
save(zobjects)
|
112
|
+
end
|
113
|
+
|
114
|
+
def process_save(zobjects, action)
|
115
|
+
unless [:create, :update].include? action
|
116
|
+
raise "Invalid action type for saving. Must be create or update."
|
117
|
+
end
|
118
|
+
|
119
|
+
return 0 if zobjects.empty?
|
120
|
+
|
121
|
+
results = connection.request(action) do |soap|
|
122
|
+
soap.body do |xml|
|
123
|
+
zobjects.map do |zobject|
|
124
|
+
zobject.build_xml(xml, soap,
|
125
|
+
:namespace => soap.namespace,
|
126
|
+
:element_name => :zObjects,
|
127
|
+
:force_type => true,
|
128
|
+
:nil_strategy => :fields_to_null)
|
129
|
+
end.last
|
130
|
+
end
|
131
|
+
end["#{action.to_s}_response".to_sym][:result]
|
132
|
+
|
133
|
+
results = [results] unless results.is_a?(Array)
|
134
|
+
zobjects.zip(results).each do |zobject, result|
|
135
|
+
if result[:success]
|
136
|
+
zobject.clear_changed_attributes
|
137
|
+
else
|
138
|
+
zobject.add_zuora_errors result[:errors]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Return the count of updates that succeeded.
|
143
|
+
results.select{ |result| result[:success] }.size
|
144
|
+
end
|
145
|
+
|
146
|
+
def delete(*ids)
|
147
|
+
ids.flatten!
|
148
|
+
deleted_records = 0
|
149
|
+
ids.each_slice(MAX_BATCH_SIZE) do |batch|
|
150
|
+
deleted_records += process_delete(batch)
|
151
|
+
end
|
152
|
+
deleted_records
|
153
|
+
end
|
154
|
+
|
155
|
+
def process_delete(*ids)
|
156
|
+
ids.flatten!
|
157
|
+
results = connection.request(:delete) do |soap|
|
158
|
+
qualifier = soap.namespace_by_uri(soap.namespace)
|
159
|
+
soap.body do |xml|
|
160
|
+
xml.tag!(qualifier, :type, zuora_object_name)
|
161
|
+
ids.map { |id| xml.tag!(qualifier, :ids, id) }.last
|
162
|
+
end
|
163
|
+
end[:delete_response][:result]
|
164
|
+
results = [results] unless results.is_a?(Array)
|
165
|
+
# Return the count of deletes that succeeded.
|
166
|
+
results.select{ |result| result[:success] }.size
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
module ActiveZuora
|
2
|
+
class Relation
|
3
|
+
|
4
|
+
attr_accessor :selected_field_names, :filters, :order_attribute, :order_direction
|
5
|
+
|
6
|
+
attr_reader :zobject_class
|
7
|
+
|
8
|
+
def initialize(zobject_class, selected_field_names=[:id])
|
9
|
+
@zobject_class, @selected_field_names, @filters = zobject_class, selected_field_names, []
|
10
|
+
|
11
|
+
if field?(:created_date)
|
12
|
+
@order_attribute, @order_direction = :created_date, :asc
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def dup
|
17
|
+
dup = super
|
18
|
+
dup.selected_field_names = dup.selected_field_names.dup
|
19
|
+
dup.filters = dup.filters.dup
|
20
|
+
dup.unload
|
21
|
+
dup
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Conditions / Selecting
|
26
|
+
#
|
27
|
+
|
28
|
+
def select(*field_names)
|
29
|
+
dup.tap { |dup| dup.selected_field_names = field_names.flatten }
|
30
|
+
end
|
31
|
+
|
32
|
+
def where(conditions)
|
33
|
+
dup.tap { |dup| dup.filters << ['and', conditions] }
|
34
|
+
end
|
35
|
+
|
36
|
+
alias :and :where
|
37
|
+
|
38
|
+
def or(conditions)
|
39
|
+
dup.tap { |dup| dup.filters << ['or', conditions] }\
|
40
|
+
end
|
41
|
+
|
42
|
+
def order(attribute, direction = :asc)
|
43
|
+
dup.tap do |dup|
|
44
|
+
dup.order_attribute = attribute
|
45
|
+
dup.order_direction = direction
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def scoped
|
50
|
+
# Account.select(:id).where(:status => "Draft") do
|
51
|
+
# Account.all # => select id from Account where status = "Draft"
|
52
|
+
# end
|
53
|
+
previous_scope, zobject_class.current_scope = zobject_class.current_scope, self
|
54
|
+
yield
|
55
|
+
ensure
|
56
|
+
zobject_class.current_scope = previous_scope
|
57
|
+
end
|
58
|
+
|
59
|
+
def merge(relation)
|
60
|
+
if relation.is_a?(Hash)
|
61
|
+
where(relation)
|
62
|
+
else
|
63
|
+
dup.tap do |dup|
|
64
|
+
dup.filters.concat relation.filters
|
65
|
+
dup.filters.uniq!
|
66
|
+
dup.order_attribute = relation.order_attribute
|
67
|
+
dup.order_direction = relation.order_direction
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Finding / Loading
|
74
|
+
#
|
75
|
+
|
76
|
+
def to_zql
|
77
|
+
select_statement + " from " + zobject_class.zuora_object_name + " " + where_statement
|
78
|
+
end
|
79
|
+
|
80
|
+
def find(id)
|
81
|
+
return nil if id.blank?
|
82
|
+
where(:id => id).first
|
83
|
+
end
|
84
|
+
|
85
|
+
def find_each(&block)
|
86
|
+
# Iterate through each item, but don't save the results in memory.
|
87
|
+
if loaded?
|
88
|
+
# If we're already loaded, iterate through the cached records.
|
89
|
+
to_a.each(&block)
|
90
|
+
else
|
91
|
+
query.each(&block)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_a
|
96
|
+
@records ||= query
|
97
|
+
end
|
98
|
+
|
99
|
+
alias :all :to_a
|
100
|
+
|
101
|
+
def loaded?
|
102
|
+
!@records.nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
def unload
|
106
|
+
@records = nil
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def reload
|
111
|
+
unload.to_a
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
def query(&block)
|
116
|
+
# Keep querying until all pages are retrieved.
|
117
|
+
# Throws an exception for an invalid query.
|
118
|
+
response = zobject_class.connection.request(:query){ |soap| soap.body = { :query_string => to_zql } }
|
119
|
+
query_response = response[:query_response]
|
120
|
+
records = objectify_query_results(query_response[:result][:records])
|
121
|
+
records.each(&:block) if block_given?
|
122
|
+
# If there are more pages of records, keep fetching
|
123
|
+
# them until done.
|
124
|
+
until query_response[:result][:done]
|
125
|
+
query_response = zobject_class.connection.request(:query_more) do |soap|
|
126
|
+
soap.body = { :query_locator => query_response[:result][:query_locator] }
|
127
|
+
end[:query_more_response]
|
128
|
+
more_records = objectify_query_results(query_response[:result][:records])
|
129
|
+
more_records.each(&:block) if block_given?
|
130
|
+
records.concat more_records
|
131
|
+
end
|
132
|
+
sort_records!(records)
|
133
|
+
rescue Savon::SOAP::Fault => exception
|
134
|
+
# Add the zql to the exception message and re-raise.
|
135
|
+
exception.message << ": #{to_zql}"
|
136
|
+
raise
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Updating / Deleting
|
141
|
+
#
|
142
|
+
|
143
|
+
def update_all(attributes={})
|
144
|
+
# Update using an attribute hash, or you can pass a block
|
145
|
+
# and update the attributes directly on the objects.
|
146
|
+
if block_given?
|
147
|
+
to_a.each { |record| yield record }
|
148
|
+
else
|
149
|
+
to_a.each { |record| record.attributes = attributes }
|
150
|
+
end
|
151
|
+
zobject_class.update(to_a)
|
152
|
+
end
|
153
|
+
|
154
|
+
def delete_all
|
155
|
+
zobject_class.delete(to_a.map(&:id))
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
|
160
|
+
def method_missing(method, *args, &block)
|
161
|
+
# This is how the chaing can happen on class methods or named scopes on the
|
162
|
+
# ZObject class.
|
163
|
+
if Array.method_defined?(method)
|
164
|
+
to_a.send(method, *args, &block)
|
165
|
+
elsif zobject_class.respond_to?(method)
|
166
|
+
scoped { zobject_class.send(method, *args, &block) }
|
167
|
+
else
|
168
|
+
super
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Helper methods to build the ZQL.
|
174
|
+
#
|
175
|
+
|
176
|
+
def select_statement
|
177
|
+
"select " + selected_field_names.map { |field_name| zuora_field_name(field_name) }.join(', ')
|
178
|
+
end
|
179
|
+
|
180
|
+
def where_statement
|
181
|
+
return '' if @filters.empty?
|
182
|
+
tokens = []
|
183
|
+
@filters.each do |logical_operator, conditions|
|
184
|
+
if conditions.is_a?(Hash)
|
185
|
+
conditions.each do |field_name, comparisons|
|
186
|
+
zuora_field_name = zuora_field_name(field_name)
|
187
|
+
comparisons = { '=' => comparisons } unless comparisons.is_a?(Hash)
|
188
|
+
comparisons.each do |operator, value|
|
189
|
+
tokens.concat [logical_operator, zuora_field_name, operator, escape_filter_value(value)]
|
190
|
+
end
|
191
|
+
end
|
192
|
+
else
|
193
|
+
tokens.concat [logical_operator, conditions.to_s]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
tokens[0] = "where"
|
197
|
+
tokens.join ' '
|
198
|
+
end
|
199
|
+
|
200
|
+
def zuora_field_name(name)
|
201
|
+
zobject_class.get_field!(name).zuora_name
|
202
|
+
end
|
203
|
+
|
204
|
+
def escape_filter_value(value)
|
205
|
+
if value.nil?
|
206
|
+
"null"
|
207
|
+
elsif value.is_a?(String)
|
208
|
+
"'#{value.gsub("'","\\\\'")}'"
|
209
|
+
elsif value.is_a?(DateTime) || value.is_a?(Time)
|
210
|
+
# If we already have a DateTime or Time, use the zone it already has.
|
211
|
+
escape_filter_value(value.strftime("%FT%T%:z")) # 2007-11-19T08:37:48-06:00
|
212
|
+
elsif value.is_a?(Date)
|
213
|
+
# Create a DateTime from the date using Zuora's timezone.
|
214
|
+
escape_filter_value(value.to_datetime.change(:offset => "+0800"))
|
215
|
+
else
|
216
|
+
value
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def objectify_query_results(results)
|
221
|
+
return [] if results.blank?
|
222
|
+
# Sometimes Zuora will return only a single record, not in an array.
|
223
|
+
results = [results] unless results.is_a?(Array)
|
224
|
+
results.map do |attributes|
|
225
|
+
# Strip any noisy attributes from the results that have to do with
|
226
|
+
# SOAP namespaces.
|
227
|
+
attributes.delete_if { |key, value| key.to_s.start_with? "@" }
|
228
|
+
# Instantiate the zobject class, but don't track the changes.
|
229
|
+
if ActiveSupport.version.to_s.to_f >= 5.2
|
230
|
+
zobject_class.new(attributes).tap { |record| record.clear_changes_information }
|
231
|
+
else
|
232
|
+
zobject_class.new(attributes).tap { |record| record.changed_attributes.clear }
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def sort_records!(records)
|
238
|
+
return records unless order_attribute.present?
|
239
|
+
records.sort! do |a, b|
|
240
|
+
if a.nil?
|
241
|
+
-1
|
242
|
+
elsif b.nil?
|
243
|
+
1
|
244
|
+
else
|
245
|
+
a.send(order_attribute) <=> b.send(order_attribute)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
records.reverse! if order_direction == :desc
|
249
|
+
records
|
250
|
+
end
|
251
|
+
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
|
260
|
+
|