ooor 2.0.3 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +1 -0
- data/bin/ooor +1 -0
- data/lib/ooor.rb +2 -0
- data/lib/ooor/associations.rb +30 -40
- data/lib/ooor/base.rb +22 -113
- data/lib/ooor/callbacks.rb +18 -0
- data/lib/ooor/field_methods.rb +133 -85
- data/lib/ooor/helpers/core_helpers.rb +9 -63
- data/lib/ooor/mini_active_resource.rb +0 -18
- data/lib/ooor/naming.rb +2 -2
- data/lib/ooor/persistence.rb +140 -0
- data/lib/ooor/railtie.rb +2 -1
- data/lib/ooor/reflection.rb +11 -399
- data/lib/ooor/reflection_ooor.rb +36 -11
- data/lib/ooor/relation.rb +48 -23
- data/lib/ooor/type_casting.rb +108 -75
- data/lib/ooor/version.rb +1 -1
- data/spec/ooor_spec.rb +133 -27
- metadata +35 -40
data/lib/ooor/reflection_ooor.rb
CHANGED
@@ -6,23 +6,29 @@ module Ooor
|
|
6
6
|
module ReflectionOoor # :nodoc:
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
|
+
def column_for_attribute(name)
|
10
|
+
self.class.columns_hash[name.to_s]
|
11
|
+
end
|
12
|
+
|
9
13
|
module ClassMethods
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def columns_hash(view_fields=nil)
|
15
|
+
if view_fields || !@t.columns_hash
|
16
|
+
view_fields ||= {}
|
17
|
+
reload_fields_definition()
|
18
|
+
@t.columns_hash ||= {}
|
19
|
+
@t.fields.each do |k, field|
|
20
|
+
unless @t.associations_keys.index(k)
|
21
|
+
@t.columns_hash[k] = field.merge({type: to_rails_type(view_fields[k] && view_fields[k]['type'] || field['type'])})
|
22
|
+
end
|
16
23
|
end
|
24
|
+
@t.columns_hash
|
25
|
+
else
|
26
|
+
@t.columns_hash
|
17
27
|
end
|
18
|
-
@t.columns_hash
|
19
|
-
end
|
20
|
-
|
21
|
-
def column_for_attribute(name)
|
22
|
-
columns_hash[name.to_s]
|
23
28
|
end
|
24
29
|
|
25
30
|
def create_reflection(name)
|
31
|
+
reload_fields_definition()
|
26
32
|
options = {}
|
27
33
|
if many2one_associations.keys.include?(name)
|
28
34
|
macro = :belongs_to
|
@@ -86,6 +92,25 @@ module Ooor
|
|
86
92
|
@klass ||= connection.class_name_from_model_key(class_name).constantize
|
87
93
|
end
|
88
94
|
|
95
|
+
def initialize(macro, name, options, active_record)
|
96
|
+
super
|
97
|
+
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns a new, unsaved instance of the associated class. +options+ will
|
101
|
+
# be passed to the class's constructor.
|
102
|
+
def build_association(*options, &block)
|
103
|
+
klass.new(*options, &block)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns whether or not this association reflection is for a collection
|
107
|
+
# association. Returns +true+ if the +macro+ is either +has_many+ or
|
108
|
+
# +has_and_belongs_to_many+, +false+ otherwise.
|
109
|
+
def collection?
|
110
|
+
@collection
|
111
|
+
end
|
112
|
+
|
113
|
+
|
89
114
|
end
|
90
115
|
|
91
116
|
end
|
data/lib/ooor/relation.rb
CHANGED
@@ -14,6 +14,7 @@ module Ooor
|
|
14
14
|
:select_values, :group_values, :order_values, :reorder_flag, :joins_values, :where_values, :having_values,
|
15
15
|
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value, :page_value, :per_value
|
16
16
|
alias :loaded? :loaded
|
17
|
+
alias :model :klass
|
17
18
|
|
18
19
|
def build_where(opts, other = [])#TODO OpenERP domain is more than just the intersection of restrictions
|
19
20
|
case opts
|
@@ -54,14 +55,14 @@ module Ooor
|
|
54
55
|
|
55
56
|
def order(*args)
|
56
57
|
relation = clone
|
57
|
-
relation.order_values += args.flatten unless args.blank?
|
58
|
+
relation.order_values += args.flatten unless args.blank? || args[0] == false
|
58
59
|
relation
|
59
60
|
end
|
60
61
|
|
61
|
-
def count(column_name = nil, options = {})
|
62
|
-
column_name, options = nil, column_name if column_name.is_a?(Hash)
|
63
|
-
calculate(:count, column_name, options)
|
64
|
-
end
|
62
|
+
# def count(column_name = nil, options = {}) #TODO possible to implement?
|
63
|
+
# column_name, options = nil, column_name if column_name.is_a?(Hash)
|
64
|
+
# calculate(:count, column_name, options)
|
65
|
+
# end
|
65
66
|
|
66
67
|
def initialize(klass, options={})
|
67
68
|
@klass = klass
|
@@ -78,6 +79,8 @@ module Ooor
|
|
78
79
|
@klass.new(*args, &block)
|
79
80
|
end
|
80
81
|
|
82
|
+
alias build new
|
83
|
+
|
81
84
|
def reload
|
82
85
|
reset
|
83
86
|
to_a # force reload
|
@@ -115,6 +118,14 @@ module Ooor
|
|
115
118
|
args.any? ? apply_finder_options(args.first).to_a : to_a
|
116
119
|
end
|
117
120
|
|
121
|
+
def first(*args)
|
122
|
+
limit(1).order('id').all(*args).first
|
123
|
+
end
|
124
|
+
|
125
|
+
def last(*args)
|
126
|
+
limit(1).order('id DESC').all(*args).first
|
127
|
+
end
|
128
|
+
|
118
129
|
def to_a
|
119
130
|
if loaded?
|
120
131
|
@records
|
@@ -128,26 +139,10 @@ module Ooor
|
|
128
139
|
if @options && @options[:name_search]
|
129
140
|
name_search = @klass.name_search(@options[:name_search], where_values, 'ilike', @options[:context], @limit_value)
|
130
141
|
@records = name_search.map do |tuple|
|
131
|
-
|
132
|
-
r.id = tuple[0]
|
133
|
-
r #TODO load the fields optionally
|
142
|
+
@klass.new({name: tuple[1]}, []).tap { |r| r.id = tuple[0] } #TODO load the fields optionally
|
134
143
|
end
|
135
144
|
else
|
136
|
-
|
137
|
-
offset = @per_value * @page_value
|
138
|
-
limit = @per_value
|
139
|
-
else
|
140
|
-
offset = @offset_value
|
141
|
-
limit = @limit_value || false
|
142
|
-
end
|
143
|
-
@loaded = true
|
144
|
-
opts = @options.merge({
|
145
|
-
domain: where_values,
|
146
|
-
offset: offset,
|
147
|
-
limit: limit,
|
148
|
-
order: search_order,
|
149
|
-
})
|
150
|
-
@records = @klass.find(:all, opts)
|
145
|
+
load_records_page(search_order)
|
151
146
|
end
|
152
147
|
end
|
153
148
|
end
|
@@ -156,8 +151,38 @@ module Ooor
|
|
156
151
|
false
|
157
152
|
end
|
158
153
|
|
154
|
+
def inspect
|
155
|
+
entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
|
156
|
+
entries[10] = '...' if entries.size == 11
|
157
|
+
|
158
|
+
"#<#{self.class.name} [#{entries.join(', ')}]>"
|
159
|
+
end
|
160
|
+
|
159
161
|
protected
|
160
162
|
|
163
|
+
def load_records_page(search_order)
|
164
|
+
if @per_value && @page_value
|
165
|
+
offset = @per_value * @page_value
|
166
|
+
limit = @per_value
|
167
|
+
else
|
168
|
+
offset = @offset_value
|
169
|
+
limit = @limit_value || false
|
170
|
+
end
|
171
|
+
@loaded = true
|
172
|
+
opts = @options.merge({
|
173
|
+
domain: where_values,
|
174
|
+
offset: offset,
|
175
|
+
limit: limit,
|
176
|
+
order: search_order,
|
177
|
+
})
|
178
|
+
scope = @options.delete(:ids) || :all
|
179
|
+
if scope == []
|
180
|
+
@records = []
|
181
|
+
else
|
182
|
+
@records = @klass.find(scope, opts)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
161
186
|
def method_missing(method, *args, &block)
|
162
187
|
if Array.method_defined?(method)
|
163
188
|
to_a.send(method, *args, &block)
|
data/lib/ooor/type_casting.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# OOOR: OpenObject On Ruby
|
2
|
-
# Copyright (C) 2009-
|
2
|
+
# Copyright (C) 2009-2014 Akretion LTDA (<http://www.akretion.com>).
|
3
3
|
# Author: Raphaël Valyi
|
4
4
|
# Licensed under the MIT license, see MIT-LICENSE file
|
5
5
|
|
@@ -8,6 +8,7 @@ module Ooor
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
OPERATORS = ["=", "!=", "<=", "<", ">", ">=", "=?", "=like", "=ilike", "like", "not like", "ilike", "not ilike", "in", "not in", "child_of"]
|
11
|
+
BLACKLIST = %w[id write_date create_date write_ui create_ui]
|
11
12
|
|
12
13
|
module ClassMethods
|
13
14
|
|
@@ -68,28 +69,8 @@ module Ooor
|
|
68
69
|
elsif request.is_a?(Hash)
|
69
70
|
request2 = {}
|
70
71
|
request.each do |k, v|
|
71
|
-
|
72
|
-
if k.to_s.end_with?("_attributes")
|
73
|
-
attrs = []
|
74
|
-
if v.is_a?(Hash)
|
75
|
-
v.each do |key, val|
|
76
|
-
if !val["_destroy"].empty?
|
77
|
-
attrs << [2, val[:id] || val['id']]
|
78
|
-
elsif val[:id] || val['id']
|
79
|
-
attrs << [1, val[:id] || val['id'], cast_request_to_openerp(val)]
|
80
|
-
else
|
81
|
-
attrs << [0, 0, cast_request_to_openerp(val)]
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
request2[k.to_s.gsub("_attributes", "")] = attrs
|
87
|
-
else
|
88
|
-
request2[k] = cast_request_to_openerp(v)
|
89
|
-
end
|
72
|
+
request2[k] = cast_request_to_openerp(v)
|
90
73
|
end
|
91
|
-
request2
|
92
|
-
|
93
74
|
else
|
94
75
|
value_to_openerp(request)
|
95
76
|
end
|
@@ -120,72 +101,124 @@ module Ooor
|
|
120
101
|
|
121
102
|
end
|
122
103
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
104
|
+
def sanitize_attribute(skey, value)
|
105
|
+
type = self.class.fields[skey]['type']
|
106
|
+
if type == 'boolean' && value == 1 || value == "1"
|
107
|
+
true
|
108
|
+
elsif type == 'boolean'&& value == 0 || value == "0"
|
109
|
+
false
|
110
|
+
elsif value == false and type != 'boolean'
|
111
|
+
nil
|
112
|
+
else
|
113
|
+
value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def sanitize_association(skey, value)
|
118
|
+
if value.is_a?(Ooor::Base) || value.is_a?(Array) && value.all? {|i| i.is_a?(Ooor::Base)}
|
119
|
+
value
|
120
|
+
elsif value.is_a?(Array) && !self.class.many2one_associations.keys.index(skey)
|
121
|
+
value.reject {|i| i == ''}.map {|i| i.is_a?(String) ? i.to_i : i}
|
122
|
+
elsif value.is_a?(String)
|
123
|
+
sanitize_associatio_as_string(skey, value)
|
124
|
+
else
|
125
|
+
value
|
133
126
|
end
|
134
|
-
|
127
|
+
end
|
128
|
+
|
129
|
+
def sanitize_associatio_as_string(skey, value)
|
130
|
+
if self.class.polymorphic_m2o_associations.has_key?(skey)
|
131
|
+
value
|
132
|
+
elsif self.class.many2one_associations.has_key?(skey)
|
133
|
+
sanitize_m2o_association(value)
|
134
|
+
else
|
135
|
+
value.split(",").map {|i| i.to_i}
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def sanitize_m2o_association(value)
|
140
|
+
if value.blank? || value == "0"
|
141
|
+
false
|
142
|
+
else
|
143
|
+
value.to_i
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def to_openerp_hash
|
148
|
+
attributes, associations = get_changed_values
|
149
|
+
associations = cast_associations_to_openerp(associations)
|
150
|
+
attributes.merge(associations)
|
135
151
|
end
|
136
152
|
|
137
|
-
def
|
138
|
-
|
139
|
-
associations
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
if k.end_with?("_ids") && v.is_a?(String)
|
149
|
-
v = v.split(",").map{|i| i.to_i}
|
150
|
-
end
|
151
|
-
associations2[k] = v
|
153
|
+
def get_changed_values
|
154
|
+
attributes = {}
|
155
|
+
associations = {}
|
156
|
+
|
157
|
+
changed.each do |k|
|
158
|
+
if self.class.associations_keys.index(k)
|
159
|
+
associations[k] = @associations[k]#changes[k][1]
|
160
|
+
elsif self.class.fields.has_key?(k)
|
161
|
+
attributes[k]= @attributes[k]
|
162
|
+
elsif !BLACKLIST.index(k)
|
163
|
+
attributes[k] = changes[k][1]
|
152
164
|
end
|
153
165
|
end
|
166
|
+
return attributes, associations
|
167
|
+
end
|
154
168
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
end
|
169
|
+
def cast_associations_to_openerp(associations=@associations)
|
170
|
+
associations.each do |k, v|
|
171
|
+
associations[k] = self.cast_association(k, v)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# talk OpenERP cryptic associations API
|
176
|
+
def cast_association(k, v)
|
177
|
+
if self.class.one2many_associations[k]
|
178
|
+
cast_o2m_assocation(v)
|
179
|
+
elsif self.class.many2many_associations[k]
|
180
|
+
[[6, false, (v || []).map {|i| i.is_a?(Base) ? i.id : i}]]
|
181
|
+
elsif self.class.many2one_associations[k]
|
182
|
+
cast_m2o_association(v)
|
170
183
|
end
|
171
|
-
associations2
|
172
184
|
end
|
173
185
|
|
174
|
-
def
|
175
|
-
if
|
176
|
-
|
177
|
-
|
186
|
+
def cast_o2m_assocation(v)
|
187
|
+
if v.is_a?(Hash)
|
188
|
+
cast_o2m_nested_attributes(v)
|
189
|
+
else
|
190
|
+
v.collect do |value|
|
191
|
+
if value.is_a?(Base)
|
178
192
|
[0, 0, value.to_openerp_hash]
|
193
|
+
elsif value.is_a?(Hash)
|
194
|
+
[0, 0, value]
|
179
195
|
else
|
180
|
-
|
181
|
-
[0, 0, value]
|
182
|
-
else
|
183
|
-
[1, value, {}]
|
184
|
-
end
|
196
|
+
[1, value, {}]
|
185
197
|
end
|
186
198
|
end
|
187
|
-
|
188
|
-
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def cast_o2m_nested_attributes(v)
|
203
|
+
v.keys.collect do |key|
|
204
|
+
val = v[key]
|
205
|
+
if !val["_destroy"].blank?
|
206
|
+
[2, val[:id].to_i || val['id']]
|
207
|
+
elsif val[:id] || val['id']
|
208
|
+
[1, val[:id].to_i || val['id'], val]
|
209
|
+
else
|
210
|
+
[0, 0, val]
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def cast_m2o_association(v)
|
216
|
+
if v.is_a?(Array)
|
217
|
+
v[0]
|
218
|
+
elsif v.is_a?(Base)
|
219
|
+
v.id
|
220
|
+
else
|
221
|
+
v
|
189
222
|
end
|
190
223
|
end
|
191
224
|
|
data/lib/ooor/version.rb
CHANGED
data/spec/ooor_spec.rb
CHANGED
@@ -42,15 +42,7 @@ describe Ooor do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should be able to load a profile" do
|
45
|
-
|
46
|
-
module_ids.each do |accounting_module_id|
|
47
|
-
mod = IrModuleModule.find(accounting_module_id)
|
48
|
-
unless mod.state == "installed"
|
49
|
-
mod.button_install
|
50
|
-
end
|
51
|
-
end
|
52
|
-
wizard = BaseModuleUpgrade.create
|
53
|
-
wizard.upgrade_module
|
45
|
+
IrModuleModule.install_modules(['sale', 'account_voucher'])
|
54
46
|
@ooor.load_models
|
55
47
|
@ooor.models.keys.should_not be_empty
|
56
48
|
end
|
@@ -68,7 +60,7 @@ describe Ooor do
|
|
68
60
|
describe "Do operations on configured database" do
|
69
61
|
before(:all) do
|
70
62
|
@ooor = Ooor.new(:url => @url, :username => @username, :password => @password, :database => @database,
|
71
|
-
:models => ['res.user', 'res.partner', 'product.product', 'sale.order', 'account.invoice', 'product.category', '
|
63
|
+
:models => ['res.user', 'res.partner', 'product.product', 'sale.order', 'account.invoice', 'product.category', 'ir.cron', 'ir.ui.menu', 'ir.module.module'])
|
72
64
|
end
|
73
65
|
|
74
66
|
describe "Finders operations" do
|
@@ -173,8 +165,16 @@ describe Ooor do
|
|
173
165
|
it "should cast dates properly from OpenERP to Ruby" do
|
174
166
|
o = SaleOrder.find(1)
|
175
167
|
o.date_order.should be_kind_of(Date)
|
176
|
-
|
177
|
-
|
168
|
+
c = IrCron.find(1)
|
169
|
+
c.nextcall.should be_kind_of(DateTime)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should not load false values in empty strings (for HTML forms)" do
|
173
|
+
ResPartner.first.phone.should be_nil
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should map OpenERP types to Rails types" do
|
177
|
+
(%w[char binary many2one one2many many2many]).each { |t| Ooor::Base.to_rails_type(t).should be_kind_of(Symbol) }
|
178
178
|
end
|
179
179
|
|
180
180
|
it "should be able to call any Class method" do
|
@@ -218,6 +218,12 @@ describe Ooor do
|
|
218
218
|
p.name.should == "testProduct1"
|
219
219
|
end
|
220
220
|
|
221
|
+
it "should properly change value when m2o is set" do
|
222
|
+
p = ProductProduct.find(:first)
|
223
|
+
p.categ_id = 7
|
224
|
+
p.categ_id.id.should == 7
|
225
|
+
end
|
226
|
+
|
221
227
|
it "should be able to create a product" do
|
222
228
|
p = ProductProduct.create(:name => "testProduct1", :categ_id => 1)
|
223
229
|
ProductProduct.find(p.id).categ_id.id.should == 1
|
@@ -235,12 +241,12 @@ describe Ooor do
|
|
235
241
|
u.save
|
236
242
|
u.id.should_not be_nil
|
237
243
|
u.name.should == "joe"
|
238
|
-
u.destroy
|
244
|
+
u.destroy.should be_kind_of(ResUsers)
|
239
245
|
end
|
240
246
|
|
241
247
|
it "should be able to create an order" do
|
242
|
-
|
243
|
-
|
248
|
+
p_id = ResPartner.search([['name', 'ilike', 'Agrolait']])[0]
|
249
|
+
o = SaleOrder.create(partner_id: p_id, partner_invoice_id: p_id, partner_shipping_id: p_id, pricelist_id: 1)
|
244
250
|
o.id.should be_kind_of(Integer)
|
245
251
|
end
|
246
252
|
|
@@ -268,7 +274,7 @@ describe Ooor do
|
|
268
274
|
|
269
275
|
it "should use default fields on creation" do
|
270
276
|
p = ProductProduct.new
|
271
|
-
p.
|
277
|
+
p.categ_id.should be_kind_of(ProductCategory)
|
272
278
|
end
|
273
279
|
|
274
280
|
it "should skipped inherited default fields properly, for instance at product variant creation" do
|
@@ -335,16 +341,44 @@ describe Ooor do
|
|
335
341
|
p.taxes_id_ids.should be_kind_of(Array)
|
336
342
|
end
|
337
343
|
|
338
|
-
it "should support Rails nested attributes" do
|
344
|
+
it "should support Rails nested attributes methods" do
|
339
345
|
so = SaleOrder.find :first
|
340
346
|
so.respond_to?(:order_line_attributes).should be_true
|
341
347
|
so.respond_to?(:order_line_attributes=).should be_true
|
342
348
|
end
|
343
349
|
|
350
|
+
it "should support CRUD on o2m via nested attributes" do
|
351
|
+
p = ProductProduct.create(name:'Ooor product with packages')
|
352
|
+
p.packaging_attributes = {'1' => {name: 'pack1'}, '2' => {name: 'pack2'}}
|
353
|
+
p.save
|
354
|
+
p = ProductProduct.find p.id
|
355
|
+
pack1 = p.packaging[0]
|
356
|
+
pack2 = p.packaging[1]
|
357
|
+
pack2.name.should == 'pack2'
|
358
|
+
p.packaging_attributes = {'1' => {name: 'pack1', '_destroy'=> true, id: pack1.id}, '2' => {name: 'pack2_modified', id: pack2.id}}
|
359
|
+
p.save
|
360
|
+
p.packaging.size.should == 1
|
361
|
+
p.packaging[0].name.should == 'pack2_modified'
|
362
|
+
end
|
363
|
+
|
344
364
|
it "should be able to call build upon a o2m association" do
|
345
365
|
so = SaleOrder.find :first
|
346
366
|
so.order_line.build().should be_kind_of(SaleOrderLine)
|
347
367
|
end
|
368
|
+
|
369
|
+
it "should recast string m2o string id to an integer (it happens in forms)" do
|
370
|
+
uom_id = @ooor.const_get('product.uom').search()[0]
|
371
|
+
p = ProductProduct.new(name: "z recast id", uom_id: uom_id.to_s)
|
372
|
+
p.save
|
373
|
+
p.uom_id.id.should == uom_id
|
374
|
+
end
|
375
|
+
|
376
|
+
it "should recast string m2m string ids to an array of integer (it happens in forms)" do
|
377
|
+
categ_ids = @ooor.const_get('res.partner.category').search()[0..1]
|
378
|
+
p = ResPartner.new(name: "z recast ids", category_id: categ_ids.join(','))
|
379
|
+
p.save
|
380
|
+
p.category_id.map{|c| c.id}.should == categ_ids
|
381
|
+
end
|
348
382
|
end
|
349
383
|
|
350
384
|
describe "Fields validations" do
|
@@ -354,6 +388,28 @@ describe Ooor do
|
|
354
388
|
p.save.should == false
|
355
389
|
p.errors.messages[:ean13].should_not be_nil
|
356
390
|
end
|
391
|
+
|
392
|
+
it "should list all available fields when you call an invalid field" do
|
393
|
+
expect { ProductProduct.find(1).unexisting_field_or_method }.to raise_error(Ooor::UnknownAttributeOrAssociationError, /AVAILABLE FIELDS/)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
describe "Life cycle Callbacks" do
|
398
|
+
include Ooor
|
399
|
+
|
400
|
+
it "should call customized before_save callback" do
|
401
|
+
expect do
|
402
|
+
Ooor.xtend('ir.ui.menu') do
|
403
|
+
before_save do
|
404
|
+
raise 'before_save_called'
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
with_ooor_session username: 'demo', password: 'demo' do |session|
|
409
|
+
session['ir.ui.menu'].first.save
|
410
|
+
end
|
411
|
+
end.to raise_error(RuntimeError, /before_save_called/)
|
412
|
+
end
|
357
413
|
end
|
358
414
|
|
359
415
|
describe "ARel emulation" do
|
@@ -361,13 +417,37 @@ describe Ooor do
|
|
361
417
|
ResUsers.all.should be_kind_of(Array)
|
362
418
|
end
|
363
419
|
|
420
|
+
it "should have a 'first' method" do
|
421
|
+
ResUsers.first.id.should == 1
|
422
|
+
end
|
423
|
+
|
424
|
+
it "should have a 'last' method" do
|
425
|
+
ResUsers.last.id.should == ResUsers.find(:last).id
|
426
|
+
end
|
427
|
+
|
364
428
|
it "should be ready for Kaminari pagination via ARel scoping" do
|
365
429
|
num = 2
|
366
430
|
default_per_page = 5
|
367
|
-
collection = ProductProduct.limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
|
431
|
+
collection = ProductProduct.where(active: true).limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1)).order("categ_id")
|
368
432
|
collection.all(fields:['name']).should be_kind_of(Array)
|
369
433
|
collection.all.size.should == 5
|
370
434
|
end
|
435
|
+
|
436
|
+
it "should support name_search in ARel (used in association widgets with Ooorest)" do
|
437
|
+
Ooor.default_session.const_get('product.category').all(name_search: 'Com')[0].name.should == "All products / Saleable / Components"
|
438
|
+
end
|
439
|
+
|
440
|
+
it "should be possible to invoke batch methods on relations" do
|
441
|
+
Ooor.default_session.const_get('product.product').where(type: 'service').write(type: 'service').should == true
|
442
|
+
end
|
443
|
+
|
444
|
+
it "should forward Array methods to the Array" do
|
445
|
+
Ooor.default_session.const_get('product.product').where(type: 'service').size.should be_kind_of(Integer)
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should support reloading relation" do
|
449
|
+
Ooor.default_session.const_get('product.product').where(type: 'service').reload.all.should be_kind_of(Array)
|
450
|
+
end
|
371
451
|
end
|
372
452
|
|
373
453
|
describe "report support" do
|
@@ -383,12 +463,12 @@ describe Ooor do
|
|
383
463
|
inv.state.should == "draft"
|
384
464
|
inv.wkf_action('invoice_open')
|
385
465
|
inv.state.should == "open"
|
386
|
-
voucher =
|
387
|
-
voucher.on_change("onchange_partner_id", [], :partner_id, inv.partner_id.id,
|
466
|
+
voucher = @ooor.const_get('account.voucher').new({:amount=>inv.amount_total, :type=>"receipt", :partner_id => inv.partner_id.id}, {"default_amount"=>inv.amount_total, "invoice_id"=>inv.id})
|
467
|
+
voucher.on_change("onchange_partner_id", [], :partner_id, inv.partner_id.id, @ooor.const_get('account.journal').find('account.bank_journal').id, 0.0, 1, 'receipt', false)
|
388
468
|
voucher.save
|
389
|
-
voucher.wkf_action 'proforma_voucher'
|
469
|
+
# voucher.wkf_action 'proforma_voucher'
|
390
470
|
|
391
|
-
inv.reload
|
471
|
+
# inv.reload
|
392
472
|
end
|
393
473
|
|
394
474
|
it "should be possible to call resource actions and workflow actions" do
|
@@ -443,11 +523,19 @@ describe Ooor do
|
|
443
523
|
Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connectio reuse here
|
444
524
|
with_ooor_session(:url => @url, :database => @database, :username => @username, :password => @password, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
|
445
525
|
session['products'].search().should be_kind_of(Array)
|
526
|
+
session['product.product'].alias.should == 'products'
|
446
527
|
end
|
447
528
|
end
|
448
529
|
|
449
|
-
it "should
|
530
|
+
it "should have a to_param method" do
|
450
531
|
Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connectio reuse here
|
532
|
+
with_ooor_session(:url => @url, :database => @database, :username => @username, :password => @password, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
|
533
|
+
session['product.product'].find(:first).to_param.should be_kind_of(String)
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
it "should find by permalink" do
|
538
|
+
Ooor.session_handler.reset!() # alias isn't part of the connection spec, we don't want connection reuse here
|
451
539
|
with_ooor_session(:url => @url, :database => @database, :username => @username, :password => @password, :aliases => {en_US: {products: 'product.product'}}, :param_keys => {'product.product' => 'name'}) do |session|
|
452
540
|
lang = Ooor::Locale.to_erp_locale('en')
|
453
541
|
session['products'].find_by_permalink('Service', context: {'lang' => lang}, fields: ['name']).should be_kind_of(Ooor::Base)
|
@@ -455,24 +543,33 @@ describe Ooor do
|
|
455
543
|
end
|
456
544
|
end
|
457
545
|
|
458
|
-
describe "Ative-Record like
|
546
|
+
describe "Ative-Record like Reflections" do
|
459
547
|
before(:all) do
|
460
|
-
@ooor = Ooor.new(:url => @url, :username => @username, :password => @password, :database => @database, :models => ['product.product'], :reload => true)
|
548
|
+
@ooor = Ooor.new(:url => @url, :username => @username, :password => @password, :database => @database, :models => ['product.product', 'product.category'], :reload => true)
|
461
549
|
end
|
462
550
|
|
463
|
-
it "should test correct class attributes" do
|
551
|
+
it "should test correct class attributes of ActiveRecord Reflection" do
|
464
552
|
object = Ooor::Reflection::AssociationReflection.new(:test, :people, {}, nil)
|
465
553
|
object.name.should == :people
|
466
554
|
object.macro.should == :test
|
467
555
|
object.options.should == {}
|
468
556
|
end
|
469
557
|
|
470
|
-
it "should test correct class name matching
|
558
|
+
it "should test correct class name matching with class name" do
|
471
559
|
object = Ooor::Reflection::AssociationReflection.new(:test, 'product_product', {class_name: 'product.product'}, nil)
|
472
560
|
object.connection = @ooor
|
473
561
|
object.klass.should == ProductProduct
|
474
562
|
end
|
475
563
|
|
564
|
+
it "should reflect on association (used in simple_form, cocoon...)" do
|
565
|
+
reflection = ProductProduct.reflect_on_association(:categ_id)
|
566
|
+
reflection.should be_kind_of(Ooor::Reflection::AssociationReflection)
|
567
|
+
reflection.klass.should == ProductCategory
|
568
|
+
end
|
569
|
+
|
570
|
+
it "should support column_for_attribute (used by simple_form)" do
|
571
|
+
@ooor.const_get('ir.cron').find(:first).column_for_attribute('name')[:type].should == :string
|
572
|
+
end
|
476
573
|
end
|
477
574
|
|
478
575
|
describe "Multi-instance and class name scoping" do
|
@@ -510,6 +607,15 @@ describe Ooor do
|
|
510
607
|
session['res.users'].search().should be_kind_of(Array)
|
511
608
|
end
|
512
609
|
end
|
610
|
+
|
611
|
+
it "should recover from expired sessions" do
|
612
|
+
with_ooor_session(:url => @url, :username => @username, :password => @password, :database => @database) do |session|
|
613
|
+
user_obj = session['res.users']
|
614
|
+
user_obj.search().should be_kind_of(Array)
|
615
|
+
session.web_session[:session_id] = 'invalid'
|
616
|
+
user_obj.search().should be_kind_of(Array)
|
617
|
+
end
|
618
|
+
end
|
513
619
|
end
|
514
620
|
|
515
621
|
|