ooor 2.0.3 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 set_columns_hash(view_fields={})
11
- reload_fields_definition()
12
- @t.columns_hash ||= {}
13
- @t.fields.each do |k, field|
14
- unless @t.associations_keys.index(k)
15
- @t.columns_hash[k] = field.merge({type: to_rails_type(view_fields[k] && view_fields[k]['type'] || field['type'])})
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
@@ -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
- r = @klass.new({name: tuple[1]}, [])
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
- if @per_value && @page_value
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)
@@ -1,5 +1,5 @@
1
1
  # OOOR: OpenObject On Ruby
2
- # Copyright (C) 2009-2012 Akretion LTDA (<http://www.akretion.com>).
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 to_openerp_hash(attributes=@attributes, associations=@associations)
124
- associations = cast_relations_to_openerp(associations)
125
- blacklist = %w[id write_date create_date write_ui create_ui]
126
- r = {}
127
- attributes.reject {|k, v| blacklist.index(k)}.merge(associations).each do |k, v|
128
- if k.end_with?("_id") && !self.class.associations_keys.index(k) && self.class.associations_keys.index(k.gsub(/_id$/, ""))
129
- r[k.gsub(/_id$/, "")] = v && v.to_i || v
130
- else
131
- r[k] = v
132
- end
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
- r
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 cast_relations_to_openerp(associations=@associations)
138
- associations2 = {}
139
- associations.each do |k, v|
140
- if k.match(/_ids$/) && !self.class.associations_keys.index(k) && self.class.associations_keys.index(rel = k.gsub(/_ids$/, ""))
141
- if v.is_a? Array
142
- v.reject! {|i| i == ''}.map! {|i| i.to_i}
143
- end
144
- associations2[rel] = v
145
- elsif v.is_a?(Array) && (v.size == 0 or v[1].is_a?(String)) #reject non assigned many2one or empty list
146
- next
147
- else
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
- associations2.each do |k, v| #see OpenERP awkward associations API
156
- #already casted, possibly before server error!
157
- next if (v.is_a?(Array) && v.size == 1 && v[0].is_a?(Array)) \
158
- || self.class.many2one_associations[k] \
159
- || !v.is_a?(Array)
160
- new_rel = self.cast_relation(k, v, self.class.one2many_associations, self.class.many2many_associations)
161
- if new_rel #matches a known o2m or m2m
162
- associations2[k] = new_rel
163
- else
164
- self.class.many2one_associations.each do |k2, field| #try to cast the association to an inherited o2m or m2m:
165
- linked_class = self.class.const_get(field['relation'])
166
- new_rel = self.cast_relation(k, v, linked_class.one2many_associations, linked_class.many2many_associations)
167
- associations2[k] = new_rel and break if new_rel
168
- end
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 cast_relation(k, v, one2many_associations, many2many_associations)
175
- if one2many_associations[k]
176
- return v.collect do |value|
177
- if value.is_a?(Base) #on the fly creation as in the GTK client
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
- if value.is_a?(Hash)
181
- [0, 0, value]
182
- else
183
- [1, value, {}]
184
- end
196
+ [1, value, {}]
185
197
  end
186
198
  end
187
- elsif many2many_associations[k]
188
- return v = [[6, 0, v]]
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
 
@@ -1,7 +1,7 @@
1
1
  module Ooor
2
2
  MAJOR = 2
3
3
  MINOR = 0
4
- TINY = 3
4
+ TINY = 4
5
5
  PRE = nil
6
6
 
7
7
  VERSION = [MAJOR, MINOR, TINY].compact.join('.')
@@ -42,15 +42,7 @@ describe Ooor do
42
42
  end
43
43
 
44
44
  it "should be able to load a profile" do
45
- module_ids = IrModuleModule.search(['name','=', 'sale']) + IrModuleModule.search(['name','=', 'account_voucher']) + IrModuleModule.search(['name','=', 'sale_stock'])
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', 'stock.move', 'ir.ui.menu', 'ir.module.module'])
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
- m = StockMove.find(1)
177
- m.date.should be_kind_of(DateTime)
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
- o = SaleOrder.create(:partner_id => ResPartner.search([['name', 'ilike', 'Agrolait']])[0],
243
- :partner_order_id => 1, :partner_invoice_id => 1, :partner_shipping_id => 1, :pricelist_id => 1)
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.sale_delay.should be_kind_of(Integer)
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 = AccountVoucher.new({:amount=>inv.amount_total, :type=>"receipt", :partner_id => inv.partner_id.id}, {"default_amount"=>inv.amount_total, "invoice_id"=>inv.id})
387
- voucher.on_change("onchange_partner_id", [], :partner_id, inv.partner_id.id, AccountJournal.find('account.bank_journal').id, 0.0, 1, 'receipt', false)
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 find by permalink" do
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 Reflection" do
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 wit class name" do
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